Konkrete Kritik an CVSS4
Marc Ruef
In den letzten Teilen dieser Schriftenreihe haben wir die erweiterte Funktionalitäten von NSE/Lua kennengelernt. Besonders die beiden durch nmap mitgelieferte Bibliotheken zum Umgang mit Sockets und HTTP-Kommunikationen sind in den Mittelpunkt des Interesses gerückt. Durch sie lassen sich eigene Netzwerkzugriffe durchsetzen und damit die volle Funktionalität eines Vulnerability Scanners erreichen.
In diesem Teil wollen wir unser Verständnis für die Bibliothek http
– die Grundlagen derer gelten als Voraussetzung für das Verständnis dieses Teils – vertiefen. Wir werden eine spezielle Form der Version Detection implementieren. Und zwar werden wir den HTTP-Header der Rückantworten eines Webservers analysieren, um anhand dieses HTTP-Fingerprinting die gegebene Implementierung ableiten zu können. Damit wird die Grundfunktionalität geschaffen, die wir in der angekündigten NSE-Portierung von httprecon erreichen wollen.
Mit httprecon wurde 2007 ein Projekt ins Leben gerufen, das sich um die Verbesserung von HTTP-Fingerprinting bemüht. Eine erster Implementierung einer automatisierten Software wurde für Windows umgesetzt. Hierbei kommen eine Reihe von Anfragen zum Tragen, bei denen die HTTP-Header der Rückantworten ausgewertet werden. Je höher die Fingerprint-Matches ausfallen, desto eher kann eine entsprechende Implementierung identifiziert werden. Nachfolgend ein typischer Scan mit httprecon für Windows:
Die gesamte Funktionalität von httprecon in diesem Artikel nachzubilden, würde den Rahmen dessen bei weitem sprengen. Stattdessen soll einfach das Grundprinzip einer entsprechenden Implementierung vorgetragen werden.
Die nmap NSE-Portierung von httprecon kann auf der offiziellen Projektseite heruntergeladen werden.
Das Grundprinzip von httprecon basiert darauf, die einzelnen Eigenschaften des HTTP-Headers zu extrahieren und diese Fingerprints mit den in der Datenbank gespeicherten zu vergleichen. Die Datenbank wird durch kommagetrennte Dateien (CSV) bereitgestellt. Der Statustext für nicht existente Seiten (404 Not Found) wird beispielsweise wie folgt vorgelegt:
Apache;Not Found Compaq HTTP Server;Ok Microsoft IIS;Object Not Found Netscape Enterprise Server;Not found Oracle Application Server;Not Found
Hier sind zwei grundsätzliche Unterschiede in den jeweiligen Zeichenketten zu erkennen:
Not Found
Ok
Object Not Found
Not Found
unterschiedlich aus:Found
grossfound
kleinWird nun der HTTP-Header eines Webservers so dissektiert, dass der Statuscode extrahiert werden kann, lässt er sich anhand dieser Merkmale vergleichen. Je nach zu beobachtenden Charakteristika kann damit der Webserver, in einigen Fällen gar bis auf die Version genau, ausgemacht werden. Die Kombination des Vergleichs unterschiedlicher Zugriffe und ihrer Merkmale erhöht die Genauigkeit dieser Determinierung.
Zu diesem Zweck muss als erstes eien Reaktion des Webservers provoziert werden. Dies kann durch unterschiedliche Mechanismen (z.B. verschiedene HTTP-Methoden, Ressourcen, Protokoll-Versionen und Header-Informationen) erfolgen. Nachfolgend wird dies mit der in der Funktion send_http_request()
getan. Diese lässt neben der Definition des Zielsystems und -ports ebenfalls die zu nutzende HTTP-Methode sowie Ressource zu. Hierbei wird eine übliche GET-Anfrage für das Standarddokument durchgesetzt. Die Rückantwort wird dann weiterführend durch die Funktion identify_fingerprint()
, welche sich an der Fingerprint-Datenbank im Verzeichnis scripts/httprecon/get_existing/
orientiert, analysiert.
response = send_http_request(host, port, "GET", "/") if type(response) == "table" then identify_fingerprint(response, "scripts/httprecon/get_existing/") end
Wie zu sehen ist, wird sodann die Rückantwort unterschiedlichen Tests unterzogen. Hauptsächlich wird dabei der Wert einer Header-Zeile verglichen. Ein typisches Beispiel ist der Banner, welcher in der Server-Zeile bereitgestellt wird. Zusätzlich werden aber ebenfalls Schreibweisen (Gross-/Kleinschreibung), Interpunktion (Komma, Komma+Abstand) sowie Reihenfolgen (z.B. Header-Order und Vary-Order) berücksichtigt.
function identify_fingerprint(res, db) find_match_in_db(db.."accept-range.fdb", get_header_value(get_header_line(res.rawheader, "Accept-Ranges", false)), 1) find_match_in_db(db.."banner.fdb", get_header_value(get_header_line(res.rawheader, "Server", false)), 3) find_match_in_db(db.."cache-control.fdb", get_header_value(get_header_line(res.rawheader, "Cache-Control", false)), 2) find_match_in_db(db.."connection.fdb", get_header_value(get_header_line(res.rawheader, "Connection", false)), 2) find_match_in_db(db.."content-type.fdb", get_header_value(get_header_line(res.rawheader, "Content-Type", false)), 1) find_match_in_db(db.."etag-legth.fdb", string.format("%s", string.len(get_header_value(get_header_line(res.rawheader, "ETag", false)))), 3) find_match_in_db(db.."etag-quotes.fdb", get_quotes(get_header_value(get_header_line(res.rawheader, "ETag", false))), 2) find_match_in_db(db.."header-capitalafterdash.fdb", string.format("%s", capital_after_dash(analyze_header_order(res.rawheader))), 2) find_match_in_db(db.."header-order.fdb", analyze_header_order(res.rawheader), 5) find_match_in_db(db.."header-space.fdb", string.format("%s", header_space(res.rawheader)), 2) find_match_in_db(db.."htaccess-realm.fdb", get_realm(get_header_line(res.rawheader, "WWW-Authenticate", false)), 3) find_match_in_db(db.."pragma.fdb", get_header_value(get_header_line(res.rawheader, "Pragma", false)), 2) find_match_in_db(db.."protocol-name.fdb", get_protocol_name(res['status-line']), 1) find_match_in_db(db.."protocol-version.fdb", get_protocol_version(res['status-line']), 2) find_match_in_db(db.."statuscode.fdb", get_status_code(res.status), 4) find_match_in_db(db.."statustext.fdb", get_status_text(res['status-line']), 4) find_match_in_db(db.."vary-capitalize.fdb", string.format("%s", has_capital(get_header_line(res.rawheader, "Vary", false))), 2) find_match_in_db(db.."vary-delimiter.fdb", vary_delimiter(get_header_line(res.rawheader, "Vary", false)), 2) find_match_in_db(db.."vary-order.fdb", get_header_value(get_header_line(res.rawheader, "Vary", false)), 3) find_match_in_db(db.."x-powered-by.fdb", get_header_value(get_header_line(res.rawheader, "X-Powered-By", false)), 3) end
Sodann ist die Funktion find_match_in_db()
für die Identifikation der Matches in der Datenbank zuständig. Diese erwartet den Inhalt einer Fingerprint-Datei (z.B. statustext.fdb
) und den zu findenen Fingerabdruck (z.B. Object Not Found
):
function find_match_in_db(databasefile, fingerprint) local database = read_from_file(databasefile) — Fingerprint database local delimiterpos — Position of delimiter local name — Name of implementation local pattern — Pattern of fingerprint local arraypos — Position in array for i=1, #database, 1 do delimiterpos = string.find(database[i], “;”) if type(delimiterpos) == “number” then name = string.sub(database[i], 1, delimiterpos – 1) pattern = string.sub(database[i], delimiterpos + 1) if type(pattern) "string" and pattern ~= "" and type(name) “string” and name ~= “” then if fingerprint == pattern then arraypos = in_array(result, name) if type(arraypos) == “number” then result[arraypos] = { matchname = name, count = result[arraypos].count + 1 } else result1 = { matchname = name, count = 1 } end end end end end return true end
Ein Aufruf von function find_match_in_db("statustext.fdb", "Object Not Found")
schreibt in die öffentliche Table result
sämtliche Systeme, die für diesen Fingerabdruck bekannt sind. Wie wir zuvor gesehen haben, sollte dies Microsoft IIS betreffen.
Mit dem Zugriff auf result[1].name
kann sodann der erste Treffer dieses Zugriffs gefunden werden. Wird die Table zuvor entsprechend sortiert (vorzugsweise durch .count
), kann damit der beste Treffer mit den meisten Übereinstimmungen ausgemacht werden. Durch das iterieren dieser Table liessen sich dann die 10 besten Hits ausmachen. Die Ausgabe des Skripts für einen erfolgreichen Test eines Webservers sähe entsprechend so aus (hier wurde ein Microsoft IIS 6.0 identifiziert):
PORT STATE SERVICE REASON 80/tcp open http syn-ack | httprecon: Implementation Hits | 1 Microsoft IIS 6.0 38 | 2 Apache 2.0.46 35 | 3 Apache 2.0.54 34 | 4 Apache 2.2.2 34 | 5 Apache 2.2.8 33 | 6 AOLserver 3.4.2 34 | 7 Apache 1.3.33 33 | 8 Apache 1.3.34 33 | 9 Apache 2.2.3 33 |_10 Zeus 4.3 33
Die offizielle nmap NSE-Portierung von httprecon enthält einige weitere Funktionalitäten. Zum Beispiel erhalten die einzelnen Hits individuelle Werte (Score). Durch diese Gewichtung können genauere und solidere Resultate generiert werden.
Im letzten Teil dieser Artikelserie werden wir abschliessend das Umsetzen von hostbasierten Skripten anschauen. Dabei werden wir mitunter die Möglichkeit schaffen, iterativ die bestehenden Resultate von nmap zu prüfen und zu modifizieren.
Unsere Spezialisten kontaktieren Sie gern!
Marc Ruef
Marc Ruef
Marc Ruef
Marc Ruef
Unsere Spezialisten kontaktieren Sie gern!