一个从 Nday 到 0day 的漏洞,对此进行复现分析和学习
两年前一个利用很巧妙的全版本的 RCE 的漏洞,近日笔者又看到了 4.0 版本的 SQL 注入漏洞分析。希望能从复现分析的过程中获得一点漏洞挖掘的思路和启示。
3.6.x RCE
漏洞复现
Vulhub上已经有现成的利用脚本
<?php |
漏洞分析
触发流程
定位触发点 /user.php
:
对 $back_act
附值, 其实这个地方相当于 Referer
字段不包含 user.php
的前提下能达到 $back_act
的变量可控
变量传递
跟进,这里进行了变量注册。将 $back_act
注册成了 $this->_var[$tpl_var]
下面跟进 display
,/includes/cls_template.php
为模版类, display
为页面显示函数
跟进 fetch
,主要为模版处理文件。处理的 filename
为 user_passport.dwt
其中这里会触发编译模版函数
跟进make_compiled
, 会返回处理好的 hmtl 内容。然后附值给 out
之后会返回 display
函数继续处理未处理的流程,strpos
判断这里是至关重要的一点,涉及到了之后需要调用的 insert_mod
。
$this->_echash
是之前 /includes/cls_template.php
定义好的
var $_echash = '45ea207d7a2b68c49582d2d22adf953a'; |
然后进行 foreach
循环执行 insert_mod
这里 if (($key % 2) == 1)
的作用看一下被切割的 k
就知道了
跟进 insert_mod
, 主要完成这几件事儿:
- 用
|
分割变量 - 反序列化
para
fun
变量的拼接- 返回
$fun($para)
根据返回值引发思考, 我们目前得到的返回值是:$fun($para)
$fun($para)
的 fun
来自 insert_
+ 被 explode 的前半部分
para
来自被 explode 的后半部分
那么漏洞利用思路就是执行拼接了 insert_
的可控函数
问题剖析
- 问题一
首先是 $back_act
附值方式,为什么采用 Referer 头传入?
其实还有很多其他附值方法:
emample: |
究其核心原因:
/includes/init.php
addslashes_deep
- 问题二
为什么字符串是序列化字符串?
/includes/cls_template.php#insert_mod
先分割,再反序列化
- 问题三
如何bypass
主要过滤
function smarty_prefilter_preCompile($source) |
SQLi(云复现)
3.x版本引入了 /includes/safety.php
进行过滤。所以 3.x 版本 理论上是不存在 SQL 注入的
但是为了学习思路,还是云复现一下这个 2.x 版本可以利用的漏洞。
之前分析到寻找可控函数进行调用。网上用的都是 insert_ads
这个函数
关于 SQL 注入的利用思考可以参考这篇文章:https://xz.aliyun.com/t/2725
注释掉了 waf 相关的函数(绕不过去),payload 如下
45ea207d7a2b68c49582d2d22adf953aads|a:2:{s:3:"num";s:3:"669";s:2:"id";s:57:"1' and updatexml(1,make_set(3,'~',(select version())),1)#";} |
执行的相应 SQL 语句:
SELECT a.ad_id, a.position_id, a.media_type, a.ad_link, a.ad_code, a.ad_name, p.ad_width, p.ad_height, p.position_style, RAND() AS rnd FROM `ecshop360`.`ecs_ad` AS a LEFT JOIN `ecshop360`.`ecs_ad_position` AS p ON a.position_id = p.position_id WHERE enabled = 1 AND start_time <= '1606229787' AND end_time >= '1606229787' AND a.position_id = '1' and updatexml(1,make_set(3,'~',(select version())),1)#' ORDER BY rnd LIMIT 669 |
RCE
回顾 /includes/lib_insert.php#insert_ads
function insert_ads($arr) |
在这一步关键的附值,$row['position_style']
来自$res = $GLOBALS['db']->GetAll($sql)
查询后 foreach
遍历的结果。有几个关注的点:
- 首先关注
$position_style
这个变量 - 关注
$row['position_id'] != $arr['id']
(相等调用$position_style = $row['position_style'];
)
在下面完成拼接
会调用 fetch
/includes/cls_template.php
145 行是一个关键点。无非两个函数:
fetch_str
_eval
肯定首先得 fetch_str
处理截断后的 filename
,也就是
{$asd'];phpinfo\t();//}xxx |
后面经过一顿操作,各种替换之后,原来的值变成了 $asd'];phpinfo\t();//
进入 select
函数,关键点如下。返回值是直接 php echo 出来的
后面的过程在第一次分析的时候是懵逼的,其实是缺乏了对前面 SQL 语句的大局观,而且流程很杂,各种 replace
的替换和各种 if
。
@badcode 从宏观上总结的 SQL 语句和最后 position_style
的关系已经很简洁了。我这里不再复述
接下来就是把构造好的代码通过SQL注入漏洞传给
$position_style
。 这里可以用union select 来控制查询的结果,根据之前的流程,$row['position_id']
和$arr['id']
要相等,$row['position_id']
是第二列的结果,$position_style
是第九列的结果。$arr['id']
传入' /*
,$arr['num']
传入*/ union select 1,0x27202f2a,3,4,5,6,7,8,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d,10-- -
,0x27202f2a
是' /*
的16进制值,也就是$row['position_id']
的值,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d
是上面构造的php代码的16进制值,也就是$position_style
。
get_var
中会调用 make_var
,最后返回值
论 $position_style
的演变
{$asd'];phpinfo\t();//}xxx |
最后输出 phpinfo()
4.0 SQLi
没找到安装包233333…先咕咕咕了~~~
https://mp.weixin.qq.com/s/xHioArEpoAqGlHJPfq3Jiw
http://foreversong.cn/archives/1556