Document ID: /fineweb-2-swissfilter-quality_10-filterrobots/filtered/03170.jsonl.gz/1398

Cyber Threat Intelligence
Marc Ruef
Im ersten Teil dieser Serie haben wir das Automatisieren von nmap mittels NSE-Skripten vorgestellt. Dabei haben wir kurz die Idee, das Konzept und die Möglichkeiten umrissen. Zusätzlich haben wir die grundlegende Struktur eines mit NSE implementierten Tests illustriert.
Im zweiten Teil wollen wir nun ein simples Skript schreiben. Dieses stützt sich in erster Linie auf den regulären Resultaten eines Portscans mit nmap ab. Wir bezeichnen ein solches Plugin als derivativ, da es eine Ableitung von grundlegenden Resultaten durchführt und damit keine eigenen Zugriffe über das Netzwerk umsetzen wird.
NSE-Skripte werden bei einer nmap-Installation standardmässig im Verzeichnis
scripts abgelegt. Dort finden sich sodann auch die Standardskripte der jeweiligen Distribution. Zum Beispiel werden einige Skripte für die Auswertung von FTP-Server mitgeliefert:
Ein NSE-Skript benutzt in der Regel die Dateierweiterung
nse. Neue Skripte können in dieser Art ebenfalls ins
script-Verzeichnis abgelegt werden. Oder sie können in einem Unterverzeichnis oder an anderer Stelle gespeichert werden. Ein Miteinbeziehen bei einem Script-Scan erfordert in diesem Fall die zusätzliche Angabe des jeweiligen Standorts.
Der Aufbau eines NSE-Skripts gestaltet sich immer gleich: Im Kopf einer NSE-Datei finden sich einige deskriptive Felder, die grundlegende Informationen zum Plugin bereitstellen: Im Feld
description wird beispielsweise eine Beschreibung des Tests festgehalten, in
categories findet eine Zuweisung der Kategorien statt (siehe Teil 1), in
dependencies werden Abhängigkeiten von anderen Skripten definiert, in
author wird der Autor spezifiziert und in
license die Lizenzbestimmungen festgehalten. Eine minimale Angabe gestaltet sich beispielsweise wie folgt:
description = [[ Dieses minimale Skript identifiziert Webdienste ]]
author = “Marc Ruef” license = “(c) 2010 by scip AG” categories = {“default”, “safe”}
In der
rule-Sektion wird nun definiert, unter welchen Bedingungen das Skript ausgeführt wird. Hierbei kann entweder eine
portrule (für Tests von Ports) oder eine
hostrule (für Tests von Hosts) verwendet werden. Eine Ableitung von einem Portscan fokussiert sich in erster Linie auf die
portrule (das Prinzip der
hostrule werden wir später besprechen). Innerhalb von ihr wird vordefiniert, bei welchem Zustand eines Zielports eine Weiterverarbeitung des Skripts initiiert und damit schlussendlich die Schwachstelle gemeldet werden soll.
Eine
portrule kann unterschiedlich implementiert werden. Die einfachste und von vielen bevorzugte Methode besteht unter Zuhilfenahme der Library shortport und der entsprechenden Methoden. Nachfolgend wird zuerst die Library mittels
require inkludiert und danach die Methode
port_or_service() auf das Objekt
shortport angewendet. Die genannte Methode erwartet drei verschiedene Argumente, wobei diese jeweils als Table (in anderen Programmiersprachen wird dieser Datentyp Array genannt) übergeben werden müssen. Das erste Argument definiert die Portnummern, das zweite die Protokollnamen und das dritte das Transportprotokoll. Es scheint offensichtlich, dass in diesem Beispiel eine
portrule für Web-Dienste umgesetzt wird.
require “shortport”
portrule = shortport.port_or_service({80, 443}, {“http”, “https”}, {“tcp”})
In einem weiteren Schritt kann nun in
action die Weiterverarbeitung – in der NSE-Dokumentation als Mechanism bezeichnet – spezifiziert werden. Diese Funktion, sie kann als Main-Funktion verstanden werden, wird dann ausgeführt, wenn
portrule gleich
true ist. Also dann, wenn ein Webserver auf dem Zielport vermutet (anhand der Portnummern) oder identifiziert (anhand der Protokollnamen) wird. In diesem Fall geben wir nur ganz allgemein mittels
return eine Zeichenkette zurück, die explizit darüber informiert, dass ein Webserver gefunden wurde und mit
port.number geben wir die entsprechende Portnummer an.
action = function(host, port) return "Webserver gefunden auf Port " .. port.number end
Es wird nun mit dem Schalter
--script="scripts\labs\http-detection_simple.nse" dieses einzelne Skript ausgeführt. Und wie zu sehen ist, wird als Detail des offenen Ports zusätzlich die explizite Ausgabe ausgewiesen:
C:\Dokumente und Einstellungen\maru>nmap -sS -sV —script=“scripts\labs\http-detection_simple.nse” www.scip.ch -p 80
Starting Nmap 5.21 ( http://nmap.org ) at 2010-03-24 12:37 Westeuropõische Normalzeit NSE: Script Scanning completed. Nmap scan report for www.scip.ch (<ip-pii>) Host is up (0.0020s latency). rDNS record for <ip-pii>: www.scip.ch PORT STATE SERVICE VERSION 80/tcp open http Apache httpd |_http-detection_simple: Webserver gefunden auf Port 80
Service detection performed. Please report any incorrect results at http://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 7.59 seconds
Dies war alles relativ simpel und erfüllt zugegebenermassen zur Standardausgabe von nmap keinen zusätzlichen Zweck. Sodann soll die Funktion erweitert werden, um ein Mehr an Informationen bereitstellen zu können. So wird mit
common_http_ports eine Table angelegt, in der die Standardports für Webserver abgelegt sind. Indem nun diese Table mittels einer
for-Schleife durchlaufen wird, kann nun explizit ermittelt werden, ob der angenbotene Webserver auf einem Standardport horcht oder nicht. Der Zustand dessen wird als String
(Yes|No) in der lokalen Variable
common_port_found abgelegt. Zum Schluss wird in der lokalen Variable
output das Resultat generiert und zurückgeliefert.
action = function(host, port) local common_http_ports = {80, 443} local common_port_found = “Nein” for i=1, #common_http_ports, 1 do if port.number == common_http_ports[i] then common_port_found = “Ja” break end end local output = “Webserver gefunden:\n” output = output .. “Port:\t\t” .. port.number .. “\n” output = output .. “Standard:\t” .. common_port_found .. “\n” return output end
Es lassen sich in einem weiteren Schritt zusätzliche eigene Funktionen definieren. Diese sind wie bei anderen funktionalen Programmiersprachen über das Schlüsselwort
function definiert, können aus anderen Subroutinen aufgerufen und ihre Rückgabewerte weiterverarbeitet werden. Dedizierte Prozeduren lassen sich ebenfalls in externe Dateien ablegen. Möchte man diese mittels
require einbinden, sieht nmap das Verzeichnis
nselib vor. In diesem finden sich verschiedene Standard-Bibliotheken, die den Umgang mit nmap-Scans erleichtern.
Wird ein Skript-Ausgeführt, das einen Syntaxfehler aufweist, wird die Ausführung dessen abgebrochen. Die durch den Lua-Interpreter ausgegebene Fehlermeldung wird ersichtlich, wenn nmap mit dem Debugging-Schalter
-d ausgeführt wurde. In der Standardbilbiothek
nmap wird mit den Methoden
verbosity() und
debugging() die Möglichkeit geboten, die Aktivierung von Verbose- und Debugging-Parametern zu erkennen und auf diese einzugehen. Vor allem, aber nicht nur, lohnt sich bei der Entwicklung von NSE-Skripten auf diese Möglichkeit sowie auf
print_debug() zurückzugreifen, um je nachdem interne Details der Verarbeitung anzeigen zu lassen.
Im dritten Teil werden wir die Entwicklung dieses NSE-Skripts vorantreiben. Dabei werden wir die Möglichkeiten der Version Detection von nmap ausschöpfen, um anhand der ermittelten Fingerprints komplexere Ableitungen umzusetzen. Mitunter werden wir Pattern-Matching und reguläre Ausdrücke miteinbeziehen, um verwundbare Versionen erkennen und dokumentieren zu können.
Unsere Spezialisten kontaktieren Sie gern!
Marc Ruef
Marc Ruef
Marc Ruef
Unsere Spezialisten kontaktieren Sie gern!
Weitere Artikel im Archiv