def get_fingerprinting_wordlist(wordlist): """ Load the wordlist of fingerprints and prepare the info in a dict. It using as a keys the name of the server family and, as value, an iterable with the keywords related with this web server. :return: The results of load of webservers keywords info and related webservers. :rtype: tuple(WEBSERVER_KEYWORDS, RELATED_SERVES) <=> (dict(SERVERNAME: set(str(KEYWORDS))), dict(SERVER_NAME, set(str(RELATED_SERVERS))) """ # Load the wordlist m_w = WordListLoader.get_advanced_wordlist_as_dict(wordlist, separator=";", smart_load=True) # Load references. # # References in the wordlist are specified by # prefix. # already_parsed = set() related = defaultdict(set) m_webservers_keys = extend_items(m_w, already_parsed, related) return (m_webservers_keys, related)
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 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 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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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_advanced_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)