资源共享吧|易语言论坛|逆向破解教程|辅助开发教程|网络安全教程|www.zygx8.com|我的开发技术随记

 找回密码
 注册成为正式会员
查看: 1172|回复: 0

[网络安全/渗透测试] ThinkPHP 5.0 远程代码执行漏洞分析

[复制链接]

184

主题

347

帖子

14

精华

资源共享吧豪华贵族SVIP

Rank: 9Rank: 9Rank: 9

资源币
18816
积分
3920
贡献
1368
在线时间
1084 小时
注册时间
2015-4-18
最后登录
2020-2-22

终身成就常驻居民幽默大师灌水大师原创先锋精华会员资源共享吧女神宣传大使爱心大使

发表于 2019-5-20 15:00:30 | 显示全部楼层 |阅读模式
ThinkPHP 5.0 远程代码执行漏洞分析


0x01 前言
本文主要以官网下载的5.0.23 完整版(thinkphp_5.0.23_with_extend.zip)为例分析。
0x02 漏洞分析(一)
Thinkphp处理请求的关键类为Request(thinkphp/library/think/Request.php)
该类可以实现对HTTP请求的一些设置,其中成员函数method用来获取当前请求类型,其定义如下:


t01.png

该函数主要在其他成员函数(例如isGet、isPost、isPut等)中被用来做请求类型判断


t02.png


thinkphp支持配置“表单伪装变量”,默认情况下该变量值为_method


t03.png


因此在method()中,可以通过“表单伪装变量”进行变量覆盖实现对该类任意函数的调用,并且$_POST作为函数的参数传入


t04.png
Request类的构造函数定义如下:
t05.png
构造函数中,主要对$option数组进行遍历,当$option的键名为该类属性时,则将该类同名的属性赋值为$options中该键的对应值,因此可以构造请求如下,来实现对Request类属性值的覆盖,例如覆盖filter属性。filter属性保存了用于全局过滤的函数,因此在thinkphp 5.0.10 中可以通过构造如下请求实现代码执行:
t06.png
0x03 漏洞分析(二)
在官网最新下载的5.0.23完整版中,在App类(thinkphp/library/think/App.php)中module方法增加了设置filter参数值的代码,用于初始化filter。因此通过上述请求设置的filter参数值会被重新覆盖为空导致无法利用:

t07.png
在5.0.23的Request类中,存在param成员函数用于获取当前请求的参数。其中也有调用method()函数,并且传入参数值为true。param函数代码实现如下:


t08.png


当传入的$method === true时 会执行如下代码:


t09.png


其中server()实现如下:
t10.png


由此可见,参数$name为REQUEST_METHOD,最终会调用input函数。input函数实现如下。最终会执行到$filter = $this->getFilter($filter,$default); 来解析过滤器。
t11.png
此时$filter 为‘’,$default为null,继续跟进查看getFilter函数的实现:
t12.png


由于$filter = ‘‘,因此可以执行到红线处,并且最终$filter会被赋值进$this->filter ,和$default 即 null。因此经过“解析过滤器”的过程,$filter最终可被添加来自请求中的filter。之后,代码判断了$data是否为数组,最终将每个值作为参数,通过filterValue()函数进行过滤。而$data即为server()函数中传入的$this->server ,因此也可通过在调用构造函数时,实现对$this->server值的覆盖。
filterValue函数实现如下:

t13.png

最终会通过call_user_func来实现代码执行。此处原理同刚刚爆出的THINKPHP 5 通过控制controller值实现反射调用指定类来远程代码执行漏洞原理一致,不再过多分析。
因此只需要找到自动触发调用param()函数的地方即可。
其中在App类中的run()函数中,如果开启了debug模式,会实现日志的记录,其中有调用$request->param()。

t14.png

因此在debug模式下,发送如下请求可实现代码执行:
t15.png

例如touch /tmp/xxxx
t16.png

0x04 漏洞分析(三)
继续分析,可以看到“记录路由和请求信息”之前,还实现了“未设置调度信息则进行URL路由检测。”其中通过routeCheck函数来设置$dispatch。最终再通过exec函数,将$request和$config 作为参数传入后执行。其中$config 是通过initCommon函数调用init函数来加载config配置文件来实现赋值。$request即为Request::instance()。
t17.png

首先会通过加载config文件导入“路由配置”。默认配置如下:
t18.png
然后通过Route类的import方法加载路由。由于在入口文件index.php(public/index.php)中加载了start.php(thinkphp/start.php)
t19.png
start.php又加载了base.php(thinkphp/base.php)
t20.png
base.php中实现了注册自动加载
tx.png
register函数实现如下:
t21.png
该函数完成了对VENDOR下class的加载。其中完成了验证码类路由的加载:
t22.png
因此上面import之后,Route类$rules值将形如下:
t23.png
然后代码会执行到“路由检测”的部分即调用Route::check的地方,路由检测主要用于根据路由的定义返回不同的URL调度。Route类check函数实现如下:
t24.png
可以看到该check函数中调用了Request类的method方法。并将函数执行结果转换为小写后保存在$method变量。由于Request类中method函数在返回method时直接判断了如果存在$this->method直接返回:
t25.png
因此我们在调用构造函数覆盖变量时,可以直接覆盖method,这样上面的$method = strtolower($request->method()); 的$method最终的值就可以被控制了。
回到check函数,在设置完$method 后,会根据$method在self:rules中获取对应$method的路由信息,并将结果赋值给$rules.
t26.png
最终函数的return将依赖$rules ,因此$method不同返回也不同。当$method 为get 时,$rules 将为如下:
t27.png
由于$item = str_replace(‘|’, ‘/‘, $url); 而 $url = str_replace($depr, ‘|’, $url),而这个$url最初为该函数的参数,在App类routeCheck方法中调用
t28.png

而这个$path = $request->path(),Request类的path方法实现如下:
t29.png

当is_null($this->path)时,通过pathinfo()函数来获取
t30.png
其中var_pathino 默认值为s
t31.png
因此综上,我们可以通过URL中s参数来设置App类routeCheck函数中的$path ,即Router类check函数中的$url ,最终即能控制 Router类check函数中的$item 从而控制check函数真正return哪种结果,从而最终影响App类run方法的$dispath值即调度信息。
由于$dispatch 会作为参数在exec中调用
t32.png
其中exec的实现如下
t33.png
exec会根据$dispatch[‘type’]来决定实际执行的代码。
前面分析可知,我们需要触发Request类中param函数的调用来完成对filter的覆盖。此处显而易见的是,当$dispatch[‘type’]为controller 和method时有直接的调用。当然其他的类型,例如当$dispatch[‘type’]为function,调用了invokeFunction,而invokeFunction调用了bindParams函数,bindParams函数里存在对param函数的调用。总之,我们需要控制请求url中s的值完成设置不同的$method,最终让routeCheck返回我们需要的$dispath即可。
例如我们控制url为 /public/index.php?s=captcha,同时post body为_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls -al则check函数最终会进入
t34.png
然后checkRoute函数,由于rule为字符串,
t35.png
因此最终会进入
t36.png
然后会进入 parseRule

t37.png
然后会进入
t38.png

最后返回$result. 因此最终$dispatch值为:
t39.png
因而在传入exec函数后,触发Request类的param方法,最终覆盖Request类的server变量,接着通过Request类的input方法,实现任意代码执行:
t40.png
0x05 本地复现
POST /index.php?s=captcha HTTP/1.1
Host: 192.168.228.140:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 72
Connection: keep-alive
Upgrade-Insecure-Requests: 1

_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
成功执行命令id:
t41.png

回复

使用道具 举报

 点击右侧快捷回复  

本版积分规则

小黑屋|资源共享吧 ( 琼ICP备2021005790号-1 )

GMT+8, 2024-4-25 18:27 , Processed in 0.062268 second(s), 15 queries , MemCached On.

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表