某是一款支持商家入驻的可开源多用户商城系统, 支持自营、联营、招商等多种运营模式,并拥有零售、批发、团购等多种业务模式,帮助企业低成本快速构建在线商城,在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[]
数组里面,键名分别为msg
和obj
。注意,这里因为后缀是从$_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);
}
}