> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xray.cool/llms.txt
> Use this file to discover all available pages before exploring further.

# 最佳实践

> 本文将展示在不同场景下的最佳编写实践和一些编写规范

## 相关定义

### 被动指纹（Passive）

* 不请求： 不写request结构
* / 请求， 无其它额外设置

```yaml theme={null}
request:
  cache: true
  method: GET
  path: /
```

* icon： 使用 getIconContent 函数

### 主动指纹（Active）

除被动指纹外的情况都属于主动指纹

## 编写规范（Prepare a specification）

<AccordionGroup>
  <Accordion title="1. 脚本应该通过 lint 的校验">
    目前 lint 会检查以下内容

    1. celcheck：用于校验 cel 语句是否能够运行，类型推到的结果是否正确。
    2. schemacheck：校验脚本内容是否符合编写的 json schema 格式，用于检测是否有必须填写的字段缺失。
    3. payloadcheck：检查每一个 payload 分支内声明的变量类型是否是一致的。
    4. transportcheck：校验 rule 中的请求是否符合规范。
    5. 相关细节以及对应修复方式请参考[YAML插件修复手册](/plugins/yaml/Fix)
  </Accordion>

  <Accordion title="2. 将越常命中的规则放在越靠前的位置">
    当存在多个‘或’逻辑的时候，系统会按序执行，直到命中一个才会停止，所以，将常命中的规则放在越靠前的位置能够有效的减少不必要的发包
  </Accordion>

  <Accordion title="3. 如无特殊需要，不编写主动指纹，避免请求量爆炸">
    被动指纹指的是只向根路径发包，或者只使用获取icon的函数进行指纹识别的指纹，而除了这两种情况，其他的都属于主动指纹。
  </Accordion>

  <Accordion title="4. 在使用关键字进行匹配时，要求使用包含产品特征的关键字，而且尽可能长，避免误报">
    例如匹配discuz的时候，关键词中最好带有该产品的特殊的内容，例如`discuz_uid`等。
    特别的：部分通用系统存在二次开发修改title的情况，因此title只能作为辅助判断的逻辑，只能算作加强规则，不能作为决定性证据
  </Accordion>

  <Accordion title="5. 当有多条被动指纹规则时，尽量使用&&组合两条规则，减少单条规则容易造成的误报">
    一般来讲，符合第四点的情况下，两条规则即可
  </Accordion>

  <Accordion title="6. 如无特殊需要，指纹规则中不带攻击特征，避免被 WAF 拦截">
    一般有两种情况，一种是spring的访问/env等路径，有些时候会被拦；一种情况是path中带有admin，有些waf会拦截
  </Accordion>

  <Accordion title="7. 优先使用 contains、其次 icontains，减少运行过程中不必要的资源消耗">
    icontains在匹配时会忽略匹配字母的大小写，增加资源消耗
  </Accordion>

  <Accordion title="8. 在处理所有的 404 页面时统一使用get404Path()函数获取路径">
    [get404Path](/plugins/yaml/TypeFunc/example/http/get404path)函数做了一些特殊处理，这个函数可以保证一个目标将仅拥有一个404path，这样便于使用cache，降低发包，提高效率。
  </Accordion>

  <Accordion title="9. 如果能自动获取 icon， 尽量通过 getIconContent 来自动获取图标内容">
    [getIconContent](/plugins/yaml/TypeFunc/example/other/getIconContent)函数可以在响应中获取icon地址，然后访问这个icon获取其字节流数据。
  </Accordion>

  <Accordion title="10. 取特征时，不要在header头Set-Cookie中取">
    因为引擎在做批量扫描时会先访问一次根路径，所以之后的请求包中通常不会有Set-Cookie的响应头，导致无法命中
  </Accordion>

  <Accordion title="11. 尽量使用推荐的规则名称，当有多个同类规则时使用数字隔开">
    例如：kw\_in\_body\_01、kw\_in\_body\_02
  </Accordion>

  <Accordion title="12. 当存在多个版本匹配的规则的时候，建议使用payloads进行编写，提高插件的可阅读性，必要时可以使用三目运算符做判断">
    详情参考：[三目运算符的使用方式](/plugins/yaml/advanced/Set#ternary-operator)

    ```yaml theme={null}
    name: fingerprint-yaml-tcp-mysql
    manual: false
    transport: tcp
    set:
      GenericLines: b"\r\n\r\n"
    payloads:
      payloads:
        l:
          re: '"(?s)^.\\0\\0\\0\\xffj\\x04''[\\d.]+'' .* MySQL"'
        m:
          re: '"(?s)^.\\0\\0\\0\\x0a(?P<version>5\\.[-_~.+:\\w]+MariaDB-[-_~.+:\\w]+~bionic)\\0"'
        n:
          re: '"(?s)^.\\0\\0\\0\\x0a(?P<version>[\\w._-]+)\\0............\\0\\x5f\\xd3\\x2d\\x02\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0............\\0$"'
        r:
          re: '"(?s)^.\\0\\0\\0\\x0a(?P<version>3\\.[-_~.+\\w]+)\\0...\\0"'
        s:
          re: '"(?s)^.\\0\\0\\0\\x0a(?P<version>4\\.[-_~.+\\w]+)\\0"'
    rules:
      r1:
        request:
          cache: true
          content: '{{GenericLines}}'
        expression: re.bmatches(response.raw)
        output:
          result: re.bsubmatch(response.raw)
          osname: |
            re.contains("bionic") ? "Linux" : ""
          version: result["version"]
    expression: r1()
    detail:
      fingerprint:
        infos:
          - type: system_bin
            name: mysql
            version: '{{version}}'
          - type: operating_system
            name: '{{osname}}'
    ```
  </Accordion>
</AccordionGroup>

## 通过 ICON HASH 匹配

<Tip>建议规则名称：`favicon_hash`</Tip>

### 便捷计算icon hash的方式

<Tabs>
  <Tab title="Windows">
    <Tip>将下列代码放入powershell中运行，请确保你对应版本的python有安装requests，mmh3包</Tip>

    <CodeGroup>
      ```powershell python3 theme={null}
      function Get-IconHash {
      param(
          [string]$url
      )
      python -c "import requests,sys,mmh3,codecs;print('icon_hash=' + str(mmh3.hash(codecs.lookup('base64').encode(requests.get('$url').content)[0])))"
      }
      ```

      ```powershell python2 theme={null}
      function Get-IconHash {
      param(
          [string]$url
      )
      python -c "import requests,sys,mmh3,codecs;response = requests.get(sys.argv[1]);favicon = codecs.encode(response.content, 'base64');hash = mmh3.hash(favicon);print 'icon_hash=%s' % hash" $url
      }
      ```
    </CodeGroup>

    使用示例：

    ```powershell theme={null}
    Get-IconHash 'https://www.baidu.com/favicon.ico'

    # 运行结果
    icon_hash=-1588080585
    ```
  </Tab>

  <Tab title="Linux">
    <Tip>将下列代码放入bash中运行，请确保你对应版本的python有安装requests，mmh3包</Tip>

    <CodeGroup>
      ```bash python3 theme={null}
      alias icon_hash="python3 -c \"import requests,sys,mmh3,codecs;print('icon_hash=%s'%mmh3.hash(codecs.lookup('base64').encode(requests.get(sys.argv[1]).content)[0]))\""
      ```

      ```bash python2 theme={null}
      alias icon_hash="python2 -c \"import requests,sys,mmh3;print mmh3.hash(requests.get(sys.argv[1]).content.encode('base64'))\""
      ```
    </CodeGroup>

    使用示例：

    ```bash theme={null}
    icon_hash https://www.baidu.com/favicon.ico

    # 运行结果
    icon_hash=-1588080585
    ```
  </Tab>
</Tabs>

### 使用内置函数自动搜索 ICON 路径

```yaml theme={null}
rules:
  favicon_hash:
    request:
      method: GET
      path: /
    expression: |-
      faviconHash(response.getIconContent()) == 149371702
```

### 自定义 ICON 路径

```yaml theme={null}
rules:
  favicon_hash:
    request:
      method: GET
      path: /not_normal_favicon_path.ico
    expression: |-
      faviconHash(response.body) == 149371702
```

## 通过内容MD5匹配

<Tip>建议规则名称：`md5_in_[position]`</Tip>

```yaml theme={null}
rules:
  md5_in_body:
    request:
      method: GET
      path: /this_is_test_path
    expression: |-
      md5(response.body) == "ce1a1c8754948c6cbfcfa48545e8174b"
```

## 通过 Body 关键字匹配

<Tip>建议规则名称：`kw_in_body`</Tip>

### 基础模版（Basic templates）

```yaml theme={null}
rules:
  kw_in_body:
    request:
      method: GET
      path: /
    expression: |-
      response.body_string.contains("<hr><center>openresty")
```

### 匹配的字符串中包含引号（inverted comma）

```yaml theme={null}
rules:
  kw_in_body:
    request:
      method: GET
      path: /
    expression: |-
      response.body_string.contains('href="https://www.cloudflare.com/5xx-error-landing"')
```

### 忽略大小写（ignore capitals）

```yaml theme={null}
rules:
  kw_in_body:
    request:
      method: GET
      path: /
    expression: |-
      response.body_string.icontains('seeyon')
```

### 二进制内容匹配（binary）

```yaml theme={null}
rules:
  kw_in_body:
    request:
      method: GET
      path: /
    expression: |-
      response.body.bcontains(b'\xe6\x98\x93\xe7\xbd\x91\xe5\x85\xb3')
```

## 通过 Header 关键字匹配

<Tip>建议规则名称：`kw_in_header || kw_in_[header_name]`</Tip>

### Cookie 匹配

```yaml theme={null}
rules:
  kw_in_cookie:
    request:
      method: GET
      path: /
    expression: |-
      response.headers["Cookie"].contains("JSESSIONID=")
```

### Server 匹配

```yaml theme={null}
rules:
  kw_in_server:
    request:
      method: GET
      path: /
    expression: |-
      response.headers["server"].contains("Apache/")
```

### 不常见的 Header Key 匹配（规则名称统一使用 kw\_in\_header）

```yaml theme={null}
rules:
  kw_in_header:
    request:
      method: GET
      path: /
    expression: |-
      response.headers["X-Protected-By"].contains("OpenRASP")
```

### 判断某个 Key 是否在 Headers 中

```yaml theme={null}
rules:
  kw_in_header:
    request:
      method: GET
      path: /
    expression: |-
      "CF-RAY" in response.headers
```

### 在完整响应头中匹配（raw\_header）

```yaml theme={null}
rules:
  kw_in_header:
    request:
      method: GET
      path: /
    expression: |-
      response.raw_header.bcontains(b'HP-ChaiSOE')
```

## 通过 CERT 关键字匹配

<Tip>建议规则名称：kw\_in\_cert</Tip>

```yaml theme={null}
rules:
  kw_in_cert:
    request:
      method: GET
      path: /
    expression: |-
      response.raw_cert.ibcontains(b"SANGFOR VMP")
```

## 通过 404Path 进行特征匹配

```yaml theme={null}
name: xxxxx
transport: http
detail:
  cpe: xxxx
set:
  pathname: get404Path()
rules:
  kw_in_404_body:
    request:
      method: GET
      path: /{{pathname}}
    expression: response.body_string.contains("xxxx")
expression: kw_in_404_body()
```

## 多个 Context 匹配的简化写法

<Tip>
  详见[全局变量载荷 - payload](../../yaml/advanced/Payloads)
</Tip>

```yaml theme={null}
name: hessian
transport: http
detail:
  cpe: caucho_technology:hessian
  version: '{{version}}'
payloads:
  payloads:
    p0:
      path: |
        "<path_0>"
    p1:
      path: |
        "<path_1>"
    p2:
      path: |
        "<path_2>"
rules:
  kw_in_body:
    request:
      method: POST
      path: /{{path}}
    expression: |
      response.body_string.contains("xxx")
expression: kw_in_body()
```

## 使用正则表达式进行匹配（RE）

```yaml theme={null}
rules:
  kw_in_body:
    request:
      method: GET
      path: /
    expression: |-
      "/owa/auth/.*?/themes/resources/favicon.ico".matches(response.body_string)
```

## 使用正则提取信息（RE）

```yaml theme={null}
version_detect:
  request:
    method: GET
    path: /
  expression: |
    "server" in response.headers && response.headers["server"].contains("nginx")
  output:
    version: |
      "^nginx/(?P<version>.*)$".submatch(response.headers["server"])["version"]
```
