Nmap扩展开发(四)

倾旋
倾旋
技术分享|2019-4-24|最后更新: 2023-6-25|
type
status
date
slug
summary
tags
category
icon
password
URL

0x01 HTTP包的使用

一般情况下,我们扫描一些Web服务的同时需要进行渗透测试、安全评估、漏洞检测等操作,但是官方并未提供符合我们需求的脚本,这时候就要自己写脚本了。Nmap已经内置了HTTP包,不需要再进行下载和配置。

0x02 基础概念铺垫

首先,先介绍两个表结构,为了方便我们后续的数据操作,让读者先熟悉两个东西:
  • 响应表
响应表中主要涵盖了:HTTP状态码、HTTP响应头、HTTP版本、HTTP原始响应头、Cookies、HTTP响应主体内容(body)等
  • Options表
Options表主要用于设置HTTP请求时的超时时间、Cookie、请求头、HTTP认证、页面缓存、地址类型(IPV4/IPV6)、是否验证重定向

0x03 确认目标主机的HTTP服务是否支持HEAD

  • 引入HTTP包:
local http = require "http"
  • 确认目标主机的HTTP服务是否支持HEAD
这里主要使用can_use_head函数,参数有4个,函数原型如下:
local status,header = can_use_head(host,port,result_404,path)
参数说明:
  • host : host表
  • port : port表
  • result_404 : 由identify_404函数确认当前Web服务器是否设置了404页面且返回200状态码,一般情况下填写404或者nil。
  • path : 请求路径,默认为“/”根目录
  • 其中status是一个布尔值,如果返回true则支持HEAD,返回false则不支持
  • header是HEAD请求的结果。
需求:确认目标主机是否支持HEAD,如果支持则输出响应头
代码解读:
看完代码读者可能会有疑问,hostrule函数为什么返回false?
在hostrule中返回false是因为如果是true会自动调用action,此时port的值是nil,所以会抛出一些错误。
紧接着就是将端口号为80的host和port交给action函数执行,调用can_use_head函数,判断status是否为true,是true则支持HEAD方法请求。最后生成一个output_table,用来将响应内容填入这个表,以便于格式化显示。
notion image
如果只想取得目标主机响应的server,我们可以这样写:
执行结果如下:
notion image

0x04 发送一个HTTP请求

generic_request是一个最基本的发送HTTP请求的函数,参数有以下几个:
  • host : host表
  • port : port 表
  • method : HTTP方法,例如:GET、POST、HEAD…
  • path : 请求路径,默认是根路径“/”
  • options : 用于设置请求相关的Cookie、超时时间、header
例如:
发送一个OPTIONS请求,来获取目标服务支持哪些HTTP方法
执行结果如下:
notion image
  • 返回值:
该函数返回一个响应表,具体可参考前面的0X02响应表

0x04 发送一个GET请求

get函数也是基于generic_request函数的,具体可以去看nselib/http.lua源代码,该函数有以下参数:
  • host : host表
  • port : port 表
  • path : 请求路径,默认是根路径“/”
  • options : 用于设置请求相关的Cookie、超时时间、header
除了比generic_request函数中少了一个method参数,其他相同。
执行结果:
notion image
  • 返回值:
该函数返回一个响应表,具体可参考前面的0X02响应表

0x05 发送一个POST请求

post函数也是基于generic_request函数的,具体可以去看nselib/http.lua源代码,该函数有以下参数:
  • host : host表
  • port : port 表
  • path : 请求路径,默认是根路径“/”
  • options : 用于设置请求相关的Cookie、超时时间、header
  • ignored : 是否忽略向后兼容性
  • postdata : post提交数据,可以是一个表,也可以是一个字符串,具体形式如下:
尝试使用账号密码登录某个系统
用php脚本语言写一个简单登录判断的页面:
通过post函数提交username与password,然后获取body判断是否登录成功
执行结果:
notion image
  • 返回值:
该函数返回一个响应表,具体可参考前面的0X02响应表

0X06 编写一个检测CVE-2017-12615的脚本

为了检验读者对之前的内容是否有所收获,所以产生一个需求,编写一个针对CVE-2017-12615的漏洞检测脚本。首先我们需要了解这个漏洞:
编写CVE-2017-12615的漏洞检测脚本
攻击者可以利用这个漏洞,向目标服务器上传恶意 JSP 文件,通过上传的 JSP 文件 ,可在用户服务器上执行任意代码,从而导致数据泄露或获取服务器权限,存在高安全风险。
漏洞的利用方式是通过PUT请求,这让我们不得不学习一个新的函数——put函数,它的参数类似于post函数。
  • host : host表
  • port : port 表
  • path : 请求路径,默认是根路径“/”
  • options : 用于设置请求相关的Cookie、超时时间、header
  • putdata : 要上传的文件的内容
这里我使用Docker已经搭建好了一个Tomcat环境:
notion image
编写的脚本如下:
在action函数中,首先生成一个随机的文件名,然后发送PUT请求,判断响应码是否是201。注意,发送PUT请求的时候,文件扩展名后门必须带”/”,是为了绕过tomcat的检测。
执行结果:
notion image
使用浏览器访问:
notion image
发现CVE-2017-12615这个字符,证明该漏洞的确存在。

0x07 响应内容匹配

当我们需要对HTTP响应内容进行操作的时候,需要学习一些字符串操作函数、HTTP包内的函数。
  • response_contains函数,用于在响应表中匹配字符串,参数如下:
  • response : 响应表,可以是(http.get、http.post、http. pipeline_go、http.head等函数的返回值)
  • pattern : 字符串匹配模式,可参考lua手册
  • case_sensitive : 是否区分大小写,默认值为false,不区分
返回值:
  • match_state : 匹配成功为true,匹配失败为false
  • matchs : 返回一个匹配结果表,前提是match_state为true
了解了这个函数后,我们可以继续将CVE-2017-12615漏洞检测脚本进一步的优化,让脚本判断写入了jsp文件后,判断是否是我们写入的字符串。这样能够使检测脚本的准确度大大提高,下面请看我在action函数中写入的新代码:
脚本执行结果与上一节中的内容相同,只是多了一次GET请求,为了让读者真正理解这个脚本的执行过程,下面对比一下wireshark流量:
  • 优化前:
notion image
首先脚本直接向80端口发送了一个PUT请求,然后服务器响应405,漏洞利用失败。
紧接着脚本又请求了8080端口,服务器响应201,证明漏洞利用成功。
notion image
  • 优化后:
notion image
通过向8080端口发送PUT请求成功利用后,又向写入文件发送了一次GET请求,获取响应内容,进行字符串匹配,达到更加深度的验证漏洞是否利用成功。

0x08 并发HTTP请求

这里说的并发HTTP请求的原理是与目标主机建立一个socket,在每一次发送报文后都不会断开(除了最后一次请求)。平常我们在扩展脚本中调用一个http.get函数,将会返回一个响应表,代表已经获取了目标主机的响应报文,当返回响应表之前就已经与目标主机断开了连接。下一次调用http.get时,还需要进行一次建立连接的过程,导致我们会消耗一定的时间。如果一个扩展脚本需要发送多次请求,可以考虑使用http.pipeline_add与http.pipline_go配合使用。
下面就来介绍这两个函数如何配合使用:
http.pipeline_add参数如下:
  • path : 请求路径
  • options : 用于设置请求相关的Cookie、超时时间、header
  • all_requests : (可选值),如果是第一次调用,则为nil,若不是第一次调用,需要传入上一次http.pipeline_add的返回值
  • method : HTTP方法(GET、POST、HEAD、PUT等),默认为GET
返回值:
  • 一个请求表
可以有多个,下标从1开始,如果需要查看这个请求列表的结构,可以直接在action函数中return出http.pipeline_add的返回值,代码演示如下:
执行结果:
notion image
因为调用了两次http.pipeline_add,所以会产生两个请求列表队列元素,如果只想获取第一个请求队列元素,可以return all_requests[1]。
下面我们要开始将队列交给http.pipeline_go函数,由它来完成所有请求,函数参数如下:
  • host : host表
  • port : port 表
  • all_requests : 由http.pipeline_add函数装在好的请求列表
返回值:
  • response_list : 响应列表
示例代码:
这段代码中添加了两个请求队列元素,分别是:
  • /index.jsp
  • /docs/changelog.html
将队列交给http.pipeline_go函数后,返回一个响应列表,all_response[1]对应index.jsp的响应表,all_response[2]对应/docs/changelog.html的响应表。因此可以使用迭代,将每个请求的某个字段放入一个输出表里,本次示例是取得了两次请求队status-line。
执行结果:
notion image

0x09 表单操作

关于表单操作,在爬虫、漏洞扫描、爆破时用的较多,一般要先取回目标主机的响应body,然后字符串匹配获得表单结构。但是这些操作在Nmap中已经提供了一些方法,接下来就让我们一起学习http包中的表单操作函数吧。
http.grab_forms 用于在响应内容中查找表单,并返回一个form_list表,参数如下:
  • body : 响应表中的body
返回值:
  • form_list : 表单列表
获取页面中的表单列表
我使用apache和php搭建了一个简单的登录界面,尝试通过nmap的扩展脚本来获取表单列表。
notion image
查看一下HTML源码:
notion image
可以发现页面中这个表单是提交到index.php的,并且提供了两个输入值,分别是username和password,这种情况下我们完全可以采用lua的字符串匹配模式获得input的name,但是如果页面上有很多表单,这就会增加我们处理数据的压力。
我们来试试http.grab_forms函数:
因为http.grab_forms函数接收的是响应表的body,所以需要调用http.get函数将响应表取到,再把响应表的body传递进去。
执行结果:
notion image
目前是获得了一个登录表单,注意:返回的是一个表单列表,而不是只能获得一个表单,在登录页面中新添加一个表单后:
notion image
再执行脚本观察一下:
notion image
如果还想进一步获得input的name值,就要学习另外一个函数—— http. parse_form
参数如下:
  • form : 表单的明文
返回值:
  • 一个带键的表,分别有:action、method、fields
示例:
注意:fields是一个table,不是一个字符串,里面有表单的多个字段
需求:爬取页面表单,并尝试登录
当前页面有两个表单,先尝试一个表单来登录,整体步骤如下:
  1. 抓取表单
  1. 解析表单
  1. 拼接字段
  1. 登录
  1. 判断是否登录成功
本段代码首先获取index.php的响应表,通过http.grab_forms函数获得登录,将返回值(第一个表单)交给http.parse_from,取得表单fields(字段),然后遍历每一个字段的name,把它填入一个table,最后执行http.post函数,刚好http.post的data可以是一个table也可以是一个字符串。请求完毕后得到登录的响应结果,再由http.response_contains判断是否登录成功,登录成功会出现success字样提示。
为了解决疑惑,我贴出index.php的源代码:
下一章:DNS包的使用
正向解析、反向解析、发送DNS请求等
©2021-2024 倾旋. All rights reserved.