长亭百川云 - 文章详情

如何自定义 PoC V 2.0

CT Stack

32

2022-04-15

V2 版本

POC 格式

一个最基础的 POC 如下:

1name: poc-yaml-example-com
2# 脚本部分
3transport: http
4rules:
5    r1:
6        request:
7            method: GET
8            path: "/"
9        expression: |
10            response.status==200 && response.body.bcontains(b'Example Domain')
11expression:
12    r1()
13# 信息部分
14detail:
15    author: name(link)
16    links: 
17        - http://example.com

整个 POC 大致可以分为 3 部分:

  • 名称: 脚本名称, string 类型
  • 脚本部分:主要逻辑控制部分,控制着脚本的运行过程
  • 信息部分:主要是用来声明该脚本的一些信息,包括输出内容

接下来分别介绍一个各个部分的格式和内容。

脚本部分

这个部分包含 4 部分内容,分别为:

  • 传输方式(transport)
  • 全局变量定义(set)
  • 规则描述(rules)
  • 规则表达式(expression)

传输方式(transport)

该字段用于指定发送数据包的协议。 transport: string

形如:

1transport: http

目前 transport 的取值可以为以下 3 种之一:

  1. tcp
  2. udp
  3. http

相比于 v1 版本,v2 版本做出的一个重要变更是允许发送多种多样的数据包了,不再仅局限于 http 请求,我们新增 tcp 和 udp 的支持。

目前不允许一个脚本中发送不同种 transport 的请求,因为通常我们的输入是一个稳定的协议, 比如:

  1. 端口存活探测的结果通常会知道它是 tcp 或者 udp 存活
  2. 或者从一个明确的 http 请求或者 web 站点开始

如果后续有其它协议的需求,这个字段的取值是可以扩展的。

全局变量定义(set)

该字段用于定义全局变量。比如随机数,反连平台等。 set: map[string]interface{}

形如:

1set:
2    a: 1

全局变量载荷 (payload)

该字段用于定义多个 payload,来实现发送不同 payload 的效果。 该字段结构如下

变量名/函数名类型说明
continuebool命中一个之后是否继续
payloadsmap[string]Setset 一样的结构和语法

形如:

1payloads:
2  continue: false
3  payloads:
4    ping:
5      cmd: r"ping test.com"
6    curl:
7      cmd: r"curl test.com"

:

  1. 只支持罗列 payload, 目前不考虑支持文件或者复杂排列组合等情况,每个 payload 中的 key 必须严格一致
  2. 循环 payload 然后把当前 payload 加在 set 之后,组成新的 set 执行
1set:
2    a: 1
3payloads:
4    - b: a
5    - b: b

实际运行结果相当于,运行两遍

第一遍:

1set:
2    a: 1
3    b: a

第二遍:

1set:
2    a: 1
3    b: b

规则描述(rules)

该字段定义了一些具名的 rule。 rules: map[string]Rule
形如:

1rules:
2    r1:
3        # 此处为一个 http request 的例子
4        request:
5            method: GET
6            path: "/"
7        expression: |
8            response.status==200 && response.body.bcontains(b'Example Domain')
9        # 相比于 V1 版本新增
10        output:
11            search: |
12                r'(?P<version>1.1.1)'.bsubmatch(response.raw)'
13            version: search['version']

其中 rule 的结构又可以分为 3 部分:

  • request:构造请求相关参数
  • expression:判断该条 Rule 的结果
  • output:声明一些变量,用于后续使用
request

该字段应该与前文说到的 transport 是对应的。对应于不同的 transport 有不同的请求参数设置。

tcp

结构如下:

1cache: true
2content: "request"
3read_timeout: "10"
4connection_id: xxx
  • cache: bool 是否使用缓存的请求,如果该选项为 true,那么如果在一次探测中其它脚本对相同目标发送过相同请求,那么便使用之前缓存的响应,而不发新的数据包
  • content:string 请求内容
  • read_timeout: string 发送请求之后的读取超时时间( 实际是一个 int, 但是为了能够变量渲染,设置为 string
  • connection_id: string 连接 id ,同一个连接 id 复用连接( 不允许用0; cache 为 true 的时候可能会导致请求不会发送,所以如果有特殊需求记得 cache: false)
udp

结构如下:

1cache: true
2content: "request"
3read_timeout: "10"
4connection_id: xxx
  • cache: bool 是否使用缓存的请求,如果该选项为 true,那么如果在一次探测中其它脚本对相同目标发送过相同请求,那么便使用之前缓存的响应,而不发新的数据包
  • content:string 请求内容
  • read_timeout: string 发送请求之后的读取超时时间( 实际是一个 int, 但是为了能够变量渲染,设置为 string
  • connection_id: string 连接 id ,同一个连接 id 复用连接( 不允许用0; cache 为 true 的时候可能会导致请求不会发送,所以如果有特殊需求记得 cache: false)
http

结构如下:

1cache: true
2method: GET
3path: /
4headers:
5    Content-Type: application/xml
6body: aaaa
7follow_redirects: true
  • cache: bool 是否使用缓存的请求,如果该选项为 true,那么如果在一次探测中其它脚本对相同目标发送过相同请求,那么便使用之前缓存的响应,而不发新的数据包
  • method: string 请求方法
  • path: string 请求的完整 Path,包括 querystring 等 (详情见: HTTP PATH 的使用)
    1. 如果 path 是以 / 开头的, 取 dir 路径拼接
    2. 如果 path 是以 ^ 开头的, uri 直接取该路径
  • headers: map[string]string 请求 HTTP 头,Rule 中指定的值会被覆盖到原始数据包的 HTTP 头中
  • body: string 请求的Body
  • follow_redirects: bool 是否允许跟随300跳转
expression

该字段定义了这条 rule 最终执行的一个结果. expression: string

形如:

1expression: |
2    response.status==200 && response.body.bcontains(b'Example Domain')

具体 expression 应该如何编写,请看 expression 编写

output

该字段定义了这条 rule 运行完成之后的一些变量,该字段定义的变量会被添加到全局变量中,类似于 setoutput: []string

形如:

1output:
2    search: |
3        r'(?P<version>1.1.1)'.bsubmatch(response.raw)'
4    version: search['version']

expression

该字段定义了整个脚本最终执行的一个结果. expression: string

形如:

1expression: |
2    r1() || r2()

具体 expression 应该如何编写,请看 expression 编写, 尤其注意脚本 expression 编写部分

信息部分(detail)

该字段用于定义一些和脚本相关的信息。内容都为非必填内容。

1detail:
2    author: name(link)
3    links: 
4        - http://example.com
5    # 指纹信息
6    fingerprint:
7        infos:
8            - id: "长亭指纹库 id"
9              name: "SSH"
10              version: {{version}}
11              type: "system_bin"
12              confidence: 70
13        host_info:
14            hostname: "test"
15    # 漏洞信息
16    vulnerability:
17        id: "长亭漏洞库 id"
18        match: "证明漏洞存在的信息"
19        # 其它字段
20        cve: "CVE-2020-1234"
21    # 其它未明确定义的字段
22    summary: "test"

目前主要定义了一下几个部分:

:其中支持变量渲染,形如 {{variable}}, 其中变量为 set 或者 rule output 中定义的变量

  • author: string 作者
  • links: []string 相关链接
  • fingerprint 指纹信息
    • infos: []Info 指纹信息
      • id: string 长亭指纹库 ID
      • name: string 名称
      • version: string 版本号
      • type: string 指纹类型,有以下可选值: operating_system, hardware, system_bin, web_application, dependency
      • confidence: int 取值范围(1-100)
    • host_info 主机信息
      • hostname: string 主机名
  • vulnerability 漏洞信息
    • id: string 长亭漏洞库 ID
    • match: string 证明漏洞存在的一些信息
    • 额外字段
  • 额外字段

expression编写

正如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”

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

response.content_type.contains("json")

值得注意的是,类似于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 结构中的 expression, 这个结果作为整个Rule调用的值

对于脚本层级的 expression,这个结果作为最后脚本是否匹配成功的值,通常脚本层级的 expression 是 rule 结果的一个组合。 比如一个脚本包含 r1, r2, r3r4 4 条规则, 作为脚本层级的 expression,其全局变量将会定义 r1, r2, r3r4 4 个函数,调用这个 4 个函数即可获得它对应 rule 的结果。

1expression: r1() && r2() && r3() && r4()

相比于 V1 版本, rule 如何运行这件事情发生了较大的改变。假设我们有 r1, r2, r3r4 4 条规则

最开始时, V1 版本添加了 rules: []Rule 来定义 rule 及其执行顺序。其逻辑为顺序执行 rule, 且 r1, r2, r3r4 都为 true 时, 脚本执行成功

1rules: 
2    r1: ...
3    r2: ...
4    r3: ...
5    r4: ...

然后添加了 group: map[string][]Rule 拓展了一下 ruel 的执行方式。支持了 r1,r2 同时为 true 或者 r3, r4 同时为 true 时, 脚本执行成功

1group:
2    g1: [r1, r2]
3    g2: [r3, r4]

在 V2 版本下, 我们使用 expression 来组织 rule 的执行逻辑。

对于方式1,其对应的形式为:

1rules: 
2    r1: ...
3    r2: ...
4    r3: ...
5    r4: ...
6expression: r1() && r2() && r3() && r4()

对于方式2, 其对应的形式为:

1rules: 
2    r1: ...
3    r2: ...
4    r3: ...
5    r4: ...
6expression: (r1() && r2()) || (r3() && r4())

甚至我们还能支持:

1rules: 
2    r1: ...
3    r2: ...
4    r3: ...
5    r4: ...
6expression: (r1() || r2() || r3()) && r4()

这里有几点需要说明一下:

  1. rule 的执行顺序是按照该逻辑表达式的执行顺序来执行的
  2. 短路求值, 即 r1() || r2(), 如果 r1() 的结果为 true 那么 r2 是不会执行的

xray 脚本注入的类型

xray 支持所有CEL文档中的类型,同时还注入了几种特殊的类型,包含:

  • addrType 连接地址信息
  • connInfoType 连接信息,包含源地址和目的地址, 可以通过 response.conn
  • urlType url 类型,可以 request.urlresponse.urlreverse.url 调用
  • reverseType 反连平台类型
  • request 扫描请求
  • response 请求的响应,通用属性包含:raw

其中注入的 request 和 response 类型是随着 transport 对应的值进行改变的。

基本类型

addrType 类型包含字段如下, 设变量名为 addr

变量名类型说明适用版本
addr.transportstringtranportXray ≥ 1.8.4
addr.addrstring目的地址, 获取失败时返回空字符串,形如: "192.0.2.1:25", "[2001:2001::1]:80"Xray ≥ 1.8.4
addr.portstring端口号, 获取失败时返回 ""Xray ≥ 1.8.4

connInfoType 类型包含字段如下, 设变量名为 conn

变量名类型说明适用版本
conn.sourceaddrType源地址信息Xray ≥ 1.8.4
conn.destinationaddrType目的地址信息Xray ≥ 1.8.4

urlType 类型包含的字段如下, 设变量名为 url, 以 http://example.com:8080/a?c=d#x=y 为例:

变量名类型说明适用版本
url.schemestringurl 的 scheme, 示例为 "http"Xray ≥ 1.8.4
url.domainstringurl 的域名,示例例为 "example.com"Xray ≥ 1.8.4
url.hoststringurl 的主机名,示例为 "example.com:8080"Xray ≥ 1.8.4
url.portstringurl 的 port,注意这里也是字符串。 示例为 "8080"Xray ≥ 1.8.4
url.pathstringurl 的 path, 示例为 "/a"Xray ≥ 1.8.4
url.querystringurl 的 query, 示例为 "c=d"Xray ≥ 1.8.4
url.fragmentstringurl 的锚点,示例为 "x=y"Xray ≥ 1.8.4

reverseType 类型包含字段如下, 设变量名为 reverse(需要先使用 newReverse() 生成实例)

变量名类型说明适用版本
reverse.urlurlType反连平台的 urlXray ≥ 1.8.4
reverse.domainstring反连平台的域名Xray ≥ 1.8.4
reverse.ipstring反连平台的 ip 地址Xray ≥ 1.8.4
reverse.is_domain_name_serverbool反连平台的 domain 是否同时是 nameserverXray ≥ 1.8.4
reverse.wait(timeout)func (timeout int) bool等待 timeout 秒,并返回是否在改时间内获得了信息Xray ≥ 1.8.4

tcp request and response

其中 request 包含的字段如下:

变量名类型说明适用版本
request.raw[]byte原始请求Xray ≥ 1.8.4

response 包含的字段如下:

变量名类型说明适用版本
response.connconnInfoType连接相关信息Xray ≥ 1.8.4
response.raw[]byte原始响应Xray ≥ 1.8.4

udp request and response

其中 request 包含的字段如下:

变量名类型说明适用版本
request.raw[]byte原始请求Xray ≥ 1.8.4

response 包含的字段如下:

变量名类型说明适用版本
response.connconnInfoType连接相关信息Xray ≥ 1.8.4
response.raw[]byte原始响应Xray ≥ 1.8.4

http request and response

其中 request 包含的字段如下:

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

response 包含的字段如下:

变量名类型说明适用版本
response.raw[]byte原始响应Xray ≥ 1.8.4
response.urlurlType自定义类型 urlType, 请查看下方 urlType 的说明Xray ≥ 1.8.4
response.statusint返回包的status codeXray ≥ 1.8.4
response.raw_header[]byte原始的 header 部分,需要使用字节流相关方法来判断。Xray ≥ 1.8.4
response.body[]byte返回包的Body,因为是一个字节流(bytes)而非字符串,后面判断的时候需要使用字节流相关的方法Xray ≥ 1.8.4
response.headersmap[string]string返回包的HTTP头,类似 request.headersXray ≥ 1.8.4
response.content_typestring返回包的content-type头的值Xray ≥ 1.8.4
response.latencyint响应的延迟时间,可以用于 sql 时间盲注的判断,单位毫秒 (ms)Xray ≥ 1.8.4

xray 脚本注入的函数

xray 支持所有CEL文档中的函数,同时还新增了一些函数支持,下面列举一下常用的函数:

:gocel 原生的正则支持使用的 golang 自带的 regex 库, 在脚本中我们使用 regex2 替换了自带的 regex 库

rule函数

:只有脚本层级的 expression 才能使用这些函数

函数名函数原型说明适用版本
rule namefunc <rule name>() bool返回这条 rule expression 执行的结果Xray ≥ 1.8.4

字符串处理

函数名函数原型说明适用版本
containsfunc (s1 string) contains(s2 string) bool判断s1是否包含s2,返回bool类型结果Xray ≥ 1.8.4
icontainsfunc (s1 string) icontains(s2 string) bool判断s1是否包含s2,返回bool类型结果, 与contains不同的是,icontains 忽略大小写Xray ≥ 1.8.4
substrfunc substr(string, start int, length int) string截取字符串Xray ≥ 1.8.4
replaceAllfunc replaceAll(string, old string, new string) string将 string 中的 old 替换为 new,返回替换后的 stringXray ≥ 1.8.4
printablefunc printable(string) string将 string 中的非 unicode 编码字符去掉Xray ≥ 1.8.4
toUintStringfunc toUintString(s1 string, direction string) stringdirection 取值为 >,<表示读取方向, 将 s1 按 direction 读取为一个整数,返回该整数的字符串形式Xray ≥ 1.8.4
startsWithfunc (s1 string) startsWith(s2 string) bool判断s1是否由s2开头Xray ≥ 1.8.4
endsWithfunc (s1 string) endsWith(s2 string) bool判断s1是否由s2结尾Xray ≥ 1.8.4

[]byte 处理

函数名函数原型说明适用版本
bcontainsfunc (b1 bytes) bcontains(b2 bytes) bool判断一个b1是否包含b2,返回bool类型结果。与contains不同的是,bcontains是字节流(bytes)的查找Xray ≥ 1.8.4
ibcontainsfunc (b1 bytes) ibcontains(b2 bytes) bool判断b1是否包含b2,返回bool类型结果, 与contains不同的是,ibcontains 是字节流(bytes)的查找, 且忽略大小写Xray ≥ 1.8.4
bstartsWithfunc (b1 bytes) bstartsWith(b2 bytes) bool判断一个b1是否由b2开头,返回bool类型结果。与startsWith不同的是,bcontains是字节流(bytes)的查找Xray ≥ 1.8.4
bformatfunc bformat(bytes, int, int, string, int)string将bytes进行进制转换编码,可以根据输入的参数转换成对应的进制编码,并可以自定义转换格式Xray ≥ 1.8.5

函数介绍:

  1. bformat的第一个参数为要转换的数据;
  2. 第二个参数为要转换的进制,范围为2-32;
  3. 第三个参数为填充位数,如果转换后的字符的位数小于该值,则会在前面添加0来进行填充;
  4. 第四个参数为在转换后的字符前添加指定字符串,例如\x等;
  5. 第五个参数控制第三,四个参数添加的位置

示例1:bformat(b'asdfghj',16,0,'',0)
输出1:6173646667686a

示例2:bformat(b'asdfghj',16,4,'',0)
输出2:006100730064006600670068006a

示例3:bformat(b'asdfghj',16,0,'\x',1)
输出3:\x61\x73\x64\x66\x67\x68\x6a

编码加密函数

函数名函数原型说明适用版本
md5func md5(string) string字符串的 md5Xray ≥ 1.8.4
base64func base64(string/bytes) string将字符串或 bytes 进行 base64 编码Xray ≥ 1.8.4
base64Decodefunc base64Decode(string/bytes) string将字符串或 bytes 进行 base64 解码Xray ≥ 1.8.4
urlencodefunc urlencode(string/bytes) string将字符串或 bytes 进行 urlencode 编码Xray ≥ 1.8.4
urldecodefunc urldecode(string/bytes) string将字符串或 bytes 进行 urldecode 解码Xray ≥ 1.8.4
faviconHashfunc faviconHash(string/bytes) int将字符串或 bytes 进行 faviconHash 编码,参考:iconhashXray ≥ 1.8.4
shafunc sha(string/bytes, string)string该函数可以将指定字符串或bytes进行sha系列计算,第二个参数控制加密类型。例:sha('asd','sha1')。目前支持'sha1'、'sha224'、'sha256'、'sha384'、'sha512'Xray ≥ 1.8.4
hmacShafunc hmacSha(string/bytes, string/bytes, string)string该函数可以将指定字符串或bytes进行hmac_sha系列计算,第一个参数为待加密明文,第二个参数为密钥,第三个参数控制加密类型。例:sha('asd','123','sha1')。目前支持'sha1'、'sha224'、'sha256'、'sha384'、'sha512'Xray ≥ 1.8.4

随机值

函数名函数原型说明适用版本
randomIntfunc randomInt(from, to int) int两个范围内的随机数Xray ≥ 1.8.4
randomLowercasefunc randomLowercase(n length) string指定长度的小写字母组成的随机字符串Xray ≥ 1.8.4

正则

函数名函数原型说明适用版本
matchesfunc (s1 string) matches(s2 string) bool使用正则表达式s1来匹配s2,返回bool类型匹配结果Xray ≥ 1.8.4
submatchfunc (s1 string) matches(s2 string) map[string]string使用正则表达式s1来匹配s2,返回 map[string]string 类型结果,:只返回具名的正则匹配结果 (?P…) 格式Xray ≥ 1.8.4
bmatchesfunc (s1 string) bmatches(b1 bytes) bool使用正则表达式s1来匹配b1,返回bool类型匹配结果。与matches不同的是,bmatches匹配的是字节流(bytes)Xray ≥ 1.8.4
bsubmatchfunc (s1 string) bmatches(b1 bytes) map[string]string使用正则表达式s1来匹配b1,返回 map[string]string 类型结果 :只返回具名的正则匹配结果 (?P…) 格式。与matches不同的是,bmatches匹配的是字节流(bytes)Xray ≥ 1.8.4

反连平台

函数名函数原型说明适用版本
newReversefunc newReverse() reverseType返回一个 reverse 实例Xray ≥ 1.8.4
waitfunc (reverse reverseType) wait(timeout int) bool等待 timeout 秒,并返回是否在改时间内获得了信息Xray ≥ 1.8.4

时间

函数名函数原型说明适用版本
nowfunc now() Time使用该函数将返回当前时间,使用int(now()),将会获取当前时间的时间戳Xray ≥ 1.8.5
sleepfunc sleep(int) bool暂停执行等待指定的秒数Xray ≥ 1.8.4
timeConvertfunc timeConvert(int, string) string该函数可以将指定时间戳转换成自定义格式的字符串Xray ≥ 1.8.5
  • timeConvert本质上是使用golang的time包提供的Format方法,第一个参数传入时间戳,第二个参数传入想要函数输出的时间格式

  • 以下是golang中time包对于时间的详细定义

    代表写法
    ⽉份1,01,Jan,January
    2,02,_2
    3,03,15,PM,pm,AM,am
    4,04
    5,05
    06,2006
    时区-07,-0700,Z0700,Z07:00,-07:00,MST
    周⼏Mon,Monday
    • ⽐如⼩时的表⽰(原定义是下午3时,也就是15时)
      3 ⽤12⼩时制表⽰,去掉前导0
      03 ⽤12⼩时制表⽰,保留前导0
      15 ⽤24⼩时制表⽰,保留前导0
      03pm ⽤24⼩时制am/pm表⽰上下午表⽰,保留前导0
      3pm ⽤24⼩时制am/pm表⽰上下午表⽰,去掉前导0

    • ⼜⽐如⽉份
      1 数字表⽰⽉份,去掉前导0
      01 数字表⽰⽉份,保留前导0
      Jan 缩写单词表⽰⽉份
      January 全单词表⽰⽉份

  • 示例:timeConvert(1560391089, '2006_01_02/3/4/05')
    输出:2019_06_13/9/58/09

其它

函数名函数原型说明适用版本
instring in mapmap 中是否包含某个 key,目前只有 headers 是 map 类型Xray ≥ 1.8.4
相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2