某是一款支持商家入驻的可开源多用户商城系统, 支持自营、联营、招商等多种运营模式,并拥有零售、批发、团购等多种业务模式,帮助企业低成本快速构建在线商城,在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);
    }
}

源链接

Hacking more

...