Thinkphp 反序列化利用链深入分析


作者:Ethan@知道创宇404实验室

时间:2019年9月21日今年7月份,ThinkPHP 5.1.x爆出来了一个反序列化漏洞。之前没有分析过关于ThinkPHP的反序列化漏洞。今天就探讨一下ThinkPHP的反序列化问题!在刚接触反序列化漏洞的时候,更多遇到的是在魔术方法中,因此自动调用魔术方法而触发漏洞。但如果漏洞触发代码不在魔法函数中,而在一个类的普通方法中。并且魔法函数通过属性(对象)调用了一些函数,恰巧在其他的类中有同名的函数(pop链)。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。首先漏洞的起点为
/thinkphp/library/think/process/pipes/Windows.php
__destruct()

__destruct()里面调用了两个函数,我们跟进
removeFiles()函数。这里使用了
$t开发云主机域名his->files,而且这里的
$files是可控的。所以存在一个任意文件删除的漏洞。POC可以这样构造:这里只需要一个反序列化漏洞的触发点,便可以实现任意文件删除。在
removeFiles()中使用了
file_exists
$filename进行了处理。我们进入
file_exists函数可以知道,
$filename会被作为字符串处理。

__toString当一个对象被反序列化后又被当做字符串使用时会被触发,我们通过传入一个对象来触发
__toString方法。我们全局搜索
__toString方法。
我们跟进
thinkphplibrarythinkmodelconcernConversion.php的Conversion类的第224行,这里调用了一个
toJson()方法。跟进
toJson()方法继续跟进
toArray()方法我们需要在
toArray()函数中寻找一个满足
$可控变量->方法(参数可控)的点,首先,这里调用了一个
getRelation方法。我们跟进
getRelation(),它位于
Attribute类中由于
getRelation()下面的
if语句为
if (!$relation),所以这里不用理会,返回空即可。然后调用了
getAttr方法,我们跟进
getAttr方法继续跟进
getData方法通过查看
getData函数我们可以知道
$relation的值为
$this->data[$name],需要注意的一点是这里类的定义使用的是
Trait而不是
class。自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为
trait。通过在类中使用
use关键字,声明要组合的Trait名称。所以,这里类的继承要使用
use关键字。然后我们需要找到一个子类同时继承了
Attribute类和
Conversion类。我们可以在
thinkphplibrarythinkModel.php中找到这样一个类我们梳理一下目前我们需要控制的变量利用链如下:
我们现在缺少一个进行代码执行的点,在这个类中需要没有
visible方法。并且最好存在
__call方法,因为
__call一般会存在
__call_user_func
__call_user_func_array,php代码执行的终点经常选择这里。我们不止一次在Thinkphp的rce中见到这两个方法。可以在
/thinkphp/library/think/Request.php,找到一个
__call函数。
__call调用不可访问或不存在的方法时被调用。但是这里我们只能控制
$args,所以这里很难反序列化成功,但是
$hook这里是可控的,所以我们可以构造一个hook数组
"visable"=>"method",但是
array_unshift()向数组插入新元素时会将新数组的值将被插入到数组的开头。这种情况下我们是构造不出可用的payload的。在Thinkphp的Request类中还有一个功能
filter功能,事实上Thinkphp多个RCE都与这个功能有关。我们可以尝试覆盖
filter的方法去执行代码。代码位于第1456行。但这里的
$value不可控,所以我们需要找到可以控制
$value的点。但是input函数的参数不可控,所以我们还得继续寻找可控点。我们继续找一个调用
input函数的地方。我们找到了
param函数。这里仍然是不可控的,所以我们继续找调用
param函数的地方。找到了
isAjax函数在
isAjax函数中,我们可以控制
$this->config['var_ajax']
$this->config['var_ajax']可控就意味着
param函数中的
$name可控。
param函数中的
$name可控就意味着
input函数中的
$name可控。
param函数可以获得
$_GET数组并赋值给
$this->param。再回到
input函数中
$name的值来自于
$this->config['var_ajax'],我们跟进
getData函数。这里
$data直接等于
$data[$val]了然后跟进
getFilter函数这里的
$filter来自于
this->filter,我们需要定义
this->filter为函数名。我们再来看一下
input函数,有这么几行代码这是一个回调函数,跟进
filterValue函数。通过分析我们可以发现
filterValue.value的值为第一个通过
GET请求的值,而
filters.key
GET请求的键,并且
filters.filters就等于
input.filters的值。我们尝试构造payload,这里需要
namespace定义命名空间首先自己构造一个利用点,别问我为什么,这个漏洞就是需要后期开发的时候有利用点,才能触发
我们把payload通过
POST传过去,然后通过
GET请求获取需要执行的命令
执行点如下:
利用链如下:

https://blog.riskivy.com/挖掘暗藏thinkphp中的反序列利用链/
https://xz.aliyun.com/t/3674
https://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html
http://www.f4ckweb.top/index.php/archives/73/
https://cl0und.github.io/2017/10/01/POP%E9%93%BE%E5%AD%A6%E4%B9%A0/

相关推荐: git命令行命令(1)

我们知道git是分布式的版本库,也就是本地仓库里面包含了开发的所用内容,每个人都是本地版本库的主人,包括历史记录、文件内容。即使没有和远程代码库交换依旧可以提交内容到本地仓库,然后git push到远程仓库。可以使用git $commit –help查看每个…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 05/06 17:47
下一篇 05/06 17:47