0x01 ThinkPHP简介
ThinkPHP是一个基于MVC和面向对象的轻量级PHP开发框架,遵循Apache2开源协议发布。从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,注重开发体验和易用性,为web应用开发提供了强有力的支持。
0x02 漏洞简介
(1)漏洞名称
ThinkPHP5远程命令执行
(2)漏洞描述
由于ThinkPHP5框架对控制器名没有进行足够的安全检测,导致在没有开启强制路由的情况下,攻击者构造指定的请求,可以直接getshell。
(3)影响范围
- ThinkPHP 5.0系列 <5.0.23
- ThinkPHP 5.1系列< 5.1.31
- 基于ThinkPHP5二次开发的CMS:如AdminLTE后台管理系统、Thinkcmf、ThinkSNS等。
0x03 测试环境
- centos 7.5
- nginx 1.14.0
- php 7.2.10
- thinkphp5.1beta
0x04 环境部署
查看服务开启状态
将thinkPHP5.1源码K拷贝到nginx
Ŀ¼/usr/local/nginx/html
下
赋予权限chmod -R 777 thinkphp5.1/
检测是否部署成功
url:http://192.168.162.128/thinkphp5.1/public/index.php
0x05 漏洞验证
payload:http://192.168.162.128/thinkphp5.1/public/index.php?s=index/\think\Request/input&filter=phpinfo&data=1
0x06 漏洞产生原因
1. 概述
该漏洞出现的原因在于ThinkPHP5框架底层对控制器名过滤不严,从而让攻击者可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞。
2. 代码审计
由结果推原因thinkphp5.1\public\index.php
,17行,请求start.php
,21行,
run()
函数thinkphp5.1\thinkphp\library\think\App.php
,280行,run()
函数,进行URL路由检测thinkphp5.1\thinkphp\library\think\App.php
,371行,path()
函数thinkphp5.1\thinkphp\library\think\Request.php
,path()
函数:获取当前请求URL的pathinfo信息(不含URL后缀)并返回pathinfo()
函数:获取当前请求URL的pathinfo信息(含URL后缀)并返回
从配置文件中获取var_pathinfo
的值
配置文件thinkphp5.1\config\app.php
中var_pathinfo
的值为s
;
注意这里的$_GET[$this->config->get('var_pathinfo')]
,当请求报文包含 $_GET['s']
,就取其值作为pathinfo,并返回pathinfo
给调用函数,来传递路由信息。而$_GET['s']
是用户可控的。
上面将用户可控的pathinfo
返回给调用函数path()
,再跟进函数path()
将pathinfo
传递给$this->path
之后返回给调用函数routeCheck()
再跟进routeCheck()
函数,得到返回值并赋值给$path
,再调用check($path, $depr, $must)
函数,
跟进check($path, $depr, $must)
函数,$path
作为参数之一thinkphp5.1\thinkphp\library\think\Route.php
,744行,check($path, $depr, $must)
函数:检测URL路由,传递进来的参数$url
是可控的
跟踪可控变量$url
:
首先经过str_replace
函数:将url
中的/
替换为|
;
调用check
函数,url
作为参数之一
又调用check
函数,检查路由;
之后创建UrlDispatch
实例,$url
作为参数之一传递给UrlDispatch
的构造函数
跟进UrlDispatch
的构造函数,thinkphp5.1\thinkphp\library\think\route\Dispatch.php
,29行,将值传递给this->action
,之后调用抽象run()函数
\thinkphp5.1beta\thinkphp\library\think\route\dispatch\Module.php
,19行,类Module
继承了抽象类Dispatch
,实现了函数run()
,
将$this->action
的值传递给$result
,
获取控制器名
获取操作名
实例化控制器thinkphp5.1beta\thinkphp\library\think\App.php
,461行,查看实例化过程
可以看到,如果name
中包含\
,就将name
的值赋值给class
,之后返回,以此这里控制器的实例化是用户可控的,
之后便是执行实例化后的函数,然后返回给浏览器。