def test_directory(self, path): if self.verbose == 2: print(u"+ Testing directory {0}".format(path)) test_page = HTTP.HTTPResource(path + "does_n0t_exist.htm") try: resp = self.HTTP.send(test_page) if resp.getCode() not in ["403", "404"]: # we don't want to deal with this at the moment return for i, candidate in enumerate(self.payloads): self.pbar.update(i + 1) url = path + candidate if url not in self.known_dirs and url not in self.known_pages and url not in self.new_resources: page = HTTP.HTTPResource(path + candidate) try: resp = self.HTTP.send(page) if resp.getCode() == "301": loc = resp.getLocation() if loc in self.known_dirs or loc in self.known_pages: continue self.logR("Found webpage {0}", loc) self.new_resources.append(loc) elif resp.getCode() not in ["403", "404"]: self.logR("Found webpage {0}", page.path) self.new_resources.append(page.path) except requests.exceptions.Timeout: continue except requests.exceptions.Timeout: pass
def addStartURL(self, url): if self.__checklink(url): print(_("Invalid link argument: {0}").format(url)) sys.exit(0) if self.__inzone(url) == 0: self.tobrowse.append(HTTP.HTTPResource(url)) else: self.out_of_scope_urls.append(HTTP.HTTPResource(url))
def attack(self, urls, forms): junk_string = "w" + "".join([random.choice("0123456789abcdefghjijklmnopqrstuvwxyz") for __ in range(0, 5000)]) for l in self.nikto_db: match = match_or = match_and = False fail = fail_or = False osv_id = l[1] path = l[3] method = l[4] vuln_desc = l[10] post_data = l[11] path = path.replace("@CGIDIRS", "/cgi-bin/") path = path.replace("@ADMIN", "/admin/") path = path.replace("@NUKE", "/modules/") path = path.replace("@PHPMYADMIN", "/phpMyAdmin/") path = path.replace("@POSTNUKE", "/postnuke/") path = re.sub("JUNK\((\d+)\)", lambda x: junk_string[:int(x.group(1))], path) if path[0] == "@": continue if path[0] != "/": path = "/" + path url = "" try: url = "http://" + self.HTTP.server + path except UnicodeDecodeError: continue evil_req = None if method == "GET": evil_req = HTTP.HTTPResource(url) elif method == "POST": evil_req = HTTP.HTTPResource(url, post_params=post_data, method=method) else: evil_req = HTTP.HTTPResource(url, post_params=post_data, method=method) if self.verbose == 2: try: if method == "GET": print(u"+ {0}".format(evil_req.url)) else: print(u"+ {0}".format(evil_req.http_repr)) except Exception, e: continue try: resp = self.HTTP.send(evil_req) except Exception, e: # requests bug print(e) continue
def attackGET(self, http_res): url = http_res.path resp_headers = http_res.headers referer = http_res.referer headers = {} if referer: headers["referer"] = referer if url not in self.attackedGET: if self.verbose == 2: print(u"+ {0}".format(url)) err1 = self.__returnErrorByCode(resp_headers["status_code"]) if err1 != "ok": test_req = HTTP.HTTPResource(url) data1 = self.HTTP.send(test_req, headers=headers).getPage() # .htaccess protection detected if self.verbose >= 1: self.log(_("HtAccess protection found: {0}"), url) evil_req = HTTP.HTTPResource(url, method="ABC") data2, code2 = self.HTTP.send(evil_req, headers=headers).getPageCode() err2 = self.__returnErrorByCode(code2) if err2 == "ok": # .htaccess bypass success if self.verbose >= 1: self.logC(_("|HTTP Code: {0} : {1}"), resp_headers["status_code"], err1) if self.verbose == 2: self.logY(_("Source code:")) self.logW(data1) self.logVuln(category=Vulnerability.HTACCESS, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("{0} HtAccess").format(err1)) self.logR(_(" .htaccess bypass vulnerability: {0}"), evil_req.url) # print output informations by verbosity option if self.verbose >= 1: self.logC(_("|HTTP Code: {0}"), code2) if self.verbose == 2: self.logY(_("Source code:")) self.logW(data2) self.attackedGET.append(url)
def __init__(self, http, xmlRepGenerator): Attack.__init__(self, http, xmlRepGenerator) user_config_dir = os.getenv('HOME') or os.getenv('USERPROFILE') user_config_dir += "/config" if not os.path.isdir(user_config_dir): os.makedirs(user_config_dir) try: fd = open(os.path.join(user_config_dir, self.CONFIG_FILE)) reader = csv.reader(fd) self.nikto_db = [l for l in reader if l != [] and l[0].isdigit()] fd.close() except IOError: try: print(_("Problem with local nikto database.")) print(_("Downloading from the web...")) nikto_req = HTTP.HTTPResource("http://cirt.net/nikto/UPDATES/2.1.5/db_tests") resp = self.HTTP.send(nikto_req) page = resp.getRawPage() csv.register_dialect("nikto", quoting=csv.QUOTE_ALL, doublequote=False, escapechar="\\") reader = csv.reader(page.split("\n"), "nikto") self.nikto_db = [l for l in reader if l != [] and l[0].isdigit()] fd = open(os.path.join(user_config_dir, self.CONFIG_FILE), "w") writer = csv.writer(fd) writer.writerows(self.nikto_db) fd.close() except socket.timeout: print(_("Error downloading Nikto database"))
def attack(self, get_resources, forms): """This method searches XSS which could be permanently stored in the web application""" for http_resource in get_resources: if http_resource.method != "GET": continue url = http_resource.url target_req = HTTP.HTTPResource(url) referer = http_resource.referer headers = {} if referer: headers["referer"] = referer if self.verbose >= 1: print(u"+ {0}".format(url)) try: resp = self.HTTP.send(target_req, headers=headers) data = resp.getPage() except requests.exceptions.Timeout: data = "" except socket.error, se: data = "" print( _('error: {0} while attacking {1}').format( repr(str(se[1])), url)) except Exception, e: print( _('error: {0} while attacking {1}').format( repr(str(e[0])), url)) continue
def browse(self, web_resource): """Extract urls from a webpage and add them to the list of urls to browse if they aren't in the exclusion list. Returns True if the URL is kept for attacks.""" # We are going too much deep, don't browse this link if web_resource.link_depth > self.max_link_depth: return False url = web_resource.url # We don't need destination anchors current_full_url = url.split("#")[0] # Url without query string current = current_full_url.split("?")[0] # Get the dirname of the file currentdir = web_resource.dir_name parrentdir = web_resource.parent_dir # Timeout must not be too long to block big documents # (for example a download script) # and not too short to give good results socket.setdefaulttimeout(self.timeout) if parrentdir not in self.custom_404: invalid_res = HTTP.HTTPResource(parrentdir + self.invalid_page) try: invalid_resp = self.h.send(invalid_res) invalid_code = invalid_resp.getCode() self.custom_404[parrentdir] = invalid_code except socket.error: pass try: resp = self.h.send(web_resource) except socket.timeout: self.excluded.append(url) return False except requests.exceptions.Timeout: self.excluded.append(url) return False except socket.error, msg: if msg.errno == 111: print(_("Connection refused!")) self.excluded.append(url) return False
def __end_element(self, name): if name == self.RESOURCE: http_res = HTTP.HTTPResource(self.path, method=self.method, encoding=self.encoding, referer=self.referer, get_params=self.get_params, post_params=self.post_params, file_params=self.file_params) http_res.setHeaders(self.headers) if self.array is self.to_browse: self.to_browse.append(http_res) else: if self.method == "GET": self.browsed_links.append(http_res) elif self.method == "POST": self.browsed_forms.append(http_res)
def __init__(self, root, http_engine=None): self.h = http_engine if root.startswith("-"): print(_("First argument must be the root url !")) sys.exit(0) if not "://" in root: root = "http://" + root if self.__checklink(root): print(_("Invalid protocol: {0}").format(root.split("://")[0])) sys.exit(0) if root[-1] != "/" and not "/" in root.split("://")[1]: root += "/" self.out_of_scope_urls = [] self.browsed_links = [] self.proxies = {} self.excluded = [] self.browsed_forms = [] self.uploads = [] self.verbose = 0 self.auth_basic = [] self.bad_params = [] self.timeout = 6.0 self.global_headers = {} self.cookiejar = None self.scope = "folder" self.link_encoding = {} self.custom_404 = {} self.invalid_page = "zqxj{0}.html".format("".join( [choice(ascii_letters) for __ in xrange(0, 10)])) # 0 means no limits self.nice = 0 self.max_link_depth = 40 server = (root.split("://")[1]).split("/")[0] self.root = HTTP.HTTPResource(root) # Initial URL self.server = server # Domain (with potential :port) self.scope_url = root # Scope of the analysis self.tobrowse = [self.root] self.persister = CrawlerPersister()
def __init__(self, root, http_engine=None, crawlerFile=None): self.h = http_engine if root.startswith("-"): print(_("First argument must be the root url !")) sys.exit(0) if not "://" in root: root = "http://" + root if (self.__checklink(root)): print(_("Invalid protocol: {0}").format(root.split("://")[0])) sys.exit(0) if root[-1] != "/" and not "/" in root.split("://")[1]: root += "/" server = (root.split("://")[1]).split("/")[0] self.root = HTTP.HTTPResource(root) # Initial URL self.server = server # Domain self.scopeURL = root # Scope of the analysis self.tobrowse.append(self.root) self.persister = CrawlerPersister()
def attackGET(self, http_res): if http_res.file_name == "": return page = http_res.path headers = http_res.headers # Do not attack application-type files if not "content-type" in headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in headers["content-type"]: return for payload in self.payloads: payload = payload.replace("[FILE_NAME]", http_res.file_name) url = page.replace(http_res.file_name, payload) if self.verbose == 2: print(u"+ {0}".format(url)) if url not in self.attackedGET: self.attackedGET.append(url) try: evil_req = HTTP.HTTPResource(url) resp = self.HTTP.send(evil_req) data, code = resp.getPageCode() err = self.__returnErrorByCode(code) if err == "ok": self.logR(_("Found backup file !")) self.logR(u" -> {0}".format(evil_req.url)) self.logVuln(category=Vulnerability.BACKUP, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("Backup file {0} found for {1}").format(url, page)) except socket.timeout: break
def attackGET(self, http_res): url = http_res.path if self.verbose == 2: print(u"+ {0}".format(url)) if url not in self.attackedGET: self.attackedGET.append(url) try: evil_req = HTTP.HTTPResource(url) resp = self.HTTP.send(evil_req, headers=self.hdrs) data, code = resp.getPageCode() if "".join(self.random_bytes) in data: self.logR(_("URL {0} seems vulnerable to Shellshock attack !").format(url)) self.logVuln(category=Vulnerability.EXEC, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("URL {0} seems vulnerable to Shellshock attack").format(url)) except socket.timeout: return
def attackGET(self, http_res): """This method performs the cross site scripting attack (XSS attack) with method GET""" # copies page = http_res.path params_list = http_res.get_params resp_headers = http_res.headers referer = http_res.referer headers = {} http_code = "" if referer: headers["referer"] = referer param_name = "PHP_SELF" # Some PHP scripts doesn't sanitize data coming from $_SERVER['PHP_SELF'] if page not in self.PHP_SELF: evil_req = None if page.endswith("/"): evil_req = HTTP.HTTPResource(page + self.php_self_payload) elif page.endswith(".php"): evil_req = HTTP.HTTPResource(page + "/" + self.php_self_payload) if evil_req is not None: if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: data, http_code = self.HTTP.send(evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: data = "" self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) if self._validXSSContentType(evil_req) and self.php_self_check in data: self.logR(Vulnerability.MSG_PATH_INJECT, self.MSG_VULN, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) self.logVuln(category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("XSS vulnerability found via injection in the resource path")) elif http_code == "500": self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.PHP_SELF.append(page) timeouted = False returned500 = False # page is the url of the script # params_list is a list of [key, value] lists if not params_list: # Do not attack application-type files if not "content-type" in resp_headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in resp_headers["content-type"]: return url = page + "?__XSS__" if url not in self.attackedGET: self.attackedGET.append(url) code = self.random_string() test_req = HTTP.HTTPResource(page + "?" + code) self.GET_XSS[code] = (test_req, "QUERY_STRING") try: data, http_code = self.HTTP.send(test_req, headers=headers).getPageCode() except requests.exceptions.Timeout: data = "" if code in data: # Simple text injection worked, let's try with JS code payloads = self.generate_payloads(data, code) for payload in payloads: evil_req = HTTP.HTTPResource(page + "?" + self.HTTP.quote(payload)) if self.verbose == 2: print(u"+ {0}".format(evil_req)) try: dat, http_code = self.HTTP.send(evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: dat = "" if timeouted: continue self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) timeouted = True param_name = "QUERY_STRING" if self._validXSSContentType(evil_req) and dat is not None and len(dat) > 1: if payload.lower() in dat.lower(): self.SUCCESSFUL_XSS[code] = payload self.logVuln(category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("XSS vulnerability found via injection in the query string")) self.logR(Vulnerability.MSG_QS_INJECT, self.MSG_VULN, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) # No more payload injection break elif http_code == "500" and not returned500: self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') returned500 = True # URL contains parameters else: for i in xrange(len(params_list)): saved_value = params_list[i][1] if saved_value is None: saved_value = "" param_name = self.HTTP.quote(params_list[i][0]) params_list[i][1] = "__XSS__" url = page + "?" + self.HTTP.encode(params_list) if url not in self.attackedGET: self.attackedGET.append(url) code = self.random_string() params_list[i][1] = code test_req = HTTP.HTTPResource(page + "?" + self.HTTP.encode(params_list)) self.GET_XSS[code] = (test_req, param_name) try: data, http_code = self.HTTP.send(test_req, headers=headers).getPageCode() except requests.exceptions.Timeout: data = "" # is the random code on the webpage ? if code in data: # YES! But where exactly ? payloads = self.generate_payloads(data, code) for payload in payloads: params_list[i][1] = payload evil_req = HTTP.HTTPResource(page + "?" + self.HTTP.encode(params_list)) if self.verbose == 2: print(u"+ {0}".format(evil_req)) try: dat, http_code = self.HTTP.send(evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: dat = "" if timeouted: continue self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) timeouted = True if self._validXSSContentType(evil_req) and dat is not None and len(dat) > 1: if payload.lower() in dat.lower(): self.SUCCESSFUL_XSS[code] = payload self.logVuln(category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("XSS vulnerability found via injection" " in the parameter {0}").format(param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) # stop trying payloads and jum to the next parameter break elif http_code == "500" and not returned500: self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') returned500 = True # Restore the value of this argument before testing the next one params_list[i][1] = saved_value
def attackPOST(self, form): """This method performs the SQL Injection attack with method POST""" payload = "\xbf'\"(" filename_payload = "'\"(" err = "" # copies get_params = form.get_params post_params = form.post_params file_params = form.file_params referer = form.referer for params_list in [get_params, post_params, file_params]: for i in xrange(len(params_list)): saved_value = params_list[i][1] if saved_value is None: saved_value = "" if params_list is file_params: params_list[i][1] = ["_SQL__", params_list[i][1][1]] else: params_list[i][1] = "__SQL__" param_name = self.HTTP.quote(params_list[i][0]) attack_pattern = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params) if attack_pattern not in self.attackedPOST: self.attackedPOST.append(attack_pattern) if params_list is file_params: params_list[i][1][0] = filename_payload else: params_list[i][1] = payload evil_req = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) if self.verbose == 2: print(u"+ {0}".format(evil_req)) try: resp = self.HTTP.send(evil_req) data, code = resp.getPageCode() except requests.exceptions.Timeout, timeout: # No timeout report here... launch blind sql detection later code = "408" else: err = self.__findPatternInResponse(data) if err != "": self.logVuln(category=Vulnerability.SQL_INJECTION, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in the parameter {1}").format(err, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, err, evil_req.url, param_name) self.logR(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.vulnerablePOST.append(attack_pattern) else: if code == "500": self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') params_list[i][1] = saved_value
def attackGET(self, http_res): """This method performs the SQL Injection attack with method GET""" page = http_res.path params_list = http_res.get_params resp_headers = http_res.headers referer = http_res.referer headers = {} if referer: headers["referer"] = referer # about this payload : http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string payload = "\xBF'\"(" if not params_list: # Do not attack application-type files if not "content-type" in resp_headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in resp_headers["content-type"]: return payload = self.HTTP.quote(payload) url = page + "?" + payload if url not in self.attackedGET: self.attackedGET.append(url) evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(url)) try: resp = self.HTTP.send(evil_req, headers=headers) data, code = resp.getPageCode() except requests.exceptions.Timeout: # No timeout report here... launch blind sql detection later code = "408" err = "" else: err = self.__findPatternInResponse(data) if err != "": self.logVuln(category=Vulnerability.SQL_INJECTION, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("{0} via injection in the query string").format(err)) self.logR(Vulnerability.MSG_QS_INJECT, err, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) self.vulnerableGET.append(page + "?" + "__SQL__") else: if code == "500": self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, info=Anomaly.MSG_QS_500) self.logO(Anomaly.MSG_500, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) else: for i in range(len(params_list)): param_name = self.HTTP.quote(params_list[i][0]) saved_value = params_list[i][1] if saved_value is None: saved_value = "" params_list[i][1] = "__SQL__" pattern_url = page + "?" + self.HTTP.encode(params_list) if pattern_url not in self.attackedGET: self.attackedGET.append(pattern_url) params_list[i][1] = self.HTTP.quote(payload) url = page + "?" + self.HTTP.encode(params_list) evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: resp = self.HTTP.send(evil_req, headers=headers) data, code = resp.getPageCode() except requests.exceptions.Timeout: # No timeout report here... launch blind sql detection later code = "408" err = "" else: err = self.__findPatternInResponse(data) if err != "": self.logVuln(category=Vulnerability.SQL_INJECTION, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in the parameter {1}").format(err, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, err, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) self.vulnerableGET.append(pattern_url) elif code == "500": self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) params_list[i][1] = saved_value
self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) else: for i in range(len(params_list)): err = "" param_name = self.HTTP.quote(params_list[i][0]) saved_value = params_list[i][1] if saved_value is None: saved_value = "" params_list[i][1] = "__SQL__" pattern_url = page + "?" + self.HTTP.encode(params_list) if pattern_url not in self.attackedGET: self.attackedGET.append(pattern_url) params_list[i][1] = self.HTTP.quote(payload) url = page + "?" + self.HTTP.encode(params_list) evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: resp = self.HTTP.send(evil_req, headers=headers) data, code = resp.getPageCode() except requests.exceptions.Timeout, timeout: # No timeout report here... launch blind sql detection later data = "" code = "408" err = "" resp = timeout else: err = self.__findPatternInResponse(data) if err != "":
def attackGET(self, http_res): dir_name = http_res.dir_name headers = http_res.headers for payload in self.payloads: if self.verbose == 2: print(u"+ {0} detecting...".format(payload['title'])) for rule in payload['rules']: for case in switch(rule['type']): if self.verbose == 2: print(u" type: {0}".format(rule['type'])) if case(1): url = dir_name if self.verbose == 2: print(u" name: {0}".format(rule['name'])) print(u" url: {0}".format(url)) print(u" matching pattern: {0}".format( rule['match'])) print("") try: #data = requests.get(url, allow_redirects=True) evil_req = HTTP.HTTPResource(url) resp = self.HTTP.send(evil_req) data, code = resp.getPageCode() err = self.__returnErrorByCode(code) if err == "ok": d = pq(data) for element in d(''.join([ rule['name'], "[", rule['attributes'], "]" ])).items(): if re.search( rule['match'].lower(), element.attr(rule['attributes']). lower()) is not None: self.logR(_("Found fingerprint !")) self.logR(u" -> {0}".format( evil_req.url)) self.logR("Framework: {0} !".format( payload['title']) ) # wait to internalization self.logVuln( category=Vulnerability.FINGERPRINT, level=Vulnerability.LOW_LEVEL, request=evil_req, info=_("Framework {0} used in {1}" ).format( payload['title'], dir_name)) self.fingerprint_flag = True #err = self.__findPatternInResponse(d.text()) return elif err == "Moved": self.logR( "This site might be moved to \"{0}\". Try again." .format( requests.get( url, allow_redirects=True).url)) self.fingerprint_flag = True return except socket.timeout: break break if case(3): if rule['name'] == "URL": url = dir_name + rule['attributes'] if self.verbose == 2: print(u" name: {0}".format(rule['name'])) print(u" url: {0}".format(url)) print(u" matching pattern: {0}".format( rule['match'])) print("") try: evil_req = HTTP.HTTPResource(url) resp = self.HTTP.send(evil_req) data, code = resp.getPageCode() err = self.__returnErrorByCode(code) if err == "ok": d = pq(data) if rule['match'] in d.text(): self.logR(_("Found fingerprint !")) self.logR(u" -> {0}".format( evil_req.url)) self.logR("Framework: {0} !".format( payload['title']) ) # wait to internalization self.logVuln( category=Vulnerability.FINGERPRINT, level=Vulnerability.LOW_LEVEL, request=evil_req, info=_("Framework {0} used in {1}" ).format( payload['title'], dir_name)) self.fingerprint_flag = True #err = self.__findPatternInResponse(d.text()) return except socket.timeout: break else: url = dir_name if self.verbose == 2: print(u" name: {0}".format(rule['name'])) print(u" url: {0}".format(url)) print( u" matching pattern: {0} in html tag '{1}'" .format(rule['match'], rule['name'])) print("") try: #data = requests.get(url, allow_redirects=True) evil_req = HTTP.HTTPResource(url) resp = self.HTTP.send(evil_req) data, code = resp.getPageCode() err = self.__returnErrorByCode(code) if err == "ok": d = pq(data) if re.search(rule['match'], d(rule['name']).text() ) is not None: self.logR(_("Found fingerprint !")) self.logR(u" -> {0}".format( evil_req.url)) self.logR("Framework: {0} !".format( payload['title']) ) # wait to internalization self.logVuln( category=Vulnerability.FINGERPRINT, level=Vulnerability.LOW_LEVEL, request=evil_req, info=_("Framework {0} used in {1}" ).format( payload['title'], dir_name)) self.fingerprint_flag = True #err = self.__findPatternInResponse(d.text()) return elif err == "Moved": self.logR( "This site might be moved to \"{0}\". Try again." .format( requests.get( url, allow_redirects=True).url)) self.fingerprint_flag = True return except socket.timeout: break break if case(8): break
try: flash_parser = swf_parser.swf_parser(data) swf_links = flash_parser.getLinks() except Exception, err_data: swf_links = err_data[1] elif "/x-javascript" in mime_type or "/x-js" in mime_type or "/javascript" in mime_type: js_links = lamejs.lamejs(data).getLinks() data = "" # Manage redirections if "location" in info: redir = self.correctlink(info["location"], current, current_full_url, currentdir, proto, None) if redir is not None: if self.__inzone(redir) == 0: self.link_encoding[redir] = self.link_encoding[url] redir = HTTP.HTTPResource(redir, link_depth=current_depth+1) # Is the document not visited yet and not forbidden ? if (redir not in self.browsed_links and redir not in self.tobrowse and not self.isExcluded(redir)): self.tobrowse.append(redir) if not web_resource.is_root and self.custom_404.get(parrentdir, "404") == code: self.excluded.append(url) return False html_source = data bs = BeautifulSoup(html_source) # Look for a base tag with an href attribute if bs.head: for base in bs.head.findAll("base"):
def attackPOST(self, form): """This method performs the command execution with method POST""" # copies get_params = form.get_params post_params = form.post_params file_params = form.file_params referer = form.referer for params_list in [get_params, post_params, file_params]: for i in xrange(len(params_list)): saved_value = params_list[i][1] if saved_value is None: saved_value = "" timeouted = False warned = 0 cmd = 0 err500 = 0 param_name = self.HTTP.quote(params_list[i][0]) if params_list is file_params: params_list[i][1] = ["_EXEC__", params_list[i][1][1]] else: params_list[i][1] = "__EXEC__" attack_pattern = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params) if attack_pattern not in self.attackedPOST: self.attackedPOST.append(attack_pattern) for payload in self.payloads: # no quoting: send() will do it for us if params_list is file_params: payload = payload.replace("[VALUE]", saved_value[0]) params_list[i][1][0] = payload else: payload = payload.replace("[VALUE]", saved_value) params_list[i][1] = payload evil_req = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) if self.verbose == 2: print(u"+ {0}".format(evil_req)) err = "" try: data, code = self.HTTP.send(evil_req).getPageCode() except requests.exceptions.Timeout: if timeouted: continue data = "" code = "408" self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) timeouted = True else: err, cmd, warned = self.__findPatternInResponse(data, warned) if err != "": self.logVuln(category=Vulnerability.EXEC, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in the parameter {1}").format(err, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, err, evil_req.url, param_name) self.logR(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') if cmd: # Successful command execution, go to the next field break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') params_list[i][1] = saved_value
def attackGET(self, http_res): """This method performs the command execution with method GET""" page = http_res.path params_list = http_res.get_params resp_headers = http_res.headers referer = http_res.referer headers = {} if referer: headers["referer"] = referer if not params_list: # Do not attack application-type files if not "content-type" in resp_headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in resp_headers["content-type"]: return timeouted = False warned = 0 cmd = 0 err500 = 0 for payload in self.payloads: if "[VALUE]" in payload: continue err = "" url = page + "?" + self.HTTP.quote(payload) if url not in self.attackedGET: evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(url)) self.attackedGET.append(url) try: data, code = self.HTTP.send(evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: if timeouted: continue data = "" code = "408" err = "" self.logO(Anomaly.MSG_TIMEOUT, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, info=Anomaly.MSG_QS_TIMEOUT) timeouted = True else: err, cmd, warned = self.__findPatternInResponse(data, warned) if err != "": self.logVuln(category=Vulnerability.EXEC, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=Vulnerability.MSG_QS_INJECT.format(err, page)) self.logR(Vulnerability.MSG_QS_INJECT, err, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, info=Anomaly.MSG_QS_500) self.logO(Anomaly.MSG_500, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) if cmd: break for i in range(len(params_list)): timeouted = False warned = 0 cmd = 0 err500 = 0 saved_value = params_list[i][1] if saved_value is None: saved_value = "" params_list[i][1] = "__EXEC__" url = page + "?" + self.HTTP.encode(params_list) param_name = self.HTTP.quote(params_list[i][0]) if url not in self.attackedGET: self.attackedGET.append(url) for payload in self.payloads: err = "" payload = payload.replace("[VALUE]", saved_value) params_list[i][1] = self.HTTP.quote(payload) evil_req = HTTP.HTTPResource(page + "?" + self.HTTP.encode(params_list)) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: data, code = self.HTTP.send(evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: if timeouted: continue data = "" code = "408" err = "" self.logO(Anomaly.MSG_TIMEOUT, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) timeouted = True else: err, cmd, warned = self.__findPatternInResponse(data, warned) if err != "": self.logVuln(category=Vulnerability.EXEC, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in the parameter {1}").format(err, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, err, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) if cmd: # Successful command execution, go to the next field break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) params_list[i][1] = saved_value
def attackGET(self, http_res): """This method performs the CRLF attack with method GET""" page = http_res.path params_list = http_res.get_params resp_headers = http_res.headers referer = http_res.referer headers = {} if referer: headers["referer"] = referer payload = self.HTTP.quote( "http://www.google.fr\r\nwapiti: 2.3.0 version") if not params_list: # Do not attack application-type files if not "content-type" in resp_headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in resp_headers["content-type"]: return url = page + "?" + payload if url not in self.attackedGET: evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: resp = self.HTTP.send(evil_req, headers=headers) if "wapiti" in resp.getHeaders(): self.logVuln(category=Vulnerability.CRLF, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=self.MSG_VULN + " " + _("(QUERY_STRING)")) self.logR(Vulnerability.MSG_QS_INJECT, self.MSG_VULN, page) self.logR(Vulnerability.MSG_EVIL_URL, url) except requests.exceptions.Timeout: self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, info=self.MSG_VULN + " " + _("(QUERY_STRING)")) self.logO(Anomaly.MSG_TIMEOUT, page) self.logO(Anomaly.MSG_EVIL_URL, url) except requests.exceptions.HTTPError: # print("Error: The server did not understand this request") pass self.attackedGET.append(url) else: for i in range(len(params_list)): saved_value = params_list[i][1] if saved_value is None: saved_value = "" # payload is already escaped, see at top params_list[i][1] = payload param_name = self.HTTP.quote(params_list[i][0]) url = page + "?" + self.HTTP.encode(params_list) if url not in self.attackedGET: self.attackedGET.append(url) evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: resp = self.HTTP.send(evil_req, headers=headers) if "wapiti" in resp.getHeaders(): self.logVuln(category=Vulnerability.CRLF, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=self.MSG_VULN + " (" + param_name + ")") self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, url) except requests.exceptions.Timeout: self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info="Timeout (" + param_name + ")") self.logO(Anomaly.MSG_TIMEOUT, page) self.logO(Anomaly.MSG_EVIL_URL, url) except requests.exceptions.HTTPError: self.log( _("Error: The server did not understand this request" )) params_list[i][1] = saved_value
def attackPOST(self, form): """This method performs the Blind SQL attack with method POST""" # copies get_params = form.get_params post_params = form.post_params file_params = form.file_params referer = form.referer for params_list in [get_params, post_params, file_params]: for i in xrange(len(params_list)): saved_value = params_list[i][1] if saved_value is None: saved_value = "" param_name = self.HTTP.quote(params_list[i][0]) if params_list is file_params: params_list[i][1] = ["_SQL__", params_list[i][1][1]] else: params_list[i][1] = "__SQL__" attack_pattern = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params) if attack_pattern in self.excludedPOST: params_list[i][1] = saved_value continue err500 = 0 if attack_pattern not in self.attackedPOST: self.attackedPOST.append(attack_pattern) for payload in self.blind_sql_payloads: if params_list is file_params: payload = payload.replace("[VALUE]", saved_value[0]) params_list[i][1][0] = payload.replace( "__TIME__", self.TIME_TO_SLEEP) else: payload = payload.replace("[VALUE]", saved_value) params_list[i][1] = payload.replace( "__TIME__", self.TIME_TO_SLEEP) evil_req = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) if self.verbose == 2: print(u"+ {0}".format(evil_req)) try: resp = self.HTTP.send(evil_req) data, code = resp.getPageCode() except requests.exceptions.Timeout: # Timeout means time-based SQL injection self.logVuln( category=Vulnerability.BLIND_SQL_INJECTION, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in the " "parameter {1}").format( self.MSG_VULN, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, evil_req.url, param_name) self.logR(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format( param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') params_list[i][1] = saved_value
def attackPOST(self, form): """This method performs the cross site scripting attack (XSS attack) with method POST""" page = form.url referer = form.referer headers = {} if referer: headers["referer"] = referer param_name = "PHP_SELF" if page not in self.PHP_SELF: evil_req = None if page.endswith("/"): evil_req = HTTP.HTTPResource(page + self.php_self_payload) elif page.endswith(".php"): evil_req = HTTP.HTTPResource(page + "/" + self.php_self_payload) if evil_req: if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: data, http_code = self.HTTP.send(evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: data = "" self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) if self._validXSSContentType(evil_req) and self.php_self_check in data: self.logR(Vulnerability.MSG_PATH_INJECT, self.MSG_VULN, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) self.logVuln(category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("XSS vulnerability found via injection in the resource path")) elif http_code == "500": self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.PHP_SELF.append(page) timeouted = False returned500 = False # copies get_params = form.get_params post_params = form.post_params file_params = form.file_params for params_list in [get_params, post_params, file_params]: for i in xrange(len(params_list)): param_name = self.HTTP.quote(params_list[i][0]) saved_value = params_list[i][1] if saved_value is None: saved_value = "" if params_list is file_params: params_list[i][1] = ["_XSS__", params_list[i][1][1]] else: params_list[i][1] = "__XSS__" # We keep an attack pattern to be sure a given form won't be attacked on the same field several times attack_pattern = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params) if not attack_pattern in self.attackedPOST: self.attackedPOST.append(attack_pattern) code = self.random_string() if params_list is file_params: params_list[i][1][0] = code else: params_list[i][1] = code # will only memorize the last used payload (working or not) but the code will always be the good test_payload = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) self.POST_XSS[code] = (test_payload, param_name) try: data, http_code = self.HTTP.send(test_payload).getPageCode() except requests.exceptions.Timeout: data = "" # rapid search on the code to check injection if code in data: # found, now study where the payload is injected and how to exploit it payloads = self.generate_payloads(data, code) for payload in payloads: if params_list is file_params: params_list[i][1][0] = payload else: params_list[i][1] = payload evil_req = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) if self.verbose == 2: print(u"+ {0}".format(evil_req)) try: dat, http_code = self.HTTP.send(evil_req).getPageCode() except requests.exceptions.Timeout: dat = "" if timeouted: continue self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) timeouted = True if self._validXSSContentType(evil_req) and dat is not None and len(dat) > 1: if payload.lower() in dat.lower(): self.SUCCESSFUL_XSS[code] = payload self.logVuln(category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("XSS vulnerability found via injection" " in the parameter {0}").format(param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, evil_req.url, param_name) self.logR(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') # Stop injecting payloads and move to the next parameter break elif http_code == "500" and not returned500: self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') returned500 = True # restore the saved parameter in the list params_list[i][1] = saved_value
param_name) if v == code: if params_list is file_params: params_list[i][1][ 0] = self.SUCCESSFUL_XSS[ code] else: params_list[i][ 1] = self.SUCCESSFUL_XSS[ code] # we found the xss payload again -> stored xss vuln evil_req = HTTP.HTTPResource( code_req.path, method="POST", get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) self.logVuln( category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_( "Found permanent XSS attacked by {0} with fields" " {1}").format( evil_req.url, self.HTTP.encode( post_params)))
class mod_permanentxss(Attack): """ This class implements a cross site scripting attack """ # magic strings we must see to be sure script is vulnerable to XSS # payloads must be created on those patterns script_ok = [ "alert('__XSS__')", "alert(\"__XSS__\")", "String.fromCharCode(0,__XSS__,1)" ] # simple payloads that doesn't rely on their position in the DOM structure # payloads injected after closing a tag attribute value (attrval) or in the # content of a tag (text node like between <p> and </p>) # only trick here must be on character encoding, filter bypassing, stuff like that # form the simplest to the most complex, Wapiti will stop on the first working independant_payloads = [] name = "permanentxss" require = ["xss"] PRIORITY = 6 # two dict for permanent XSS scanning GET_XSS = {} POST_XSS = {} # key = xss code, valid = payload SUCCESSFUL_XSS = {} CONFIG_FILE = "xssPayloads.txt" MSG_VULN = _("Stored XSS vulnerability") def __init__(self, http, xmlRepGenerator): Attack.__init__(self, http, xmlRepGenerator) self.independant_payloads = self.loadPayloads( os.path.join(self.CONFIG_DIR, self.CONFIG_FILE)) # permanent XSS def attack(self, get_resources, forms): """This method searches XSS which could be permanently stored in the web application""" for http_resource in get_resources: if http_resource.method != "GET": continue url = http_resource.url target_req = HTTP.HTTPResource(url) referer = http_resource.referer headers = {} if referer: headers["referer"] = referer if self.verbose >= 1: print(u"+ {0}".format(url)) try: resp = self.HTTP.send(target_req, headers=headers) data = resp.getPage() except requests.exceptions.Timeout: data = "" except socket.error, se: data = "" print( _('error: {0} while attacking {1}').format( repr(str(se[1])), url)) except Exception, e: print( _('error: {0} while attacking {1}').format( repr(str(e[0])), url)) continue # Search for permanent XSS vulns which were injected via GET if self.doGET == 1: for code in self.GET_XSS: if code in data: # code found in the webpage ! code_url = self.GET_XSS[code][0].url page = self.GET_XSS[code][0].path param_name = self.GET_XSS[code][1] if code in self.SUCCESSFUL_XSS: # is this an already known vuln (reflected XSS) if self.validXSS(data, code, self.SUCCESSFUL_XSS[code]): # if we can find the payload again, this is a stored XSS evil_req = HTTP.HTTPResource( code_url.replace( code, self.SUCCESSFUL_XSS[code])) if param_name == "QUERY_STRING": self.logR(Vulnerability.MSG_QS_INJECT, self.MSG_VULN, page) else: self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, code_url) self.logVuln( category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("Found permanent XSS in {0}" " with {1}").format( page, self.HTTP.escape(evil_req.url))) # we reported the vuln, now search another code continue # we where able to inject the ID but will we be able to inject javascript? else: timeouted = False returned500 = False for xss in self.independant_payloads: payload = xss.replace("__XSS__", code) evil_req = HTTP.HTTPResource( code_url.replace(code, payload)) try: http_code = self.HTTP.send( evil_req).getCode() dat = self.HTTP.send(target_req).getPage() except requests.exceptions.Timeout: dat = "" if timeouted: continue self.logO(Anomaly.MSG_TIMEOUT, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') self.logAnom( category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format( param_name)) timeouted = True except Exception, e: print( _('error: {0} while attacking {1}'). format(repr(str(e[0])), url)) continue if self.validXSS(dat, code, payload): # injection successful :) if param_name == "QUERY_STRING": self.logR(Vulnerability.MSG_QS_INJECT, self.MSG_VULN, page) else: self.logR( Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) self.logVuln( category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("Found permanent XSS in {0}" " with {1}").format( url, self.HTTP.escape( evil_req.url))) # look for another code in the webpage break elif http_code == "500" and not returned500: self.logAnom( category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format( param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') returned500 = True
# Search for permanent XSS vulns which were injected via GET if self.doGET == 1: for code in self.GET_XSS: if code in data: # code found in the webpage ! code_url = self.GET_XSS[code][0].url page = self.GET_XSS[code][0].path param_name = self.GET_XSS[code][1] if code in self.SUCCESSFUL_XSS: # is this an already known vuln (reflected XSS) if self.validXSS(data, code, self.SUCCESSFUL_XSS[code]): # if we can find the payload again, this is a stored XSS evil_req = HTTP.HTTPResource( code_url.replace( code, self.SUCCESSFUL_XSS[code])) if param_name == "QUERY_STRING": self.logR(Vulnerability.MSG_QS_INJECT, self.MSG_VULN, page) else: self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, code_url) self.logVuln( category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("Found permanent XSS in {0}"
def attackPOST(self, form): """This method performs the file handling attack with method POST""" # copies get_params = form.get_params post_params = form.post_params file_params = form.file_params referer = form.referer err = "" for params_list in [get_params, post_params, file_params]: for i in xrange(len(params_list)): timeouted = False warn = 0 inc = 0 err500 = 0 saved_value = params_list[i][1] if saved_value is None: saved_value = "" param_name = self.HTTP.quote(params_list[i][0]) if params_list is file_params: params_list[i][1] = ["_FILE__", params_list[i][1][1]] else: params_list[i][1] = "__FILE__" attack_pattern = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params) if attack_pattern not in self.attackedPOST: self.attackedPOST.append(attack_pattern) for payload in self.payloads: payload = payload.replace('[FILE_NAME]', form.file_name) if params_list is file_params: payload = payload.replace('[VALUE]', saved_value[0]) payload = payload.replace( '[DIRVALUE]', saved_value[0].rsplit('/', 1)[0]) params_list[i][1][0] = payload else: payload = payload.replace('[VALUE]', saved_value) payload = payload.replace( '[DIRVALUE]', saved_value.rsplit('/', 1)[0]) params_list[i][1] = payload evil_req = HTTP.HTTPResource(form.path, method=form.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) if self.verbose == 2: print(u"+ {0}".format(evil_req)) try: data, code = self.HTTP.send(evil_req).getPageCode() except requests.exceptions.Timeout: if timeouted: continue data = "" code = "408" self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format( param_name)) self.logO(Anomaly.MSG_TIMEOUT, evil_req.path) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') timeouted = True else: err, inc, warn = self.__findPatternInResponse( data, warn) if err != "": info_msg = _( "{0} via injection in the parameter {1}") self.logVuln(category=Vulnerability.FILE_HANDLING, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=info_msg.format(err, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, err, evil_req.url, param_name) self.logR(Vulnerability.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') if inc: break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format( param_name)) self.logO(Anomaly.MSG_500, evil_req.url) self.logO(Anomaly.MSG_EVIL_REQUEST) self.logC(evil_req.http_repr) print('') params_list[i][1] = saved_value
def attackGET(self, http_res): """This method performs the file handling attack with method GET""" page = http_res.path params_list = http_res.get_params resp_headers = http_res.headers referer = http_res.referer headers = {} if referer: headers["referer"] = referer if not params_list: # Do not attack application-type files if not "content-type" in resp_headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in resp_headers["content-type"]: return timeouted = False warn = 0 inc = 0 err500 = 0 for payload in self.payloads: if "[VALUE]" in payload or "[DIRVALUE]" in payload or "[FILE_NAME]" in payload: continue err = "" url = page + "?" + self.HTTP.quote(payload) if url not in self.attackedGET: if self.verbose == 2: print(u"+ {0}".format(url)) self.attackedGET.append(url) evil_req = HTTP.HTTPResource(url) try: data, code = self.HTTP.send( evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: # Display a warning about timeout only once for a parameter if timeouted: continue data = "" code = "408" err = "" self.logAnom(category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, info=Anomaly.MSG_QS_TIMEOUT) self.logO(Anomaly.MSG_TIMEOUT, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) timeouted = True else: err, inc, warn = self.__findPatternInResponse( data, warn) if err != "": self.logVuln( category=Vulnerability.FILE_HANDLING, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=_("{0} via injection in the query string" ).format(err)) self.logR(Vulnerability.MSG_QS_INJECT, err, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) if inc: break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, info=Anomaly.MSG_QS_500) self.logO(Anomaly.MSG_500, evil_req.path) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) for i in range(len(params_list)): timeouted = False warn = 0 inc = 0 err500 = 0 param_name = self.HTTP.quote(params_list[i][0]) saved_value = params_list[i][1] if saved_value is None: saved_value = "" params_list[i][1] = "__FILE__" url = page + "?" + self.HTTP.encode(params_list) if url not in self.attackedGET: self.attackedGET.append(url) for payload in self.payloads: err = "" payload = payload.replace('[VALUE]', saved_value) payload = payload.replace('[DIRVALUE]', saved_value.rsplit('/', 1)[0]) payload = payload.replace('[FILE_NAME]', http_res.file_name) params_list[i][1] = self.HTTP.quote(payload) url = page + "?" + self.HTTP.encode(params_list) if self.verbose == 2: print(u"+ {0}".format(url)) evil_req = HTTP.HTTPResource(url) try: data, code = self.HTTP.send( evil_req, headers=headers).getPageCode() except requests.exceptions.Timeout: if timeouted: continue data = "" code = "408" err = "" self.logAnom( category=Anomaly.RES_CONSUMPTION, level=Anomaly.MEDIUM_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_TIMEOUT.format(param_name)) self.logO(Anomaly.MSG_TIMEOUT, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) timeouted = True else: err, inc, warn = self.__findPatternInResponse( data, warn) if err != "": self.logVuln( category=Vulnerability.FILE_HANDLING, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in the parameter {1}" ).format(err, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, err, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) if inc: break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom( category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format(param_name)) self.logO(Anomaly.MSG_500, evil_req.path) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) params_list[i][1] = saved_value
def attackGET(self, http_res): """This method performs the Blind SQL attack with method GET""" page = http_res.path params_list = http_res.get_params resp_headers = http_res.headers referer = http_res.referer headers = {} if referer: headers["referer"] = referer if not params_list: # Do not attack application-type files if not "content-type" in resp_headers: # Sometimes there's no content-type... so we rely on the document extension if (page.split(".")[-1] not in self.allowed) and page[-1] != "/": return elif not "text" in resp_headers["content-type"]: return pattern_url = page + "?__SQL__" if pattern_url in self.excludedGET: return if pattern_url not in self.attackedGET: self.attackedGET.append(pattern_url) err500 = 0 for payload in self.blind_sql_payloads: if "[VALUE]" in payload: continue payload = self.HTTP.quote( payload.replace("__TIME__", self.TIME_TO_SLEEP)) url = page + "?" + payload evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: resp = self.HTTP.send(evil_req, headers=headers) data, code = resp.getPageCode() except requests.exceptions.Timeout: self.logVuln( category=Vulnerability.BLIND_SQL_INJECTION, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter="QUERY_STRING", info=_("{0} via injection in the query string" ).format(self.MSG_VULN)) self.logR(Vulnerability.MSG_QS_INJECT, self.MSG_VULN, page) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter="QUERY_STRING", info=Anomaly.MSG_QS_500) self.logO(Anomaly.MSG_500, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) else: for i in range(len(params_list)): saved_value = params_list[i][1] if saved_value is None: saved_value = "" param_name = self.HTTP.quote(params_list[i][0]) params_list[i][1] = "__SQL__" pattern_url = page + "?" + self.HTTP.encode(params_list) # This field was successfully attacked with a non-blind SQL injection if pattern_url in self.excludedGET: params_list[i][1] = saved_value continue if pattern_url not in self.attackedGET: self.attackedGET.append(pattern_url) err500 = 0 for payload in self.blind_sql_payloads: payload = payload.replace("[VALUE]", saved_value) params_list[i][1] = self.HTTP.quote( payload.replace("__TIME__", self.TIME_TO_SLEEP)) url = page + "?" + self.HTTP.encode(params_list) evil_req = HTTP.HTTPResource(url) if self.verbose == 2: print(u"+ {0}".format(evil_req.url)) try: resp = self.HTTP.send(evil_req, headers=headers) data, code = resp.getPageCode() except requests.exceptions.Timeout: self.logVuln( category=Vulnerability.BLIND_SQL_INJECTION, level=Vulnerability.HIGH_LEVEL, request=evil_req, parameter=param_name, info=_("{0} via injection in " "the parameter {1}").format( self.MSG_VULN, param_name)) self.logR(Vulnerability.MSG_PARAM_INJECT, self.MSG_VULN, page, param_name) self.logR(Vulnerability.MSG_EVIL_URL, evil_req.url) # One payload worked. Now jum to next field break else: if code == "500" and err500 == 0: err500 = 1 self.logAnom(category=Anomaly.ERROR_500, level=Anomaly.HIGH_LEVEL, request=evil_req, parameter=param_name, info=Anomaly.MSG_PARAM_500.format( param_name)) self.logO(Anomaly.MSG_500, page) self.logO(Anomaly.MSG_EVIL_URL, evil_req.url) params_list[i][1] = saved_value
swf_links = flash_parser.getLinks() except Exception, err_data: swf_links = err_data[1] data = "" elif "/x-javascript" in mime_type or "/x-js" in mime_type or "/javascript" in mime_type: js_links = lamejs.lamejs(data).getLinks() data = "" # Manage redirections if "location" in info: redir = self.correctlink(info["location"], current, current_full_url, currentdir, proto, None) if redir is not None: if self.__inzone(redir) == 0: self.link_encoding[redir] = self.link_encoding[url] redir = HTTP.HTTPResource(redir) # Is the document already visited of forbidden ? if (redir in self.browsed) or (redir in self.tobrowse) or \ self.isExcluded(redir): pass else: # No -> Will browse it soon self.tobrowse.append(redir) htmlSource = data if page_encoding: bs = BeautifulSoup.BeautifulSoup(htmlSource) # Look for a base tag with an href attribute if bs.head: baseTags = bs.head.findAll("base") for base in baseTags: