freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

thinkphp5.0.23 invokefunction RCE漏洞 详细分析与复现
2021-12-06 10:50:47
所属地 辽宁省

本文为thinkphp5.0.23 invokefunction 漏洞 RCE 详细分析与复现。

复现

去官网下载thinkphp5.0.23

漏洞payload ?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
本地搭建环境实测
XDEBUG_SESSION_START是调试用的,不用管
s参数是thinkphp的一种路由形式
http://xxx.com/index.php?s=/module/controller/func/args/value (func也叫action,就是类里的方法名)

举个小例子
例如s=index/index/getname/name/z3

漏洞分析

现在来分析下payload中的s参数
s=index/think\app/invokefunction
module 是 index,对应的是index文件夹
controller 是 think\app,对应的是???
func 是 invokefunction,对应的是 think\app的invokefunction方法

上面例子中s=index/index/getname/name/z3,是application/index/controller/Index.php文件中的getname方法,但是think\app是什么,controller文件夹下找不到

那只能调试一下,看thinkphp是如何解析路由的
从index.php开始看

index.php->/../thinkphp/start.php->/base.php

然后执行了App::run()->send();

先看App::run()方法
看注释是一些框架初始化的操作,继续往下看
是处理路由的操作
调试可以看出,routeCheck将路由解析了
进去看一下
跟进parseUrl
调用了parseUrlPath方法,使用trim,根据/ 进行分割,得到数组

然后就是对路由的解析了
path的第一个元素定义为module
第二第三元素定义为controller和action,然后返回
又回到了App.php的run方法,知道了controller就是think\php,但还不知道怎么调用的think\php
继续看
调用了exec
module方法执行了命令,跟进
创建了request对象
设置request的module
设置moduel路径

下面重点又来了,解析controller :think\app

一番操作后,controller还是think\app,

Loader::controller成功解析了think\php
注意看$instance是think\App类型对象

App.php里果然有invokeFunction方法,所以继续调试,应该就是通过反射,获取到call_user_func_array方法的反射对象,传参调用

再看下payload:

&function=call_user_func_array
&vars[0]=system
&vars[1][]=whoami

这里传入的函数是call_user_func_array
参数是[system,[whoami]]
即$reflect->invokeArgs([system,[whoami]])
注意这里$reflect->invokeArgs方法参数是一个数组,而call_user_func_array第一个参数是函数名,第二个参数也是数组,这就可以理解为什么要这样构造了&vars[0]=system,&vars[1][]=whoami

还有个问题没解决Loader::controller怎么解析的think\php
跟进Loader::controller方法
跟进ReflectionClass
通过ReflectionClass方法创建了think\App对象

试一下正常调用

s=index/index/getname/name/z3

controller 应该是index,被解析为了app\index\controller\Index
那为什么think\app没有被解析为app\index\controller\think\app呢?

再重新调一遍,在Loader::controller中,index被解析为了app\index\controller\Index

继续跟进getModuleAndClass方法
问题就在这里,如果匹配到了\,$class就原封不动,还是think\app。如果没匹配到\,进入parseClass方法
将class的路径补全为app\index\controller\Index

总结

所以现在知道了thinkphp5.0.23 invokeFunction漏洞的原理

thinkphp在解析controller时,如果有\字符,就原封不动的用它来创建对象,所以将controller设置为think\php,就会创建App对象

而App对象里有invokefunction方法,所以action设置为invokefunction

invokefunction参数是调用的函数名和参数,所以可以执行任意方法了

本质就是可以根据项目代码中的class创建对象,并调用对象的方法,并且可控参数。

思考

实战中遇到的几个问题。例如module名不确定,可能是index、admin、home等等
如果module名不对,就会报错
原因是module初始化时,会检查application文件夹下有没有module的文件名,如果不存在,就会抛出异常
解决办法

暴力尝试

在thinkphp5.0之前版本可以使用?m=index&c=index&a=index方式指定module,controller,action,那么?c=index&a=index,不指定m,就会使用默认module

如何修复

看5.0.24
修复方法很直截了当,用正则做了个过滤,不会绕。

# web安全 # 漏洞分析 # 代码审计 # 网络安全技术 # ThinkPHP
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录