某电商前台代码注入
th1s 漏洞分析 12126浏览 · 2018-01-23 05:06

某是一款支持商家入驻的可开源多用户商城系统, 支持自营、联营、招商等多种运营模式,并拥有零售、批发、团购等多种业务模式,帮助企业低成本快速构建在线商城,在PC、APP、微信等环境下,开启电子商务业务。
最近日站的时候遇到了这个cms,于是花时间审计了一发,收获颇丰,这里先放一个比较严重的问题出来。

审计入口

在ecmall.php中,对全局变量$_GET、$_POST$_COOKIE作了转义处理。一般全局作了转义以后程序员在后面写逻辑的时候就会比较放心大胆地进行各种拼接了。因此我们在审计代码的时候可以着重关注$_SERVER$_FILE$_REQUEST等变量。

Lang::get()用于获取指定键的语言项。举个例子,传入i_want_open_store,返回我要开店

$vkey是个由$key经过一些处理得到的字符串,字符串的内容是$GLOBALS数组变量,需要用eval函数来使它生效。

eval之前会调用strtokey()方法,作用就是传入$key, $owner, 返回字符串$owner['$key的值']。如果这里的$key包含单引号且没被转义呢?比如这里传入$key="'xor(phpinfo())or'生成的$vkey就是字符串$GLOBALS['ECLANG'][''xor(phpinfo())or''],这个字符串会最终进入eval造成代码注入。

也就是说,如果传入Lang::get()的参数包含没被转义的单引号就存在问题,而这个Lang::get()应用是相当之广的。。。

举个栗子。

漏洞复现

进入用户中心-个人资料,随意选择一张图片上传,保存修改。
http://localhost/cms/ecmall/index.php?app=member&act=profile

修改filename字段的值为 1.png'xor(phpinfo())or',成功执行phpinfo()

漏洞分析

根据上述poc定位到member.app.php profile()方法313行,上传资料时如果存在图片文件则调用_upload_portrait()对图片进行上传处理

跟进到_upload_portrait()中。ecmall所有的图片上传都由Uploader这个图片上传辅助类来完成,这里也不例外。首先设置了allowed_type为图片类型,然后调用addFile()方法进行上传处理。

跟进addFile()

跟进_get_uploaded_info()。可以看到由于上传的后缀png'xor(phpinfo())or'没有在allowed_type里面,ECMall开始在这里行错误存储。错误消息'not_allowed_type'和错误的后缀png'xor(phpinfo())or'被存储到了Uploader类的_errors[]数组里面,键名分别为msgobj。注意,这里因为后缀是从$_FILE里获取的,不会受到全局转义的影响。

跳出addFile()以后,由于上传出错,调用show_warning()进行错误处理和错误警告。

可以看到在_trigger_message()方法中多处调用了Lang::get(),且其中一处$err['obj']被作为了参数,本文最初时就提到

如果传入Lang::get()的参数包含没被转义的单引号就存在问题

$err['obj']恰恰是错误的后缀名png'xor(phpinfo())or',最终导致了php代码注入。

修复建议

strtokey()函数对传入的$str进行转义处理。参考代码

function strtokey($str, $owner = '')
{
    $str = addslashes($str);
    if (!$str)
    {
        return '';
    }
    if ($owner)
    {
        return $owner . '[\'' . str_replace('.', '\'][\'', $str) . '\']';
    }
    else
    {
        $parts = explode('.', $str);
        $owner = '$' . $parts[0];
        unset($parts[0]);
        return strtokey(implode('.', $parts), $owner);
    }
}
16 条评论
某人
表情
可输入 255
xingxingye
2018-03-07 11:04 0 回复

@王天 'xor(assert(base64_decode(cGhwaW5mbygp)))or'


xingxingye
2018-03-07 11:02 0 回复

@王天 你把分号去掉,应该就可以了


hades
2018-01-29 07:43 0 回复

@23456789可 看破不说破


23456789可
2018-01-29 07:38 0 回复

这是撒 CMS 如果不方便说的话 私聊 QQ869708923


mapl0
2018-01-27 11:56 0 回复

echo $GLOBALS['ECLANG'][''xor(system(base64_decode('bHMgLWxh')))or''];


还是能带空格


王天
2018-01-25 03:44 0 回复

@th1s

1.png'xor(eval(base64_decode('cGhwaW5mbygpOw==')))or'

这样依然不行,你可以本地试试


th1s
2018-01-25 03:40 0 回复

@王天 eval后面要跟完整的php语句 你要加分号...


王天
2018-01-25 03:37 0 回复

@th1s 用你这句不行

1.png'xor(eval(base64_decode('cGhwaW5mbygp')))or'

cGhwaW5mbygp是phpinfo()的base64加密

执行后没有phpinfo()出现

你可以本地试试,似乎过滤了下划线


th1s
2018-01-25 02:11 0 回复

@王天 那目测你之前环境system()被disable了。写马方式挺多的吧,举个栗子1.png'xor(eval(base64_decode('xxx')))or'


王天
2018-01-24 13:51 0 回复

@th1s 另外搭建了一个测试,可以system(id),但是命令中不能带空格,试了好几种都不能绕过,有方法可以写马吗?


王天
2018-01-24 12:40 0 回复

@th1s 是linux环境的,我试了

1.png'xor(phpinfo())or'

可以正常显示phpinfo

执行

1.png'xor(echo 11111111)or'

并没有输出1111111

执行1.png'xor(system(id))or'

也没有执行id

难道只能执行phpinfo吗?


酷帥王子
2018-01-24 11:51 0 回复

屌炸天的洞,确实是受教了,感谢楼主


th1s
2018-01-24 03:12 0 回复

@王天 我试了下这样应该没问题的吧,你是不是windows下的环境没有id命令~


王天
2018-01-24 02:08 0 回复

如果要执行system(id),要怎么写?

我这样写

1.png'xor(system(id))or'

没用,直接提示文件错误


th1s
2018-01-23 07:52 0 回复

@fjasdgywjk 我的理解是''xor(phpinfo())or'' 可以看作两个字符串在进行运算,运算的时候执行了phpinfo()。