def _inject_to(r, target, payloads, pre_func=None, append=False): if not pre_func: pre_func = lambda x: encode(x) payloads = [ pre_func(pd) for pd in _get_payload(payloads) ] rqs = RequestSet(_inject_query(r, target, payloads, append)) if r.method in ("POST", "PUT"): rqs += RequestSet(_inject_post(r, target, payloads, append)) if r.has_header("Cookie"): rqs += RequestSet(_inject_cookie(r, target, payloads, append)) rqs += RequestSet(_inject_json(r, target, payloads, append)) if not rqs: raise NoInjectionPointFound() return rqs
def inject(r, to=None, at=None, payloads="default", **kwds): """ Inject a request. This function will create a RequestSet from a Request where a part of the request is replaced with some payload. There is two ways to use this function, either to inject the value of a parameter or to inject at a specific location. When used with the 'to' parameter, Burst will lookup the value in the query string, the request content and the cookies. It will then replace the value of the parameter with the payloads. If no valid injection point is found, an error is raised. When used with the 'at' parameter, Burst will lookup the string in the whole request text and replace it with the payloads. If no valid injection point is found, an error is raised. If the string is found more than once, the function will suggest to provide the 'choice' integer keyword. payloads could either be a list of the payloads to inject or a key of the global dictionnary 'payloads'. Before being injected, each payload goes through the pre_func function which is by default 'encode'. See also: payloads, inject_all, find_injection_points """ rqs = RequestSet() if not to and not at: print error("I need some help here. Where should I inject? " + \ "Try 'help(inject)'") elif to and at: print error("Wow, too many parameters. It is either 'to' or 'at'.") elif to: if isinstance(to, (list, tuple)): for t in to: rqs.extend(_inject_multi(r, _inject_to, t, payloads, **kwds)) else: rqs.extend(_inject_multi(r, _inject_to, to, payloads, **kwds)) elif at: if isinstance(at, (list, tuple)): for a in at: rqs.extend(_inject_multi(r, _inject_at, a, payloads, **kwds)) else: rqs.extend(_inject_multi(r, _inject_at, at, payloads, **kwds)) return rqs
def import_from_burp(requests_file): if not has_lxml: raise Exception("To use the import, you need lxml") tree = lxml.etree.parse(requests_file) items = tree.getroot() rs = RequestSet() for item in items: r = Request(base64.decodestring(item.find("request").text), item.find("host").text, item.find("port").text, True if item.find("protocol").text == "https" else False) raw_date = item.find("time").text date_without_tz = " ".join([x for i,x in enumerate(raw_date.split(" ")) if i != 4]) r.sent_date = datetime.datetime.strptime(date_without_tz, "%c") r.response = Response(base64.decodestring(item.find("response").text), r) rs.append(r) r.response.received_date = r.sent_date return rs
def spider(init, max=-1, ignore_qs=False, post_func=None, excluded_func=None, hosts=None): """ Spider a request by following some links. init - The initial request(s) max - The maximum of request to execute post_func - A hook to be executed after each new page fetched hosts - A lists of authorised hosts to spider on. By default only the hostname of r_init is allowed. excluded_func - A predicate that must indicates if a Request should be executed. """ nb = 0 checked = [] if isinstance(init, Request): q = deque([init, ]) hs = [ init.hostname, ] elif isinstance(init, RequestSet): q = deque(init) hs = list(set(init.extract("hostname"))) else: raise TypeError("init must be a Request or a RequestSet") if hosts: hs += hosts try: while nb != max and q: to_add = [] r = q.popleft() print str(len(checked)) + "/" + str(len(q)), clear_line() if not r.response: r() if r.response.content_type: if re.match(r'text/html', r.response.content_type): to_add += _follow_redirect(r) to_add += _get_links(r) else: print "\nIgnoring", r.response.content_type checked.append(r) if post_func: post_func(r) for nr in to_add: if nr.hostname not in hs: continue if excluded_func and excluded_func(nr): continue if not ignore_qs and any(nr == rc for rc in checked + list(q)): continue if ignore_qs and any(nr.similar(rc) for rc in checked + list(q)): continue q.append(nr) nb += 1 except KeyboardInterrupt: print str(len(checked)) + "/" + str(len(q)) return RequestSet(checked)
def inject(r, to=None, at=None, payloads="default", **kwds): """ Inject a request. This function will create a RequestSet from a Request where a part of the request is replaced with some payload. There is two ways to use this function, either to inject the value of a parameter or to inject at a specific location. When used with the 'to' parameter, Burst will lookup the value in the query string, the request content and the cookies. It will then replace the value of the parameter with the payloads. If no valid injection point is found, an error is raised. When used with the 'at' parameter, Burst will lookup the string in the whole request text and replace it with the payloads. If no valid injection point is found, an error is raised. If the string is found more than once, the function will suggest to provide the 'choice' integer keyword. payloads could either be a list of the payloads to inject or a key of the global dictionnary 'payloads'. Before being injected, each payload pass through the pre_func function which is by default 'encode'. See also: payloads, inject_all, find_injection_points """ rqs = RequestSet() if not to and not at: print error("I need some help here. Where should I inject? " + \ "Try 'help(inject)'") elif to and at: print error("Wow, too many parameters. It is either 'to' or 'at'.") elif to: if isinstance(to, (list, tuple)): for t in to: rqs.extend(_inject_multi(r, _inject_to, t, payloads, **kwds)) else: rqs.extend(_inject_multi(r, _inject_to, to, payloads, **kwds)) elif at: if isinstance(at, (list, tuple)): for a in at: rqs.extend(_inject_multi(r, _inject_at, a, payloads, **kwds)) else: rqs.extend(_inject_multi(r, _inject_at, at, payloads, **kwds)) return rqs
def fuzz_headers(r, payloads="default"): print "TODO: adapt payloads for each header tested" rs = [] for i, e in enumerate(r.headers): k, v = e payloads = _get_payload(payloads) for p in payloads: r_new = r.copy() h_new = (k, p) r_new.headers[i] = h_new r_new.injection_point = k r_new.payload = p rs.append(r_new) return RequestSet(rs)
def _get_links(r): new_reqs = [] if not has_lxml: raise Exception("To use the spider, you need lxml") try: root = lxml.html.fromstring(r.response.content) base_tag = root.xpath('//base') if base_tag and base_tag[0].get('href'): base = base_tag[0].get('href') else: base = r.url links = [ x.get("href").strip() for x in root.xpath("//a|//area") if x.get('href')] links += [ x.get("src").strip() for x in root.xpath("//frame") if x.get('src')] for l in links: try: l.encode('ascii') except UnicodeEncodeError: l = e(l.encode('utf-8'), safe='/') url_p = urlparse.urlparse(l) if url_p.scheme in ('http', 'https'): try: new_reqs.append(create(l)) except: print "Invalid link:", l continue elif url_p.scheme in ('javascript', 'mailto') or l.startswith("#"): continue elif url_p.scheme == '' and url_p.path: nr = r.copy() n_path = urlparse.urljoin(base, l) nr.url = urlparse.urlunparse(urlparse.urlparse(r.url)[:2] + urlparse.urlparse(n_path)[2:]) new_reqs.append(nr) else: if url_p.scheme not in ("ftp", "irc", "xmpp", "mms", "tel"): print "UNKNOWN PROTOCOL Miam!?:" + l, url_p.scheme except lxml.etree.XMLSyntaxError: pass return RequestSet(new_reqs)
def proxy(ip=None, port=None, rules=( ru_bypass_ssl, ru_forward_images, ), alerter=None, persistent=True, pre_func=None, post_func=None, decode_func=None, forward_chunked=False, verbose=False): """Intercept all HTTP(S) requests on port. Return a RequestSet of all the answered requests. ip -- ip to listen to, by default conf.ip port -- port to listen to, by default conf.port alerter -- alerter triggered on each response, by default GenericAlerter rules -- set of rules for automated actions over requests pre_func -- callback used before processing a request post_func -- callback used before processing a response decode_func -- callback used when (de)coding a request/response content, by default, decode(). forward_chunked -- forward chunked response without waiting for the end of it persistent -- keep the connection persistent with your client verbose -- degree of verbosity: False -- Only display requests undergoing default_action 1/True -- Display all requests, including automated ones 2 -- Display all requests with their full content 3 -- Display all requests and responses with their full content See also: conf """ if not ip: ip = conf.ip if not port: port = conf.port if not alerter: alerter = alert.GenericAlerter() if not rules: rules = [] if not decode_func: decode_func = decode if not pre_func: pre_func = lambda x: x if not post_func: post_func = lambda x: x print "Running on", ip + ":" + str(port) print "Ctrl-C to interrupt the proxy..." httpd = ProxyHTTPServer((ip, port), ProxyHTTPRequestHandler) httpd.rules = rules httpd.auto = False httpd.pre_func = pre_func httpd.post_func = post_func httpd.decode_func = decode_func httpd.alerter = alerter httpd.reqs = [] httpd.forward_chunked = forward_chunked httpd.verbose = verbose httpd.persistent = persistent while True: try: httpd.serve_forever() except select.error: # select syscall got interrupted by window resizing pass except KeyboardInterrupt: print "Waiting for the threads to stop" httpd.shutdown() for t in threading.enumerate(): if t.name.startswith("proxy"): t.join() break return RequestSet(httpd.reqs)
def import_from_curl(curl=None): """create() a RequestSet object from a curl commandline. curl-style [start:stop:step] and {a,b,c} counters are supported. See http://curl.haxx.se/docs/manpage.html#URL for examples. Currently supported options (shorthand form also accepted): --header ; --cookie ; --data-ascii ; --request ; --user-agent; --referer ; --user ; --range """ if curl is None: curl = raw_input('curl cmdline: ') if type(curl) != list: curl = split(curl) # index counter used for variable parsing i = 0 ## The returned RequestSet(): rs = RequestSet() ## skip leading image name / path component if curl[0] == 'curl' or curl[0].startswith('/'): i = 1 HEADERS = 'HEADERS' METHOD = 'METHOD' CONTENT = 'CONTENT' o = {HEADERS: []} def add_url(url): rs.append(create(url.replace(' ', '%20'))) while i < len(curl): if curl[i].startswith('-'): this = curl[i] i += 1 arg = None if i < len(curl): arg = curl[i] if this in __curl_unimplemented[0].union(__curl_unimplemented[1]): print 'not implemented: ignoring option "{}"'.format(this) if this in __curl_unimplemented[1]: i += 1 continue ## options that require an argument, check that we have one elif arg: if this in ('-A', '--user-agent'): o[HEADERS].append(('User-Agent', arg)) elif this in ('-b', '--cookie'): o[HEADERS].append(arg) elif this in ('-d', '--data', '--data-ascii'): o[METHOD] = 'POST' o[CONTENT] = arg elif this in ('-e', '--referer'): o[HEADERS].append(('Referer', arg)) elif this in ('-H', '--header'): o[HEADERS].append(parse_headers(arg)[0]) elif this in ('--url'): add_url(arg) elif this in ('u', '--user'): if not ':' in arg: arg += ':' ## TODO this should probably be a first class citizen in Request: o[HEADERS].append( ('Authorization', 'Basic {}'.format(b64encode(arg)))) elif this in ('-r', '--range'): o[HEADERS].append(('Range', 'bytes={}'.format(arg))) elif this in ('-X', '--request'): o[METHOD] = arg else: raise Exception( 'not implemented and unknown option: "{}"'.format( this)) ## not a --switch, and not an argument, so parse it as a URL: else: add_url(curl[i]) i += 1 if [] == rs.reqs: raise Exception('curl string must contain at least one URL') ## apply options for i in xrange(0, len(rs)): rs[i].method = o.get(METHOD, rs[i].method) rs[i].content = o.get(CONTENT, rs[i].content) for hd, val in o[HEADERS]: if hd not in ('Cookie', ): ## replace unique headers rs[i].remove_header(hd) rs[i].add_header(hd, val) rs.expand_curl_ranges() return rs
def inject_all(r, payloads="default"): ips = find_injection_points(r) if ips: return reduce(lambda x, y: x + y, [i(r, to=ip, payloads=payloads) for ip in ips]) return RequestSet()
def _inject_multi(r, method, target, payloads, **kwds): if isinstance(r, Request): return method(r, target, payloads, **kwds) elif isinstance(r, RequestSet): return RequestSet(reduce(lambda x, y: x + y, [ method(ro, target, payloads, **kwds) for ro in r ]))