漏洞分析之thinkPHP反序列化:这就是黑客的世界吗( 二 )

poc的构造也比较简单,需要注意的点就是不要忽略了namespace,我们构造的poc里的命名空间应该与tp中Windows类的命名空间一致,这样才能被正确的反序列化 。上面的poc运行过后会得到base64编码过后的序列化字符串:

漏洞分析之thinkPHP反序列化:这就是黑客的世界吗

文章插图
 
然后,为了复现这个任意文件删除,我们还需要在tp应用中手动构造一个反序列化的点 。我就写在Index控制器里了
漏洞分析之thinkPHP反序列化:这就是黑客的世界吗

文章插图
 
我在index控制器里添加了一个unser方法,并对我们传过去的变量进行了base64解码以及反序列化操作 。然后我们把刚刚生成的序列化数据通过post发送过去
漏洞分析之thinkPHP反序列化:这就是黑客的世界吗

文章插图
 
这样就能成功删除文件了,但是其实我在复现这个删除文件的点的时候出现了死活删除不了的情况,原因就是因为权限,可能你的Web服务器用户没有权限删除你指定的文件,这一点需要注意 。好了,文件删除只是小菜,我们的最终目的是实现RCE,结合最开始给出的PoC,我们可以看到作者这里的$this->files变量是Pivot类的实例,在removeFiles函数中对pivot类进行了file_exists判断,file_exists()会把传入参数当做字符串处理,但是我们传入的一个对象,所以就会自动调用对象的__toString()魔术方法(知识点呀!同学们),所以,接下来正常思路就是跟进pivot对象的__toString()方法,但是pivot并没有实现__toString()方法,但是poc中他继承了Model类,于是我继续跟到Model类中,发现他也没有实现toString方法,然后我陷入了对人生以及社会的思考,到后来才知道php 5.4以后就已经有trait这个东西了
注:trait这个东西的出现是为了解决php不支持多继承的问题,一般我们将一些类的公有特性提取出来写成一个trait,然后如果某个类想要使用trait中的东西,只需要使用use关键字把这个trait包含进来就行了,其实就和继承差不多,只不过形式不同,我感觉更像是文件包含 。trait的定义也很简单,类似:
trait Conversion
{
xxxxxxxxx
}
【漏洞分析之thinkPHP反序列化:这就是黑客的世界吗】而且,在Model中就引入了好几个trait,这些trait中一个名为Conversion的,他里面就有__toString方法,也就是pivot对象的__toString()继承自这里,所以我们跟进看看:
public function __toString(){    return $this->toJson();}调用了toJSON,跟进
    public function toJson($options = JSON_UNESCAPED_UNICODE)    {        return json_encode($this->toArray(), $options);    }继续跟进toArray()
public function toArray()    {        $item       = [];        $hasVisible = false;        foreach ($this->visible as $key => $val) {            if (is_string($val)) {                if (strpos($val, '.')) {                    list($relation, $name)      = explode('.', $val);                    $this->visible[$relation][] = $name;                } else {                    $this->visible[$val] = true;                    $hasVisible          = true;                }                unset($this->visible[$key]);            }        }        foreach ($this->hidden as $key => $val) {            if (is_string($val)) {                if (strpos($val, '.')) {                    list($relation, $name)     = explode('.', $val);                    $this->hidden[$relation][] = $name;                } else {                    $this->hidden[$val] = true;                }                unset($this->hidden[$key]);            }        }        // 合并关联数据        $data = array_merge($this->data, $this->relation);        foreach ($data as $key => $val) {            if ($val instanceof Model || $val instanceof ModelCollection) {                // 关联模型对象                if (isset($this->visible[$key])) {                    $val->visible($this->visible[$key]);                } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {                    $val->hidden($this->hidden[$key]);                }                // 关联模型对象                if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {                    $item[$key] = $val->toArray();                }            } elseif (isset($this->visible[$key])) {                $item[$key] = $this->getAttr($key);            } elseif (!isset($this->hidden[$key]) && !$hasVisible) {                $item[$key] = $this->getAttr($key);            }        }        // 追加属性(必须定义获取器)        if (!empty($this->append)) {            foreach ($this->append as $key => $name) {                if (is_array($name)) {                    // 追加关联对象属性                    $relation = $this->getRelation($key);                    if (!$relation) {                        $relation = $this->getAttr($key);                        $relation->visible($name);                    }                    $item[$key] = $relation->append($name)->toArray();                } elseif (strpos($name, '.')) {                    list($key, $attr) = explode('.', $name);                    // 追加关联对象属性                    $relation = $this->getRelation($key);                    if (!$relation) {                        $relation = $this->getAttr($key);                        $relation->visible([$attr]);                    }                    $item[$key] = $relation->append([$attr])->toArray();                } else {                    $item[$name] = $this->getAttr($name, $item);                }            }        }        return $item;    }


推荐阅读