def test_url_parser(): print "Testing URL parsing for all properties..." for case in simple: url = case['url'] d = ParsedURL(url) for key, value in case.iteritems(): try: assert getattr(d, key) == value except AssertionError: print "-" * 79 print "Failed test case: %r" % url print "Attribute name: %r" % key print "Expected value: %r" % value print "Got instead: %r" % getattr(d, key) print "-" * 79 raise
def ttl_platform_detection(self, main_url): """ This function tries to recognize the remote platform doing a ping and analyzing the TTL of IP header response. :param main_url: Base url to test. :type main_url: str :return: Possible platforms. :rtype: list(tuple(OS, version)) """ # Do a ping try: m_ttl = do_ping_and_receive_ttl(ParsedURL(main_url).hostname, 2) # Load words for the wordlist l_wordlist_instance = WordListLoader.get_advanced_wordlist_as_dict( Config.plugin_extra_config["Wordlist_ttl"]["ttl"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(m_ttl) if l_matches: m_ret = {} for v in l_matches: sp = v.split("|") k = sp[0].strip() v = sp[1].strip() m_ret[k] = v return [(k, v) for k, v in m_ret.iteritems()] else: return {} except EnvironmentError: Logger.log_error( "[!] You can't run the platform detection plugin if you're not root." ) return {} except Exception, e: Logger.log_error("[!] Platform detection failed, reason: %s" % e) return {}
def test_url_parser_custom(): print "Testing URL modification and reparsing..." # Relative URLs. assert ParsedURL("/index.html", base_url="http://www.example.com" ).url == "http://www.example.com/index.html" assert ParsedURL("index.html", base_url="http://www.example.com/folder/" ).url == "http://www.example.com/folder/index.html" assert ParsedURL("index.html", base_url="http://www.example.com/folder" ).url == "http://www.example.com/index.html" # Setters. d = ParsedURL("http://www.example.com") assert d.path == "/" d.path = "/index.html" assert d.url == "http://www.example.com/index.html" assert d.path == "/index.html" assert d.port == 80 d.scheme = "https" assert d.url == "https://www.example.com/index.html" assert d.port == 443 d.port = 8080 assert d.port == 8080 assert d.url == "https://www.example.com:8080/index.html" d.scheme = "http://" assert d.port == 8080 assert d.url == "http://www.example.com:8080/index.html" d.port = None assert d.port == 80 assert d.url == "http://www.example.com/index.html" d.path = "index.html" assert d.path == "/index.html" assert d.url == "http://www.example.com/index.html" d.host = "www.site.com" assert d.url == "http://www.site.com/index.html" d.netloc = "user:[email protected]" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.username == "user" assert d.password == "pass" d.username = "******" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "someone:[email protected]" d.password = "******" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "someone:[email protected]" assert d.auth == "someone:secret" d.password = None assert d.url == "http://[email protected]/index.html" assert d.netloc == "*****@*****.**" assert d.auth == "someone" d.password = "******" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "someone:[email protected]" assert d.auth == "someone:secret" d.username = None assert d.url == "http://:[email protected]/index.html" assert d.netloc == ":[email protected]" assert d.auth == ":secret" d.auth = "test:key" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "test:[email protected]" assert d.username == "test" assert d.password == "key" d.auth = None assert d.url == "http://www.site.com/index.html" assert d.netloc == "www.site.com" assert d.username == "" assert d.password == "" d.fragment = "fragment" assert d.url == "http://www.site.com/index.html#fragment" assert d.fragment == "fragment" d.fragment = None assert d.url == "http://www.site.com/index.html" assert d.fragment == "" d.query = "key=value¶m=data" assert d.url == "http://www.site.com/index.html?key=value¶m=data" assert d.query_char == "?" assert d.query == "key=value¶m=data" assert d.query_params == {"key": "value", "param": "data"} d.query_params["test"] = "me" assert d.url == "http://www.site.com/index.html?key=value¶m=data&test=me" assert d.query == "key=value¶m=data&test=me" assert d.query_params == {"key": "value", "param": "data", "test": "me"} d.query_params = {"some": "thing"} assert d.url == "http://www.site.com/index.html?some=thing" assert d.query == "some=thing" assert d.query_params == {"some": "thing"} d.query = "a=b&c" assert d.url == "http://www.site.com/index.html?a=b&c=" assert d.query == "a=b&c=" assert d.query_params == {"a": "b", "c": ""} d.query = "teststring".encode("rot13") assert d.url == "http://www.site.com/index.html?" + "teststring".encode( "rot13") assert d.query == "teststring".encode("rot13") assert d.query_params == {} d.query = "test string".encode("base64")[:-1] assert d.url == "http://www.site.com/index.html?" + "test string".encode( "base64")[:-1] assert d.query == "test string".encode("base64")[:-1] assert d.query_params == {} d.query = "test string".encode("base64") assert d.url == "http://www.site.com/index.html?" + "test string".encode( "base64")[:-1] + "%0A" assert d.query == "test string".encode("base64")[:-1] + "%0A" assert d.query_params == {"test string".encode("base64")[:-2]: "\n"} d.query = "test=me" d.query_char = "/" assert d.url == "http://www.site.com/index.html/test=me" assert d.query_char == "/" assert d.query == "test=me" assert d.query_params == {"test": "me"} d.fragment = "frag" assert d.url == "http://www.site.com/index.html/test=me#frag" # Methods. d.hostname = "this.is.a.subdomain.of.example.co.uk" assert ".".join(d.split_hostname()) == d.host assert d.split_hostname() == ("this.is.a.subdomain.of", "example", "co.uk") d.path = "/folder.with.extensions/file.pdf.exe" assert d.get_all_extensions(directory_allowed=False, double_allowed=True) == [".pdf", ".exe"] assert d.get_all_extensions( directory_allowed=True, double_allowed=True) == [".with", ".extensions", ".pdf", ".exe"] assert d.get_all_extensions(directory_allowed=False, double_allowed=False) == [".exe"] assert d.get_all_extensions( directory_allowed=True, double_allowed=False) == [".extensions", ".exe"] assert d.get_all_extensions(directory_allowed=False) == [".pdf", ".exe"] assert d.get_all_extensions(double_allowed=False) == [ ".extensions", ".exe" ] assert d.get_all_extensions() == [".with", ".extensions", ".pdf", ".exe"] # Exceptions. last_url = d.url try: d.query_char = "*" assert False except ValueError: pass try: d.scheme = "fake://" assert False except ValueError: pass try: d.port = "fake" assert False except ValueError: pass try: d.port = -1 assert False except ValueError: pass try: d.port = 80000 assert False except ValueError: pass assert d.url == last_url # Warnings. with catch_warnings(record=True) as w: d.fragment = "#test" d.query = "?test=me" assert len(w) == 2
def test_url_parser_custom(): print "Testing URL modification and reparsing..." # Relative URLs. assert ParsedURL("/index.html", base_url="http://www.example.com").url == "http://www.example.com/index.html" assert ParsedURL("index.html", base_url="http://www.example.com/folder/").url == "http://www.example.com/folder/index.html" assert ParsedURL("index.html", base_url="http://www.example.com/folder").url == "http://www.example.com/index.html" # Setters. d = ParsedURL("http://www.example.com") assert d.path == "/" d.path = "/index.html" assert d.url == "http://www.example.com/index.html" assert d.path == "/index.html" assert d.port == 80 d.scheme = "https" assert d.url == "https://www.example.com/index.html" assert d.port == 443 d.port = 8080 assert d.port == 8080 assert d.url == "https://www.example.com:8080/index.html" d.scheme = "http://" assert d.port == 8080 assert d.url == "http://www.example.com:8080/index.html" d.port = None assert d.port == 80 assert d.url == "http://www.example.com/index.html" d.path = "index.html" assert d.path == "/index.html" assert d.url == "http://www.example.com/index.html" d.host = "www.site.com" assert d.url == "http://www.site.com/index.html" d.netloc = "user:[email protected]" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.username == "user" assert d.password == "pass" d.username = "******" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "someone:[email protected]" d.password = "******" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "someone:[email protected]" assert d.auth == "someone:secret" d.password = None assert d.url == "http://[email protected]/index.html" assert d.netloc == "*****@*****.**" assert d.auth == "someone" d.password = "******" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "someone:[email protected]" assert d.auth == "someone:secret" d.username = None assert d.url == "http://:[email protected]/index.html" assert d.netloc == ":[email protected]" assert d.auth == ":secret" d.auth = "test:key" assert d.url == "http://*****:*****@www.site.com/index.html" assert d.netloc == "test:[email protected]" assert d.username == "test" assert d.password == "key" d.auth = None assert d.url == "http://www.site.com/index.html" assert d.netloc == "www.site.com" assert d.username == "" assert d.password == "" d.fragment = "fragment" assert d.url == "http://www.site.com/index.html#fragment" assert d.fragment == "fragment" d.fragment = None assert d.url == "http://www.site.com/index.html" assert d.fragment == "" d.query = "key=value¶m=data" assert d.url == "http://www.site.com/index.html?key=value¶m=data" assert d.query_char == "?" assert d.query == "key=value¶m=data" assert d.query_params == { "key": "value", "param": "data" } d.query_params["test"] = "me" assert d.url == "http://www.site.com/index.html?key=value¶m=data&test=me" assert d.query == "key=value¶m=data&test=me" assert d.query_params == { "key": "value", "param": "data", "test": "me" } d.query_params = { "some": "thing" } assert d.url == "http://www.site.com/index.html?some=thing" assert d.query == "some=thing" assert d.query_params == { "some": "thing" } d.query = "a=b&c" assert d.url == "http://www.site.com/index.html?a=b&c=" assert d.query == "a=b&c=" assert d.query_params == { "a": "b", "c": "" } d.query = "teststring".encode("rot13") assert d.url == "http://www.site.com/index.html?" + "teststring".encode("rot13") assert d.query == "teststring".encode("rot13") assert d.query_params == {} d.query = "test string".encode("base64")[:-1] assert d.url == "http://www.site.com/index.html?" + "test string".encode("base64")[:-1] assert d.query == "test string".encode("base64")[:-1] assert d.query_params == {} d.query = "test string".encode("base64") assert d.url == "http://www.site.com/index.html?" + "test string".encode("base64")[:-1] + "%0A" assert d.query == "test string".encode("base64")[:-1] + "%0A" assert d.query_params == {"test string".encode("base64")[:-2]: "\n"} d.query = "test=me" d.query_char = "/" assert d.url == "http://www.site.com/index.html/test=me" assert d.query_char == "/" assert d.query == "test=me" assert d.query_params == { "test": "me" } d.fragment = "frag" assert d.url == "http://www.site.com/index.html/test=me#frag" # Methods. d.hostname = "this.is.a.subdomain.of.example.co.uk" assert ".".join(d.split_hostname()) == d.host assert d.split_hostname() == ("this.is.a.subdomain.of", "example", "co.uk") d.path = "/folder.with.extensions/file.pdf.exe" assert d.get_all_extensions(directory_allowed = False, double_allowed = True) == [".pdf", ".exe"] assert d.get_all_extensions(directory_allowed = True, double_allowed = True) == [".with", ".extensions", ".pdf", ".exe"] assert d.get_all_extensions(directory_allowed = False, double_allowed = False) == [".exe"] assert d.get_all_extensions(directory_allowed = True, double_allowed = False) == [".extensions", ".exe"] assert d.get_all_extensions(directory_allowed = False ) == [".pdf", ".exe"] assert d.get_all_extensions( double_allowed = False) == [".extensions", ".exe"] assert d.get_all_extensions( ) == [".with", ".extensions", ".pdf", ".exe"] # Exceptions. last_url = d.url try: d.query_char = "*" assert False except ValueError: pass try: d.scheme = "fake://" assert False except ValueError: pass try: d.port = "fake" assert False except ValueError: pass try: d.port = -1 assert False except ValueError: pass try: d.port = 80000 assert False except ValueError: pass assert d.url == last_url # Warnings. with catch_warnings(record=True) as w: d.fragment = "#test" d.query = "?test=me" assert len(w) == 2
def http_analyzers(main_url, update_status_func, number_of_entries=4): """ Analyze HTTP headers for detect the web server. Return a list with most possible web servers. :param main_url: Base url to test. :type main_url: str :param update_status_func: function used to update the status of the process :type update_status_func: function :param number_of_entries: number of resutls tu return for most probable web servers detected. :type number_of_entries: int :return: Web server family, Web server version, Web server complete description, related web servers (as a dict('SERVER_RELATED' : set(RELATED_NAMES))), others web server with their probabilities as a dict(CONCRETE_WEB_SERVER, PROBABILITY) """ # Load wordlist directly related with a HTTP fields. # { HTTP_HEADER_FIELD : [wordlists] } m_wordlists_HTTP_fields = { "Accept-Ranges" : "accept-range", "Server" : "banner", "Cache-Control" : "cache-control", "Connection" : "connection", "Content-Type" : "content-type", "WWW-Authenticate" : "htaccess-realm", "Pragma" : "pragma", "X-Powered-By" : "x-powered-by" } m_actions = { 'GET' : { 'wordlist' : 'Wordlist_get' , 'weight' : 1 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': '/' }, 'LONG_GET' : { 'wordlist' : 'Wordlist_get_long' , 'weight' : 1 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': '/%s' % ('a' * 200) }, 'NOT_FOUND' : { 'wordlist' : 'Wordlist_get_notfound' , 'weight' : 2 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': '/404_NOFOUND__X02KAS' }, 'HEAD' : { 'wordlist' : 'Wordlist_head' , 'weight' : 3 , 'protocol' : 'HTTP/1.1', 'method' : 'HEAD' , 'payload': '/' }, 'OPTIONS' : { 'wordlist' : 'Wordlist_options' , 'weight' : 2 , 'protocol' : 'HTTP/1.1', 'method' : 'OPTIONS' , 'payload': '/' }, 'DELETE' : { 'wordlist' : 'Wordlist_delete' , 'weight' : 5 , 'protocol' : 'HTTP/1.1', 'method' : 'DELETE' , 'payload': '/' }, 'TEST' : { 'wordlist' : 'Wordlist_attack' , 'weight' : 5 , 'protocol' : 'HTTP/1.1', 'method' : 'TEST' , 'payload': '/' }, 'INVALID' : { 'wordlist' : 'Wordlist_wrong_method' , 'weight' : 5 , 'protocol' : 'HTTP/9.8', 'method' : 'GET' , 'payload': '/' }, 'ATTACK' : { 'wordlist' : 'Wordlist_wrong_version' , 'weight' : 2 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': "/etc/passwd?format=%%%%&xss=\x22><script>alert('xss');</script>&traversal=../../&sql='%20OR%201;"} } # Store results for others HTTP params m_d = ParsedURL(main_url) m_hostname = m_d.hostname m_port = m_d.port m_debug = False # Only for develop # Counter of banners. Used when others methods fails. m_banners_counter = Counter() # Score counter m_counters = HTTPAnalyzer(debug=m_debug) # Var used to update the status m_data_len = len(m_actions) i = 1 # element in process for l_action, v in m_actions.iteritems(): if m_debug: print "###########" l_method = v["method"] l_payload = v["payload"] l_proto = v["protocol"] l_wordlist = v["wordlist"] # Each type of probe hast different weight. # # Weights go from 0 - 5 # l_weight = v["weight"] # Make the URL l_url = urljoin(main_url, l_payload) # Make the raw request #l_raw_request = "%(method)s %(payload)s %(protocol)s\r\nHost: %(host)s:%(port)s\r\nConnection: Close\r\n\r\n" % ( l_raw_request = "%(method)s %(payload)s %(protocol)s\r\nHost: %(host)s\r\n\r\n" % ( { "method" : l_method, "payload" : l_payload, "protocol" : l_proto, "host" : m_hostname, "port" : m_port } ) if m_debug: print "REQUEST" print l_raw_request # Do the connection l_response = None try: m_raw_request = HTTP_Raw_Request(l_raw_request) discard_data(m_raw_request) l_response = HTTP.make_raw_request( host = m_hostname, port = m_port, raw_request = m_raw_request, callback = check_raw_response) if l_response: discard_data(l_response) except NetworkException,e: Logger.log_error_more_verbose("Server-Fingerprint plugin: No response for URL (%s) '%s'. Message: %s" % (l_method, l_url, str(e))) continue if not l_response: Logger.log_error_more_verbose("No response for URL '%s'." % l_url) continue if m_debug: print "RESPONSE" print l_response.raw_headers # Update the status update_status_func((float(i) * 100.0) / float(m_data_len)) Logger.log_more_verbose("Making '%s' test." % (l_wordlist)) i += 1 # Analyze for each wordlist # # Store the server banner try: m_banners_counter[l_response.headers["Server"]] += l_weight except KeyError: pass # # ===================== # HTTP directly related # ===================== # # for l_http_header_name, l_header_wordlist in m_wordlists_HTTP_fields.iteritems(): # Check if HTTP header field is in response if l_http_header_name not in l_response.headers: continue l_curr_header_value = l_response.headers[l_http_header_name] # Generate concrete wordlist name l_wordlist_path = Config.plugin_extra_config[l_wordlist][l_header_wordlist] # Load words for the wordlist l_wordlist_instance = WordListLoader.get_wordlist_as_dict(l_wordlist_path) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_curr_header_value) m_counters.inc(l_matches, l_action, l_weight, l_http_header_name, message="HTTP field: " + l_curr_header_value) # # ======================= # HTTP INdirectly related # ======================= # # # # Status code # =========== # l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["statuscode"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_response.status) m_counters.inc(l_matches, l_action, l_weight, "statuscode", message="Status code: " + l_response.status) # # Status text # =========== # l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["statustext"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_response.reason) m_counters.inc(l_matches, l_action, l_weight, "statustext", message="Status text: " + l_response.reason) # # Header space # ============ # # Count the number of spaces between HTTP field name and their value, for example: # -> Server: Apache 1 # The number of spaces are: 1 # # -> Server:Apache 1 # The number of spaces are: 0 # l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["header-space"]) # Looking for matches try: l_http_value = l_response.headers[0] # get the value of first HTTP field l_spaces_num = str(abs(len(l_http_value) - len(l_http_value.lstrip()))) l_matches = l_wordlist_instance.matches_by_value(l_spaces_num) m_counters.inc(l_matches, l_action, l_weight, "header-space", message="Header space: " + l_spaces_num) except IndexError: print "index error header space" pass # # Header capitalafterdash # ======================= # # Look for non capitalized first letter of field name, for example: # -> Content-type: .... # Instead of: # -> Content-Type: .... # l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["header-capitalafterdash"]) # Looking for matches l_valid_fields = [x for x in l_response.headers.iterkeys() if "-" in x] if l_valid_fields: l_h = l_valid_fields[0] l_value = l_h.split("-")[1] # Get the second value: Content-type => type l_dush = None if l_value[0].isupper(): # Check first letter is lower l_dush = 1 else: l_dush = 0 l_matches = l_wordlist_instance.matches_by_value(l_dush) m_counters.inc(l_matches, l_action, l_weight, "header-capitalizedafterdush", message="Capital after dash: %s" % str(l_dush)) # # Header order # ============ # l_header_order = ','.join(l_response.headers.iterkeys()) l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["header-order"]) l_matches = l_wordlist_instance.matches_by_value(l_header_order) m_counters.inc(l_matches, l_action, l_weight, "header-order", message="Header order: " + l_header_order) # # Protocol name # ============ # # For a response like: # -> HTTP/1.0 200 OK # .... # # Get the 'HTTP' value. # try: l_proto = l_response.protocol # Get the 'HTTP' text from response, if available if l_proto: l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["protocol-name"]) l_matches = l_wordlist_instance.matches_by_value(l_proto) m_counters.inc(l_matches, l_action, l_weight, "proto-name", message="Proto name: " + l_proto) except IndexError: print "index error protocol name" pass # # Protocol version # ================ # # For a response like: # -> HTTP/1.0 200 OK # .... # # Get the '1.0' value. # try: l_version = l_response.version # Get the '1.0' text from response, if available if l_version: l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["protocol-version"]) l_matches = l_wordlist_instance.matches_by_value(l_version) m_counters.inc(l_matches, l_action, l_weight, "proto-version", message="Proto version: " + l_version) except IndexError: print "index error protocol version" pass if "ETag" in l_response.headers: l_etag_header = l_response.headers["ETag"] # # ETag length # ================ # l_etag_len = len(l_etag_header) l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["etag-legth"]) l_matches = l_wordlist_instance.matches_by_value(l_etag_len) m_counters.inc(l_matches, l_action, l_weight, "etag-length", message="ETag length: " + str(l_etag_len)) # # ETag Quotes # ================ # l_etag_striped = l_etag_header.strip() if l_etag_striped.startswith("\"") or l_etag_striped.startswith("'"): l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["etag-quotes"]) l_matches = l_wordlist_instance.matches_by_value(l_etag_striped[0]) m_counters.inc(l_matches, l_action, l_weight, "etag-quotes", message="Etag quotes: " + l_etag_striped[0]) if "Vary" in l_response.headers: l_vary_header = l_response.headers["Vary"] # # Vary delimiter # ================ # # Checks if Vary header delimiter is something like this: # -> Vary: Accept-Encoding,User-Agent # Or this: # -> Vary: Accept-Encoding, User-Agent # l_var_delimiter = ", " if l_vary_header.find(", ") else "," l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["vary-delimiter"]) l_matches = l_wordlist_instance.matches_by_value(l_var_delimiter) m_counters.inc(l_matches, l_action, l_weight, "vary-delimiter", message="Vary delimiter: " + l_var_delimiter) # # Vary capitalizer # ================ # # Checks if Vary header delimiter is something like this: # -> Vary: Accept-Encoding,user-Agent # Or this: # -> Vary: accept-encoding,user-agent # l_vary_capitalizer = str(0 if l_vary_header == l_vary_header.lower() else 1) l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["vary-capitalize"]) l_matches = l_wordlist_instance.matches_by_value(l_vary_capitalizer) m_counters.inc(l_matches, l_action, l_weight, "vary-capitalize", message="Vary capitalizer: " + l_vary_capitalizer) # # Vary order # ================ # # Checks order between vary values: # -> Vary: Accept-Encoding,user-Agent # Or this: # -> Vary: User-Agent,Accept-Encoding # l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["vary-order"]) l_matches = l_wordlist_instance.matches_by_value(l_vary_header) m_counters.inc(l_matches, l_action, l_weight, "vary-order", message="Vary order: " + l_vary_header) # # ===================== # HTTP specific options # ===================== # # if l_action == "HEAD": # # HEAD Options # ============ # l_option = l_response.headers.get("Allow") if l_option: l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["options-public"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_option) m_counters.inc(l_matches, l_action, l_weight, "options-allow", message="HEAD option: " + l_option) if l_action == "OPTIONS" or l_action == "INVALID" or l_action == "DELETE": if "Allow" in l_response.headers: # # Options allow # ============= # l_option = l_response.headers.get("Allow") if l_option: l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["options-public"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_option) m_counters.inc(l_matches, l_action, l_weight, "options-allow", message="OPTIONS allow: " + l_action + " # " + l_option) # # Allow delimiter # =============== # l_option = l_response.headers.get("Allow") if l_option: l_var_delimiter = ", " if l_option.find(", ") else "," l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["options-delimited"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_var_delimiter) m_counters.inc(l_matches, l_action, l_weight, "options-delimiter", message="OPTION allow delimiter " + l_action + " # " + l_option) if "Public" in l_response.headers: # # Public response # =============== # l_option = l_response.headers.get("Public") if l_option: l_wordlist_instance = WordListLoader.get_wordlist_as_dict(Config.plugin_extra_config[l_wordlist]["options-public"]) # Looking for matches l_matches = l_wordlist_instance.matches_by_value(l_option) m_counters.inc(l_matches, l_action, l_weight, "options-public", message="Public response: " + l_action + " # " + l_option)
def http_simple_analyzer(main_url, update_status_func, number_of_entries=4): """Simple method to get fingerprint server info :param main_url: Base url to test. :type main_url: str :param update_status_func: function used to update the status of the process :type update_status_func: function :param number_of_entries: number of resutls tu return for most probable web servers detected. :type number_of_entries: int :return: a typle as format: Web server family, Web server version, Web server complete description, related web servers (as a dict('SERVER_RELATED' : set(RELATED_NAMES))), others web server with their probabilities as a dict(CONCRETE_WEB_SERVER, PROBABILITY) """ m_actions = { 'GET' : { 'wordlist' : 'Wordlist_get' , 'weight' : 1 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': '/' }, 'LONG_GET' : { 'wordlist' : 'Wordlist_get_long' , 'weight' : 1 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': '/%s' % ('a' * 200) }, 'NOT_FOUND' : { 'wordlist' : 'Wordlist_get_notfound' , 'weight' : 2 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': '/404_NOFOUND__X02KAS' }, 'HEAD' : { 'wordlist' : 'Wordlist_head' , 'weight' : 3 , 'protocol' : 'HTTP/1.1', 'method' : 'HEAD' , 'payload': '/' }, 'OPTIONS' : { 'wordlist' : 'Wordlist_options' , 'weight' : 2 , 'protocol' : 'HTTP/1.1', 'method' : 'OPTIONS' , 'payload': '/' }, 'DELETE' : { 'wordlist' : 'Wordlist_delete' , 'weight' : 5 , 'protocol' : 'HTTP/1.1', 'method' : 'DELETE' , 'payload': '/' }, 'TEST' : { 'wordlist' : 'Wordlist_attack' , 'weight' : 5 , 'protocol' : 'HTTP/1.1', 'method' : 'TEST' , 'payload': '/' }, 'INVALID' : { 'wordlist' : 'Wordlist_wrong_method' , 'weight' : 5 , 'protocol' : 'HTTP/9.8', 'method' : 'GET' , 'payload': '/' }, 'ATTACK' : { 'wordlist' : 'Wordlist_wrong_version' , 'weight' : 2 , 'protocol' : 'HTTP/1.1', 'method' : 'GET' , 'payload': "/etc/passwd?format=%%%%&xss=\x22><script>alert('xss');</script>&traversal=../../&sql='%20OR%201;"} } m_d = ParsedURL(main_url) m_hostname = m_d.hostname m_port = m_d.port m_scheme = m_d.scheme m_debug = False # Only for develop i = 0 m_counters = HTTPAnalyzer() m_data_len = len(m_actions) # Var used to update the status m_banners_counter = Counter() for l_action, v in m_actions.iteritems(): if m_debug: print "###########" l_method = v["method"] l_payload = v["payload"] l_proto = v["protocol"] #l_wordlist = v["wordlist"] # Each type of probe hast different weight. # # Weights go from 0 - 5 # l_weight = v["weight"] # Make the raw request l_raw_request = "%(method)s %(payload)s %(protocol)s\r\nHost: %(host)s\r\n\r\n" % ( { "method" : l_method, "payload" : l_payload, "protocol" : l_proto, "host" : m_hostname, "port" : m_port } ) if m_debug: print "REQUEST" print l_raw_request # Do the connection l_response = None try: m_raw_request = HTTP_Raw_Request(l_raw_request) discard_data(m_raw_request) l_response = HTTP.make_raw_request( host = m_hostname, port = m_port, proto = m_scheme, raw_request = m_raw_request) #callback = check_raw_response) if l_response: discard_data(l_response) except NetworkException,e: Logger.log_error_more_verbose("Server-Fingerprint plugin: No response for host '%s:%d' with method '%s'. Message: %s" % (m_hostname, m_port, l_method, str(e))) continue if not l_response: Logger.log_error_more_verbose("No response for host '%s:%d' with method '%s'." % (m_hostname, m_port, l_method)) continue if m_debug: print "RESPONSE" print l_response.raw_headers # Update the status update_status_func((float(i) * 100.0) / float(m_data_len)) Logger.log_more_verbose("Making '%s' test." % l_method) i += 1 # Analyze for each wordlist # # Store the server banner try: m_banners_counter[l_response.headers["Server"]] += l_weight except KeyError: pass l_server_name = None try: l_server_name = l_response.headers["Server"] except KeyError: continue m_counters.simple_inc(l_server_name, l_method, l_weight)