巧用python和libnmapd,提取Nmap扫描结果
每当我进行内网渗透面对大量主机和服务时,我总是习惯使用自动化的方式从 nmap 扫描结果中提取信息。这样有利于自动化检测不同类型的服务,例如对 web 服务进行路径爆破,测试 SSL/TLS 服务使用的密钥或协议,以及其他有针对性的测试。
复制代码 代码如下: from libnmap.parser import NmapParser
nmap_report = NmapParser.parse_fromfile('up_hosts_all_ports_fullscan.xml') 本文的余下部分会包含一系列使用一行代码提取各种各样有用的信息。全部的示例都假设 nmap 扫描结果保存在一个如上所示的文件中。下面的会给出一些基本的示例代码,如果你想在 IPython 中直接运行它们,请先运行上面的代码,这样它会直接在控制台输出方便你的查看。我通常会先做好这一步,这样我就可以确保输出的数据跟预期的一样。 端口信息 复制代码 代码如下: [ 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 识别出的软件 复制代码 代码如下: sorted(set([ b.banner for a in nmap_report.hosts for b in a.services if 'product' in b.banner])) 软件对应的主机和端口号,按产品分组 复制代码 代码如下: [ [ 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])) ] 搜索指定关键词相关的主机和端口 复制代码 代码如下: [ [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() ] 其他的事情 相同的证书名称 复制代码 代码如下: [ [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 时只是将输出打印在屏幕上。这的确不错,因为这样你可以随时查看到自己感兴趣的信息,但你可能还会想做更多的事情。之所以去生成上述信息,一大好处就在于你可以根据结果轻松执行一些自动化的操作。 保存到磁盘 复制代码 代码如下: [ ':'.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 代码 复制代码 代码如下: 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 扫描结果。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |