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 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 __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 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 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, timeout: data = "" except socket.error, se: data = "" print(('error: {0} while attacking {1}').format(repr(str(se[1])), url))
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 = {} # 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()
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) 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"): # BeautifulSoup doesn't work as excepted with the "in" statement, keep this: if "href" in base.attrs: # Found a base url, now set it as the current url current = base["href"].split("#")[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 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 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 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
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 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
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 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 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 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 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: 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 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
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
def attack(self, urls, forms): junk_string = "w" + "".join([random.choice("0123456789abcdefghjijklmnopqrstuvwxyz") for __ in xrange(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 try: url = "http://" + self.HTTP.server + path except UnicodeDecodeError: continue 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: continue try: resp = self.HTTP.send(evil_req) except Exception, e: # requests bug print(e) continue page, code = resp.getPageCode() encoding = BeautifulSoup(page).originalEncoding if encoding: page = unicode(page, encoding, errors='ignore') raw = " ".join([x + ": " + y for x, y in resp.getHeaders().items()]) raw += page # First condition (match) if len(l[5]) == 3 and l[5].isdigit(): if code == int(l[5]): match = True else: if l[5] in raw: match = True # Second condition (or) if l[6] != "": if len(l[6]) == 3 and l[6].isdigit(): if code == int(l[6]): match_or = True else: if l[6] in raw: match_or = True # Third condition (and) if l[7] != "": if len(l[7]) == 3 and l[7].isdigit(): if code == int(l[7]): match_and = True else: if l[7] in raw: match_and = True else: match_and = True # Fourth condition (fail) if l[8] != "": if len(l[8]) == 3 and l[8].isdigit(): if code == int(l[8]): fail = True else: if l[8] in raw: fail = True # Fifth condition (or) if l[9] != "": if len(l[9]) == 3 and l[9].isdigit(): if code == int(l[9]): fail_or = True else: if l[9] in raw: fail_or = True if ((match or match_or) and match_and) and not (fail or fail_or): print(url) print(vuln_desc) refs = [] if osv_id != "0": refs.append("http://osvdb.org/show/osvdb/" + osv_id) # CERT m = re.search("(CA\-[0-9]{4}-[0-9]{2})", vuln_desc) if m is not None: refs.append("http://www.cert.org/advisories/" + m.group(0) + ".html") # SecurityFocus m = re.search("BID\-([0-9]{4})", vuln_desc) if m is not None: refs.append("http://www.securityfocus.com/bid/" + m.group(1)) # Mitre.org m = re.search("((CVE|CAN)\-[0-9]{4}-[0-9]{4,})", vuln_desc) if m is not None: refs.append("http://cve.mitre.org/cgi-bin/cvename.cgi?name=" + m.group(0)) # CERT Incidents m = re.search("(IN\-[0-9]{4}\-[0-9]{2})", vuln_desc) if m is not None: refs.append("http://www.cert.org/incident_notes/" + m.group(0) + ".html") # Microsoft Technet m = re.search("(MS[0-9]{2}\-[0-9]{3})", vuln_desc) if m is not None: refs.append("http://www.microsoft.com/technet/security/bulletin/" + m.group(0) + ".asp") info = vuln_desc if refs: print(_("References:")) print(u" {0}".format(u"\n ".join(refs))) info += "\n" + _("References:") + "\n" info += "\n".join(['<a href="' + x + '">' + x + '</a>' for x in refs]) print('') self.logVuln(category=Vulnerability.NIKTO, level=Vulnerability.HIGH_LEVEL, request=evil_req, info=info)
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