def attack(self): methods = "" if self.do_get: methods += "G" if self.do_post: methods += "PF" mutator = Mutator(methods=methods, payloads=self.random_string, qs_inject=self.must_attack_query_string, skip=self.options.get("skipped_parameters")) http_resources = self.persister.get_links( attack_module=self.name) if self.do_get else [] forms = self.persister.get_forms( attack_module=self.name) if self.do_post else [] for original_request in chain(http_resources, forms): if self.verbose >= 1: print("[+] {}".format(original_request)) for mutated_request, parameter, taint, flags in mutator.mutate( original_request): try: # We don't display the mutated request here as the payload is not interesting try: response = self.crawler.send(mutated_request) except ReadTimeout: # We just inserted harmless characters, if we get a timeout here, it's not interesting continue else: # We keep a history of taint values we sent because in case of stored value, the taint code # may be found in another webpage by the permanentxss module. self.tried_xss[taint] = (mutated_request, parameter, flags) # Reminder: valid_xss_content_type is not called before before content is not necessary # reflected here, may be found in another webpage so we have to inject tainted values # even if the Content-Type seems uninteresting. if taint.lower() in response.content.lower( ) and valid_xss_content_type(mutated_request): # Simple text injection worked in HTML response, let's try with JS code payloads = generate_payloads( response.content, taint, self.PAYLOADS_FILE) # TODO: check that and make it better if flags.method == PayloadType.get: method = "G" elif flags.method == PayloadType.file: method = "F" else: method = "P" self.attempt_exploit(method, payloads, original_request, parameter, taint) except KeyboardInterrupt as exception: yield exception yield original_request
async def attack(self, request: Request): for mutated_request, parameter, taint, flags in self.mutator.mutate( request): # We don't display the mutated request here as the payload is not interesting try: response = await self.crawler.async_send(mutated_request) except RequestError: self.network_errors += 1 # We just inserted harmless characters, if we get a timeout here, it's not interesting continue else: # We keep a history of taint values we sent because in case of stored value, the taint code # may be found in another webpage by the permanentxss module. self.tried_xss[taint] = (mutated_request, parameter, flags) # Reminder: valid_xss_content_type is not called before before content is not necessary # reflected here, may be found in another webpage so we have to inject tainted values # even if the Content-Type seems uninteresting. if taint.lower() in response.content.lower( ) and valid_xss_content_type(mutated_request): # Simple text injection worked in HTML response, let's try with JS code payloads = generate_payloads(response.content, taint, self.PAYLOADS_FILE, self.external_endpoint) # TODO: check that and make it better if flags.method == PayloadType.get: method = "G" elif flags.method == PayloadType.file: method = "F" else: method = "P" await self.attempt_exploit(method, payloads, request, parameter, taint)
async def attack(self, request: Request): """This method searches XSS which could be permanently stored in the web application""" url = request.url target_req = Request(url) referer = request.referer headers = {} if referer: headers["referer"] = referer try: response = await self.crawler.async_send(target_req, headers=headers) data = response.content except RequestError: self.network_errors += 1 return # Should we look for taint codes sent with GET in the webpages? # Exploiting those may imply sending more GET requests # Search in the page source for every taint code used by mod_xss for taint in self.tried_xss: input_request = self.tried_xss[taint][0] # Such situations should not occur as it would be stupid to block POST (or GET) requests for mod_xss # and not mod_permanentxss, but it is possible so let's filter that. if not self.do_get and input_request.method == "GET": continue if not self.do_post and input_request.method == "POST": continue if taint.lower() in data.lower(): # Code found in the webpage ! # Did mod_xss saw this as a reflected XSS ? if taint in self.successful_xss: # Yes, it means XSS payloads were injected, not just tainted code. payload, flags = self.successful_xss[taint] if self.check_payload(response, flags, taint): # If we can find the payload again, this is in fact a stored XSS get_params = input_request.get_params post_params = input_request.post_params file_params = input_request.file_params referer = input_request.referer # The following trick may seems dirty but it allows to treat GET and POST requests # the same way. for params_list in [ get_params, post_params, file_params ]: for i, __ in enumerate(params_list): parameter, value = params_list[i] parameter = quote(parameter) if value != taint: continue if params_list is file_params: params_list[i][1][0] = payload else: params_list[i][1] = payload # we found the xss payload again -> stored xss vuln evil_request = Request( input_request.path, method=input_request.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer) if request.path == input_request.path: description = _( "Permanent XSS vulnerability found via injection in the parameter {0}" ).format(parameter) else: description = _( "Permanent XSS vulnerability found in {0} by injecting" " the parameter {1} of {2}").format( request.url, parameter, input_request.path) if has_strong_csp(response): description += ".\n" + _( "Warning: Content-Security-Policy is present!" ) await self.add_vuln_high( request_id=request.path_id, category=NAME, request=evil_request, parameter=parameter, info=description) if parameter == "QUERY_STRING": injection_msg = Messages.MSG_QS_INJECT else: injection_msg = Messages.MSG_PARAM_INJECT self.log_red("---") self.log_red(injection_msg, self.MSG_VULN, request.path, parameter) if has_strong_csp(response): self.log_red( _("Warning: Content-Security-Policy is present!" )) self.log_red(Messages.MSG_EVIL_REQUEST) self.log_red(evil_request.http_repr()) self.log_red("---") # FIX: search for the next code in the webpage # Ok the content is stored, but will we be able to inject javascript? else: parameter = self.tried_xss[taint][1] payloads = generate_payloads(response.content, taint, self.PAYLOADS_FILE, self.external_endpoint) flags = self.tried_xss[taint][2] # TODO: check that and make it better if flags.method == PayloadType.get: method = "G" elif flags.method == PayloadType.file: method = "F" else: method = "P" await self.attempt_exploit(method, payloads, input_request, parameter, taint, request)
def attack(self): """This method searches XSS which could be permanently stored in the web application""" get_resources = self.persister.get_links(attack_module=self.name) if self.do_get else [] for original_request in get_resources: if not valid_xss_content_type(original_request) or original_request.status in (301, 302, 303): # If that content-type can't be interpreted as HTML by browsers then it is useless # Same goes for redirections continue url = original_request.url target_req = web.Request(url) referer = original_request.referer headers = {} if referer: headers["referer"] = referer if self.verbose >= 1: print("[+] {}".format(url)) try: response = self.crawler.send(target_req, headers=headers) data = response.content except Timeout: continue except OSError as exception: # TODO: those error messages are useless, don't give any valuable information print(_("error: {0} while attacking {1}").format(exception.strerror, url)) continue except Exception as exception: print(_("error: {0} while attacking {1}").format(exception, url)) continue # Should we look for taint codes sent with GET in the webpages? # Exploiting those may imply sending more GET requests # Search in the page source for every taint code used by mod_xss for taint in self.TRIED_XSS: input_request = self.TRIED_XSS[taint][0] # Such situations should not occur as it would be stupid to block POST (or GET) requests for mod_xss # and not mod_permanentxss, but it is possible so let's filter that. if not self.do_get and input_request.method == "GET": continue if not self.do_post and input_request.method == "POST": continue if taint.lower() in data.lower(): # Code found in the webpage ! # Did mod_xss saw this as a reflected XSS ? if taint in self.SUCCESSFUL_XSS: # Yes, it means XSS payloads were injected, not just tainted code. payload, flags = self.SUCCESSFUL_XSS[taint] if self.check_payload(response, flags, taint): # If we can find the payload again, this is in fact a stored XSS get_params = input_request.get_params post_params = input_request.post_params file_params = input_request.file_params referer = input_request.referer # The following trick may seems dirty but it allows to treat GET and POST requests # the same way. for params_list in [get_params, post_params, file_params]: for i in range(len(params_list)): parameter, value = params_list[i] parameter = quote(parameter) if value != taint: continue if params_list is file_params: params_list[i][1][0] = payload else: params_list[i][1] = payload # we found the xss payload again -> stored xss vuln evil_request = web.Request( input_request.path, method=input_request.method, get_params=get_params, post_params=post_params, file_params=file_params, referer=referer ) if original_request.path == input_request.path: description = _( "Permanent XSS vulnerability found via injection in the parameter {0}" ).format(parameter) else: description = _( "Permanent XSS vulnerability found in {0} by injecting" " the parameter {1} of {2}" ).format( original_request.url, parameter, input_request.path ) if has_csp(response): description += ".\n" + _("Warning: Content-Security-Policy is present!") self.add_vuln( request_id=original_request.path_id, category=Vulnerability.XSS, level=Vulnerability.HIGH_LEVEL, request=evil_request, parameter=parameter, info=description ) if parameter == "QUERY_STRING": injection_msg = Vulnerability.MSG_QS_INJECT else: injection_msg = Vulnerability.MSG_PARAM_INJECT self.log_red("---") self.log_red( injection_msg, self.MSG_VULN, original_request.path, parameter ) if has_csp(response): self.log_red(_("Warning: Content-Security-Policy is present!")) self.log_red(Vulnerability.MSG_EVIL_REQUEST) self.log_red(evil_request.http_repr()) self.log_red("---") # FIX: search for the next code in the webpage # Ok the content is stored, but will we be able to inject javascript? else: parameter = self.TRIED_XSS[taint][1] payloads = generate_payloads(response.content, taint, self.independant_payloads) flags = self.TRIED_XSS[taint][2] # TODO: check that and make it better if PayloadType.get in flags: method = "G" elif PayloadType.file in flags: method = "F" else: method = "P" self.attempt_exploit(method, payloads, input_request, parameter, taint, original_request) yield original_request