Fingerprint
最佳实践
本文将展示在不同场景下的最佳编写实践和一些编写规范
相关定义
被动指纹(Passive)
- 不请求: 不写request结构
- / 请求, 无其它额外设置
request:
cache: true
method: GET
path: /
- icon: 使用 getIconContent 函数
主动指纹(Active)
除被动指纹外的情况都属于主动指纹
编写规范(Prepare a specification)
目前 lint 会检查以下内容
- celcheck:用于校验 cel 语句是否能够运行,类型推到的结果是否正确。
- schemacheck:校验脚本内容是否符合编写的 json schema 格式,用于检测是否有必须填写的字段缺失。
- payloadcheck:检查每一个 payload 分支内声明的变量类型是否是一致的。
- transportcheck:校验 rule 中的请求是否符合规范。
- 相关细节以及对应修复方式请参考YAML插件修复手册
当存在多个‘或’逻辑的时候,系统会按序执行,直到命中一个才会停止,所以,将常命中的规则放在越靠前的位置能够有效的减少不必要的发包
被动指纹指的是只向根路径发包,或者只使用获取icon的函数进行指纹识别的指纹,而除了这两种情况,其他的都属于主动指纹。
例如匹配discuz的时候,关键词中最好带有该产品的特殊的内容,例如discuz_uid
等。
特别的:部分通用系统存在二次开发修改title的情况,因此title只能作为辅助判断的逻辑,只能算作加强规则,不能作为决定性证据
一般来讲,符合第四点的情况下,两条规则即可
一般有两种情况,一种是spring的访问/env等路径,有些时候会被拦;一种情况是path中带有admin,有些waf会拦截
icontains在匹配时会忽略匹配字母的大小写,增加资源消耗
get404Path函数做了一些特殊处理,这个函数可以保证一个目标将仅拥有一个404path,这样便于使用cache,降低发包,提高效率。
getIconContent函数可以在响应中获取icon地址,然后访问这个icon获取其字节流数据。
因为引擎在做批量扫描时会先访问一次根路径,所以之后的请求包中通常不会有Set-Cookie的响应头,导致无法命中
例如:kw_in_body_01、kw_in_body_02
详情参考:三目运算符的使用方式
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}}'
通过 ICON HASH 匹配
建议规则名称:
favicon_hash
便捷计算icon hash的方式
将下列代码放入powershell中运行,请确保你对应版本的python有安装requests,mmh3包
使用示例:
Get-IconHash 'https://www.baidu.com/favicon.ico'
# 运行结果
icon_hash=-1588080585
使用内置函数自动搜索 ICON 路径
rules:
favicon_hash:
request:
method: GET
path: /
expression: |-
faviconHash(response.getIconContent()) == 149371702
自定义 ICON 路径
rules:
favicon_hash:
request:
method: GET
path: /not_normal_favicon_path.ico
expression: |-
faviconHash(response.body) == 149371702
通过内容MD5匹配
建议规则名称:
md5_in_[position]
rules:
md5_in_body:
request:
method: GET
path: /this_is_test_path
expression: |-
md5(response.body) == "ce1a1c8754948c6cbfcfa48545e8174b"
通过 Body 关键字匹配
建议规则名称:
kw_in_body
基础模版(Basic templates)
rules:
kw_in_body:
request:
method: GET
path: /
expression: |-
response.body_string.contains("<hr><center>openresty")
匹配的字符串中包含引号(inverted comma)
rules:
kw_in_body:
request:
method: GET
path: /
expression: |-
response.body_string.contains('href="https://www.cloudflare.com/5xx-error-landing"')
忽略大小写(ignore capitals)
rules:
kw_in_body:
request:
method: GET
path: /
expression: |-
response.body_string.icontains('seeyon')
二进制内容匹配(binary)
rules:
kw_in_body:
request:
method: GET
path: /
expression: |-
response.body.bcontains(b'\xe6\x98\x93\xe7\xbd\x91\xe5\x85\xb3')
通过 Header 关键字匹配
建议规则名称:
kw_in_header || kw_in_[header_name]
Cookie 匹配
rules:
kw_in_cookie:
request:
method: GET
path: /
expression: |-
response.headers["Cookie"].contains("JSESSIONID=")
Server 匹配
rules:
kw_in_server:
request:
method: GET
path: /
expression: |-
response.headers["server"].contains("Apache/")
不常见的 Header Key 匹配(规则名称统一使用 kw_in_header)
rules:
kw_in_header:
request:
method: GET
path: /
expression: |-
response.headers["X-Protected-By"].contains("OpenRASP")
判断某个 Key 是否在 Headers 中
rules:
kw_in_header:
request:
method: GET
path: /
expression: |-
"CF-RAY" in response.headers
在完整响应头中匹配(raw_header)
rules:
kw_in_header:
request:
method: GET
path: /
expression: |-
response.raw_header.bcontains(b'HP-ChaiSOE')
通过 CERT 关键字匹配
建议规则名称:kw_in_cert
rules:
kw_in_cert:
request:
method: GET
path: /
expression: |-
response.raw_cert.ibcontains(b"SANGFOR VMP")
通过 404Path 进行特征匹配
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 匹配的简化写法
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)
rules:
kw_in_body:
request:
method: GET
path: /
expression: |-
"/owa/auth/.*?/themes/resources/favicon.ico".matches(response.body_string)
使用正则提取信息(RE)
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"]
On this page
- 相关定义
- 被动指纹(Passive)
- 主动指纹(Active)
- 编写规范(Prepare a specification)
- 通过 ICON HASH 匹配
- 便捷计算icon hash的方式
- 使用内置函数自动搜索 ICON 路径
- 自定义 ICON 路径
- 通过内容MD5匹配
- 通过 Body 关键字匹配
- 基础模版(Basic templates)
- 匹配的字符串中包含引号(inverted comma)
- 忽略大小写(ignore capitals)
- 二进制内容匹配(binary)
- 通过 Header 关键字匹配
- Cookie 匹配
- Server 匹配
- 不常见的 Header Key 匹配(规则名称统一使用 kw_in_header)
- 判断某个 Key 是否在 Headers 中
- 在完整响应头中匹配(raw_header)
- 通过 CERT 关键字匹配
- 通过 404Path 进行特征匹配
- 多个 Context 匹配的简化写法
- 使用正则表达式进行匹配(RE)
- 使用正则提取信息(RE)