相关定义
被动指纹(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插件修复手册
当存在多个‘或’逻辑的时候,系统会按序执行,直到命中一个才会停止,所以,将常命中的规则放在越靠前的位置能够有效的减少不必要的发包
3. 如无特殊需要,不编写主动指纹,避免请求量爆炸
被动指纹指的是只向根路径发包,或者只使用获取icon的函数进行指纹识别的指纹,而除了这两种情况,其他的都属于主动指纹。
4. 在使用关键字进行匹配时,要求使用包含产品特征的关键字,而且尽可能长,避免误报
例如匹配discuz的时候,关键词中最好带有该产品的特殊的内容,例如discuz_uid等。
特别的:部分通用系统存在二次开发修改title的情况,因此title只能作为辅助判断的逻辑,只能算作加强规则,不能作为决定性证据
5. 当有多条被动指纹规则时,尽量使用&&组合两条规则,减少单条规则容易造成的误报
一般来讲,符合第四点的情况下,两条规则即可
6. 如无特殊需要,指纹规则中不带攻击特征,避免被 WAF 拦截
一般有两种情况,一种是spring的访问/env等路径,有些时候会被拦;一种情况是path中带有admin,有些waf会拦截
7. 优先使用 contains、其次 icontains,减少运行过程中不必要的资源消耗
icontains在匹配时会忽略匹配字母的大小写,增加资源消耗
8. 在处理所有的 404 页面时统一使用get404Path()函数获取路径
get404Path函数做了一些特殊处理,这个函数可以保证一个目标将仅拥有一个404path,这样便于使用cache,降低发包,提高效率。 9. 如果能自动获取 icon, 尽量通过 getIconContent 来自动获取图标内容
getIconContent函数可以在响应中获取icon地址,然后访问这个icon获取其字节流数据。 10. 取特征时,不要在header头Set-Cookie中取
11. 尽量使用推荐的规则名称,当有多个同类规则时使用数字隔开
例如:kw_in_body_01、kw_in_body_02
12. 当存在多个版本匹配的规则的时候,建议使用payloads进行编写,提高插件的可阅读性,必要时可以使用三目运算符做判断
详情参考:三目运算符的使用方式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 匹配
便捷计算icon hash的方式
将下列代码放入powershell中运行,请确保你对应版本的python有安装requests,mmh3包
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])))"
}
使用示例: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匹配
rules:
md5_in_body:
request:
method: GET
path: /this_is_test_path
expression: |-
md5(response.body) == "ce1a1c8754948c6cbfcfa48545e8174b"
通过 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')
建议规则名称: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/")
rules:
kw_in_header:
request:
method: GET
path: /
expression: |-
response.headers["X-Protected-By"].contains("OpenRASP")
rules:
kw_in_header:
request:
method: GET
path: /
expression: |-
"CF-RAY" in response.headers
rules:
kw_in_header:
request:
method: GET
path: /
expression: |-
response.raw_header.bcontains(b'HP-ChaiSOE')
通过 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"]