首頁  >  文章  >  後端開發  >  巧用python和libnmapd,擷取Nmap掃描結果

巧用python和libnmapd,擷取Nmap掃描結果

高洛峰
高洛峰原創
2017-02-25 10:29:461673瀏覽

每當我進行內網滲透面對大量主機和服務時,我總是習慣使用自動化的方式從 nmap 掃描結果中提取資訊。這樣有利於自動化檢測不同類型的服務,例如對 web 服務進行路徑爆破,測試 SSL/TLS 服務使用的金鑰或協議,以及其他有針對性的測試。

我在滲透測試中也會經常使用到IPthon 或*nix shell,而這些又都能夠透過Python 來訪問,無論是直接在腳本中使用、在REPL 環境下使用,還是將程式碼寫入磁碟上然後透過shell 命令存取都是非常好用的。


設定
解析 nmap 掃描結果的第一步是你要進行一次 nmap 掃描。我不打算在這裡關注過多的細節部分,但是你想要直接使用本文的程式碼,你需要將掃描結構保存到一個xml 檔案中(-oX 或-oA)並且在開放埠上執行了服務偵測(-sV)和運行相關腳本(-sC)。
本文的指令假設你在一個 Python REPL 環境如 IPython 並且安裝 libnmap 模組(可以使用easy_install 或 pip 安裝)的環境下執行。
開始前,你需要設定對應的環境,先導入NmapParser 模組並讀入你的xml 掃描結果檔(實例中名為」up_hosts_all_ports_fullscan.xml」位於目前工作目錄下)

from libnmap.parser import NmapParser
nmap_report = NmapParser.parse_fromfile('up_hosts_all_ports_fullscan.xml')

本文的剩餘部分會包含一系列使用一行程式碼提取各種有用的信息。全部的範例都假設 nmap 掃描結果保存在一個如上所示的檔案中。下面的會給出一些基本的範例程式碼,如果你想在 IPython 中直接執行它們,請先執行上面的程式碼,這樣它會直接在控制台輸出方便你的檢視。我通常會先做好這一步,這樣我就可以確保輸出的資料跟預期的一樣。
然後,你可以選擇一個變量名並使用 “=” 將資料賦值給這個變量,這樣你就可以在隨後的程式碼中直接調用,或者將其寫入到磁碟上以便 shell 命令使用。如果有些東西你想使用多次,可以貼上一些程式碼片段到Python 腳本中,或者想加入一些更複雜的邏輯但這樣可能會使REPL 環境難以處理,我會在最後一節中講述如何快速的執行這些操作。

連接埠資訊
開放指定連接埠號碼的主機
顯示所有開放指定連接埠號碼的主機。產生一個包含主機位址(string)的清單。下面以 443 連接埠為例,你可以修改成你自己需要的值。 

 [ 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()] ]

開放連接埠數量

顯示一系列主機開放連接埠的數量。產生一個包含連接埠數量(int)的列表,並進行排序。 

 sorted(set([ b[0] for a in nmap_report.hosts for b in a.get_open_ports()]), key=int)

主機開放連接埠對應的服務,按連接埠號碼分組

顯示所有主機開放的連接埠號碼,並按連接埠號碼分組並排序。產生一個包含多個清單的清單(即清單的每個元素也為清單),其中每個成員清單第一個元素為連接埠號碼(int),第二個元素為一個包含開放對應埠主機IP 位址( string)的列表。

 [ [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

使用 SSL 的主機和連接埠

#顯示所有使用 SSL 的主機和連接埠。這是透過尋找是否有服務使用了 “SSL” 通道或相關腳本檢測的結果中包含 pem 憑證。產生一個包含一系列列表的列表,每個成員列表中包含主機位址(string)和連接埠號碼(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) ]

下面的內容包含上述相同的訊息,但不在是一個包含列表的列表,而是使用 join 函數建立了一個包含 “主機:連接埠號碼”(string) 的列表。 

 [ ':'.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) ]

包含 web 服務的主機和連接埠

顯示所有的 web 服務及其對應的連接埠號碼和協定(http 或 https)。這會產生一個包含多個列表的列表,其中每個成員列表包含協定(string)、位址(string)和連接埠號碼(int)。但這裡會有些問題,nmap 在報告使用https 的網站時,有些時候會顯示服務是“https”,而有時則會顯示為使用“ssl” 通道的“http”,所以我調整了下資料格式以便統一輸出。
 

 [ [(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') ]

這裡還是相同的訊息,只不過是在原先包含協定、主機和連接埠號碼的清單中增加了url(string)。

 [ (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') ]

其他服務資訊
未知服務

顯示所有 nmap 無法辨識的服務。產生一個包含多個列表的列表,其中每個成員列表包含位址(string)、連接埠號碼(int)和 nmap 掃描的連接埠指紋(string)。產生這些信息,主要是為了方便後續人工審查那些特定的服務,而不會參與任何自動化的過程中。

 [ [ 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 辨識出的軟體
顯示 nmap 掃描中辨識出的所有軟體。產生按產品字母排序的清單。

 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中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn