Immer wenn ich Intranet-Penetration durchführe und einer großen Anzahl von Hosts und Diensten gegenüberstehe, verwende ich immer automatisierte Methoden, um Informationen aus Nmap-Scan-Ergebnissen zu extrahieren. Dies erleichtert die automatisierte Erkennung verschiedener Arten von Diensten, wie z. B. Path Blasting bei Webdiensten, das Testen von Schlüsseln oder Protokollen, die von SSL/TLS-Diensten verwendet werden, und andere gezielte Tests.
Ich verwende bei Penetrationstests auch häufig IPthon oder *nix-Shell, und auf diese kann über Python zugegriffen werden, unabhängig davon, ob sie direkt in Skripten verwendet werden, in einer REPL-Umgebung verwendet werden oder sehr nützlich sind, um den Code zu schreiben auf die Festplatte kopieren und dann über Shell-Befehle darauf zugreifen.
Konfiguration
Der erste Schritt beim Parsen der NMAP-Scan-Ergebnisse besteht darin, dass Sie einen NMAP-Scan durchführen. Ich werde hier nicht zu sehr ins Detail gehen, aber wenn Sie den Code aus diesem Artikel direkt verwenden möchten, müssen Sie die Scanstruktur in einer XML-Datei (-oX oder -oA) speichern und eine Diensterkennung darauf durchführen Öffnen Sie den Port (-sV) und führen Sie zugehörige Skripte aus (-sC).
Bei den Befehlen in diesem Artikel wird davon ausgegangen, dass Sie in einer Python REPL-Umgebung wie IPython ausführen und das libnmap-Modul installiert haben (kann mit easy_install oder pip installiert werden).
Bevor Sie beginnen, müssen Sie zunächst das NmapParser-Modul importieren und Ihre XML-Scan-Ergebnisdatei einlesen (der Name im Beispiel lautet „up_hosts_all_ports_fullscan.xml“ und befindet sich im aktuellen Arbeitsverzeichnis)
from libnmap.parser import NmapParser nmap_report = NmapParser.parse_fromfile('up_hosts_all_ports_fullscan.xml')
Der Rest dieses Artikels enthält eine Reihe von Methoden, um verschiedene nützliche Informationen mit einer Codezeile zu extrahieren. Bei allen Beispielen wird davon ausgegangen, dass die Nmap-Scanergebnisse wie oben gezeigt in einer Datei gespeichert werden. Im Folgenden finden Sie einige grundlegende Beispielcodes. Wenn Sie diese direkt in IPython ausführen möchten, führen Sie bitte zuerst den obigen Code aus, damit er zur Anzeige direkt auf der Konsole ausgegeben wird. Normalerweise mache ich das zuerst, damit ich sicherstellen kann, dass die Ausgabedaten den Erwartungen entsprechen.
Sie können dann einen Variablennamen auswählen und mit „=" der Variablen Daten zuweisen, sodass Sie sie direkt im nachfolgenden Code aufrufen oder zur Verwendung durch Shell-Befehle auf die Festplatte schreiben können. Wenn Sie etwas mehrmals verwenden möchten, können Sie einige Codeausschnitte in ein Python-Skript einfügen oder eine komplexere Logik hinzufügen. Dies kann jedoch die Handhabung der REPL-Umgebung erschweren. Ich werde erklären, wie dies schnell ausgeführt werden kann im letzten Abschnitt.
Portinformationen
Hosts mit der angegebenen Portnummer geöffnet
Zeigt alle Hosts mit der angegebenen Portnummer geöffnet an. Erzeugen Sie eine Liste mit Hostadressen (Zeichenfolge). Im Folgenden wird Port 443 als Beispiel verwendet. Sie können ihn auf den von Ihnen benötigten Wert ändern.
[ a.address for a in nmap_report.hosts if (a.get_open_ports()) and 443 in [b[0] for b in a.get_open_ports()] ]
Anzahl offener Ports
Zeigt die Anzahl offener Ports für eine Reihe von Hosts an. Erzeugen Sie eine Liste mit der Anzahl der Ports (int) und sortieren Sie sie.
sorted(set([ b[0] for a in nmap_report.hosts for b in a.get_open_ports()]), key=int)
Dienste entsprechend den offenen Ports des Hosts, gruppiert nach Portnummer
Zeigt die von allen Hosts geöffneten Portnummern an, gruppiert und sortiert nach Portnummer. Generieren Sie eine Liste mit mehreren Listen (d. h. jedes Element der Liste ist auch eine Liste), in der das erste Element jeder Mitgliedsliste eine Portnummer (int) und das zweite Element eine Host-IP-Adresse ist, die geöffnet ist entsprechende Port-(String-)Liste.
[ [a, [ b.address for b in nmap_report.hosts for c in b.get_open_ports() if a==c[0] ] ] for a in sorted(set([ b[0] for a in nmap_report.hosts for b in a.get_open_ports()]),key=int) ] SSL/TLS 和 HTTP/HTTPS
Hosts und Ports, die SSL verwenden
Zeigt alle Hosts und Ports an, die SSL verwenden. Dazu wird geprüft, ob ein Dienst einen „SSL“-Kanal verwendet oder ob das zugehörige Skript in den Ergebnissen ein PEM-Zertifikat erkennt. Generieren Sie eine Liste mit einer Reihe von Listen. Jede Mitgliedsliste enthält die Hostadresse (Zeichenfolge) und die Portnummer (int).
[ [a.address, b.port] for a in nmap_report.hosts for b in a.services if b.tunnel=='ssl' or "'pem'" in str(b.scripts_results) ]
Der folgende Inhalt enthält die gleichen Informationen wie oben, aber anstelle einer Liste von Listen wird die Join-Funktion verwendet, um eine Liste mit „Host:Portnummer“ (Zeichenfolge) zu erstellen.
[ ':'.join([a.address, str(b.port)]) for a in nmap_report.hosts for b in a.services if b.tunnel=='ssl' or "'pem'" in str(b.scripts_results) ]
Enthält den Host und Port des Webdienstes
Zeigt alle Webdienste und ihre entsprechenden Portnummern und Protokolle (http oder https) an. Dadurch wird eine Liste mit mehreren Listen erstellt, wobei jede Mitgliedsliste ein Protokoll (String), eine Adresse (String) und eine Portnummer (int) enthält. Hier gibt es jedoch einige Probleme. Wenn nmap Websites meldet, die https verwenden, wird manchmal angezeigt, dass der Dienst „https“ ist, und manchmal wird er über den „ssl“-Kanal als „http“ angezeigt, daher habe ich das Datenformat angepasst für Einheitlichkeit.
[ [(b.service + b.tunnel).replace('sl',''), a.address, b.port] for a in nmap_report.hosts for b in a.services if b.open() and b.service.startswith('http') ]
Die gleichen Informationen sind immer noch hier, aber die URL (Zeichenfolge) wird zur ursprünglichen Liste von Protokoll, Host und Portnummer hinzugefügt.
[ (b.service + b.tunnel).replace('sl','') + '://' + a.address + ':' + str(b.port) + '/' for a in nmap_report.hosts for b in a.services if b.open() and b.service.startswith('http') ]
Weitere Dienstinformationen
Unbekannte Dienste
Zeigt alle Dienste an, die nmap nicht erkennen kann. Erzeugt eine Liste mit mehreren Listen, wobei jede Mitgliedsliste die Adresse (Zeichenfolge), die Portnummer (int) und den von nmap gescannten Port-Fingerabdruck (Zeichenfolge) enthält. Diese Informationen werden hauptsächlich generiert, um die spätere manuelle Überprüfung dieser spezifischen Dienste zu erleichtern, und nehmen nicht an einem automatisierten Prozess teil.
[ [ a.address, b.port, b.servicefp ] for a in nmap_report.hosts for b in a.services if (b.service =='unknown' or b.servicefp) and b.port in [c[0] for c in a.get_open_ports()] ]
Nmap-identifizierte Software
Zeigt alle durch Nmap-Scans identifizierte Software an. Erstellen Sie eine alphabetische Produktliste.
sorted(set([ b.banner for a in nmap_report.hosts for b in a.services if 'product' in b.banner]))
软件对应的主机和端口号,按产品分组
显示扫描出软件对应的主机和端口,按产品分组。生成一个包含多个列表的列表,其中每个成员列表的第一个元素为软件的名称(string),随后是另一个列表包含地址(string)和端口号(int)。
[ [ a, [ [b.address, c.port] for b in nmap_report.hosts for c in b.services if c.banner==a] ] for a in sorted(set([ b.banner for a in nmap_report.hosts for b in a.services if 'product' in b.banner])) ]
同上相同的信息,只是输出略有不同。同样还是生成一个包含多个列表的列表,成员列表的第一个元素还是软件的名称(string),但第二个是一个包含 “主机:端口号” 的列表。
[ [ a, [ ':'.join([b.address, str(c.port)]) for b in nmap_report.hosts for c in b.services if c.banner==a] ] for a in sorted(set([ b.banner for a in nmap_report.hosts for b in a.services if 'product' in b.banner])) ]
搜索指定关键词相关的主机和端口
显示所有与给定关键词相关联的主机和端口,从 nmap 扫描结果的原始文本中查找包含产品名称、服务名称等等。下面以 “Oracle” 为例。生成一个包含多个列表的列表,其中每个成员列表包含主机地址(string)和端口号(int)。
[ [a.address, b.port] for a in nmap_report.hosts for b in a.services if b.open() and 'Oracle' in str(b.get_dict()) + str(b.scripts_results) ]
同上一样的方法,只是将存储的信息修改后一律使用小写进行搜索(下面示例为小写的 “oracle”),输出格式还是跟上面一样。
[ [a.address, b.port] for a in nmap_report.hosts for b in a.services if b.open() and 'oracle' in (str(b.get_dict()) + str(b.scripts_results)).lower() ]
其他的事情
相同的证书名称
显示找到的 SSL 证书和使用 nmap 脚本解析后得到证书名称相同的部分。这样在当你从一个 IP 地址开始扫描且反向 DNS 失效的时候,可以帮助确定系统的主机名。生成一个包含多个列表的列表,其中每个成员列表包含 IP 地址(string)和提取出的主机名(string)。
[ [a.address, c['elements']['subject']['commonName'] ] for a in nmap_report.hosts for b in a.services for c in b.scripts_results if c.has_key('elements') and c['elements'].has_key('subject') ]
处理以上结果的方法
正向前面所说,上述的例子,当你直接粘贴进 IPython REPL 时只是将输出打印在屏幕上。这的确不错,因为这样你可以随时查看到自己感兴趣的信息,但你可能还会想做更多的事情。之所以去生成上述信息,一大好处就在于你可以根据结果轻松执行一些自动化的操作。
如果你已经很熟悉 Python,应当可以很容易完成这些工作,那么你可以跳过这一节。但如果你不熟悉,那么本节会讲述一些很基本的知识,告诉你如何使用上述的代码段。
保存到磁盘
如果你想将上述代码段的输出结果保存到磁盘上的文本文件中,你需要将输出的列表转换为适当的字符串格式(具体取决于你的需求),然后在将这个字符串写入文件。在 Python 中,你可以使用 join 函数来整合这些列表并将其写入文件,这里只是一个示例。
我们想要从生成的列表中提取出支持 SSL 的主机和端口,并将它们保存到一个新的文件中,这样可以在 bash 中使用循环来完成并使用命令行工具来进行测试。
我通常会在 IPython 中使用一行代码来完成这些,虽然一行代码会比较方便,但这里为了方便阅读和理解,我会将代码拆分出来说。
让我们来解析之前生成了一个包含 “主机:端口” 的列表,请注意我们使用了 str 函数将端口号从整数类型装换为了字符类型,这样使得它也能够使用 join 函数与其他字符串拼接在一起。
[ ':'.join([a.address, str(b.port)]) for a in nmap_report.hosts for b in a.services if b.tunnel=='ssl' or "'pem'" in str(b.scripts_results) ]
让我们来给上面这段代码的结果分配名为 “ssl_services” 变量,以方便后续的调用。
ssl_services = [ ':'.join([a.address, str(b.port)]) for a in nmap_report.hosts for b in a.services if b.tunnel=='ssl' or "'pem'" in str(b.scripts_results) ]
现在,让我们来使用 join 函数将列表的每一个元素拼接起来并使用 (‘\n') 进行换行,然后给它分配一个名为 “ssl_services_text” 的变量。
ssl_services_text = '\n'.join(ssl_services)
随后,我们就可以在当前工作目录下创建一个名为 “ssl_services_file.txt” 的新文建,并将 “ssl_services_text” 变量的内容写入其中。
open('ssl_services_file.txt','w').write(ssl_services_text)
就这么简单,后续你可以根据自己的需要来使用文件内容了。
使用其他 Python 代码
也许你还会想用其他的 Python 代码来完成上述工作?同样很简单,下面就是另一个示例,这里我们遍历每一个 nmap 识别出的 web 服务及其网页的请求结果。
下面会生成一个包含 URLs 的列表,我们分配一个名为 “urls” 的变量给它。
urls = [ (b.service + b.tunnel).replace('sl','') + '://' + a.address + ':' + str(b.port) + '/' for a in nmap_report.hosts for b in a.services if b.open() and b.service.startswith('http') ]
下一步,我们先进行一些准备工作,导入 requests 模块,然后设置一个简单的 getAndSave 函数进行 web 请求并将返回结果保存到磁盘上,文件名按 url 自动生成。你可能会注意到下面代码中,在 get 请求中使用了 “verify=False” 选项,这会在发送请求时忽略证书验证的错误,这个选项经常在测试内部机器时使用,因为内部机器基本不会有可信的证书颁发机构颁发的 SSL 证书。
import requests def getAndSave(url): r = requests.get(url, verify=False) open('_'.join(url.split('/')[2:]).replace(':',''),'wb').write(r.text.encode('utf8'))
现在,让我们增加一些代码来遍历每一个 url,请求每个站点的 robots.txt 文件,并将其保存到本地以供后续使用。
for a in urls: getAndSave(a + 'robots.txt')
这样就会将每一个站点的 robots.txt 文件爬取到当前工作目录下。这只是一个很简单的例子。
总结
希望你在阅读完本文后,可以自己灵活的使用 Python 解析 nmap 扫描结果。
更多巧用python和libnmapd,提取Nmap扫描结果相关文章请关注PHP中文网!