JARM 指纹是一种被动的、客户端侧的 TLS 指纹识别方法,通过分析服务器或代理对一组特制 TLS ClientHello 探测包的响应,来识别其具体的 TLS 栈。JARM 由 Salesforce 开发,可为服务器的 TLS 实现生成唯一指纹,使运维人员能够识别特定的服务器软件、检测代理并追踪基础设施。
理解 JARM 指纹
JARM(Just Another Rather Marvelous)指纹识别的原理是向目标服务器发送 10 个不同的 TLS ClientHello 数据包。每个 ClientHello 在参数上都带有特定且有序的差异,例如支持的 TLS 版本、cipher suite、扩展和椭圆曲线。服务器对这些探测包的响应——包括其选择的 TLS 版本、cipher suite、扩展及其他握手参数——随后被拼接并做哈希处理,生成一个 62 字符的指纹。
该过程利用了不同 TLS 栈(例如 OpenSSL、Microsoft SChannel、Go 的 crypto/tls、BoringSSL、LibreSSL)在协商 TLS 握手时的细微差异。即便只是配置上的微小差别或补丁版本不同,也可能产生不同的 JARM 指纹。
JARM 探测包详解
这 10 个 ClientHello 探测包在多个维度上存在差异:
* TLS 版本: 探测包针对特定的 TLS 版本(例如 TLS 1.0、1.1、1.2、1.3)。
* Cipher suite: 每个探测包包含一份不同的 cipher suite 列表及排列顺序。
* 扩展: 使用不同的 TLS 扩展集合与顺序(例如 SNI、ALPN、renegotiation_info、supported_groups)。
* 椭圆曲线: 包含所支持椭圆曲线的多种变化。
服务器对这 10 个探测包中每一个返回的 ServerHello 响应都会被采集。从这些响应中提取的关键字段包括:
* 协商出的 TLS 版本
* 选定的 cipher suite
* 出现的扩展列表
* SNI 扩展响应的形式(如果有)
这些提取出的字段随后被拼接成一个字符串。该字符串接着经过 MD5 哈希,取其中一部分,再加上完整响应字符串的 SHA-256 哈希,共同构成最终 62 字符的 JARM 指纹。指纹的结构设计使其能够编码对这 10 个探测包各自响应的信息。
JARM 对代理服务为何有用
对于运营代理服务或与之交互的机构,JARM 指纹有多种实际用途。
服务器与软件识别
JARM 能够精确识别服务器或代理底层使用的 TLS 库及其配置。这使得以下工作成为可能:
* 软件分类: 区分 Nginx、Apache HTTP Server、Microsoft IIS、Caddy 等常见 Web 服务器,或 Tomcat、Jetty 等应用服务器。
* 版本检测: 由于 TLS 栈实现的变化,同一软件的不同版本或补丁级别往往会产生不同的 JARM 指纹。
* 配置错误检测: 出现意料之外的指纹,可能意味着非标准配置或潜在的安全问题。
代理检测与刻画
对代理服务而言,识别中间代理对于理解网络路径和安全态势至关重要。
* 反向代理与 CDN: 当客户端连接到位于反向代理、负载均衡器或内容分发网络(CDN)之后的服务器时,JARM 指纹反映的是代理/CDN 边缘节点的 TLS 栈,而非源站服务器。由此可以识别 Cloudflare、Akamai、AWS CloudFront,或充当反向代理的 Nginx/HAProxy 等服务。
* 正向代理: 正向代理在进行 TLS 拦截(MITM)时可能呈现自己的 TLS 栈。如果客户端的连接被拦截,客户端得到的 JARM 指纹属于该正向代理,而非目标站点。不做拦截的正向代理通常会原样转发 ClientHello,从而保留源站服务器的 JARM。
* WAF(Web 应用防火墙): 许多 WAF 以反向代理方式运行,会呈现自己的 JARM 指纹。
安全与威胁情报
JARM 是安全运营中的重要工具:
* 恶意软件 C2 识别: 威胁行为者常在其命令与控制(C2)基础设施上复用特定的 TLS 栈。JARM 可为这些 C2 服务器生成指纹,从而在不同 IP 地址或域名之间对其进行识别和追踪。
* 基础设施测绘: 安全研究人员利用 JARM 梳理攻击者的基础设施、识别共用组件,并将看似互不相关的攻击活动关联起来。
* 钓鱼检测: 若合法域名出现异常的 JARM 指纹,可能说明攻击者正使用不同基础设施发起钓鱼攻击。
网络可见性
JARM 让人更深入地了解代理所处的 TLS 生态:
* 建立基线: 为已知服务和合作方建立预期 JARM 指纹的基线,有助于发现异常。
* 服务发现: 无需应用层交互即可识别服务所使用的底层技术。
生成 JARM 指纹
生成 JARM 指纹需要发送特定探测包并处理响应。已有相应的工具和库可用。
示例:使用 Python jarm 库
jarm Python 库提供了生成指纹的简便方式。
import jarm
def get_jarm_fingerprint(host, port=443):
"""
为给定的主机和端口生成 JARM 指纹。
"""
try:
fingerprint = jarm.string_fingerprint(host, port, timeout=5)
return fingerprint
except Exception as e:
return f"Error generating JARM for {host}:{port}: {e}"
if __name__ == "__main__":
target_host_1 = "www.example.com"
target_host_2 = "cloudflare.com" # 通常由 Cloudflare 自身的 TLS 栈承接
target_host_3 = "nginx.org"
print(f"JARM for {target_host_1}: {get_jarm_fingerprint(target_host_1)}")
print(f"JARM for {target_host_2}: {get_jarm_fingerprint(target_host_2)}")
print(f"JARM for {target_host_3}: {get_jarm_fingerprint(target_host_3)}")
该脚本会连接到指定的主机和端口,发送 JARM 探测包,并打印出得到的 62 字符指纹。
解读 JARM 指纹
JARM 指纹本身只是一串不透明的字符。它的价值来自与已知服务或威胁行为者的比对和关联。
* 数据库查询: 公开和私有数据库(例如 JARM.info、Shodan、Censys)会收集 JARM 指纹,并将其与已知软件、服务和机构关联起来。
* 模式识别: 特定的 JARM 指纹往往与特定的 TLS 库或服务器软件相对应。例如:
* 27d40d40d27d40d00045d40d00041d01dd2d4000000000494a000000000000 是 Nginx 常见的指纹。
* 07d14d14d07d14d00041d14d0000000d1d2d00000000000000000000000000 常见于 Apache HTTP Server(OpenSSL)。
* 07d07d07d07d07d00041d07d0000000d1d2d00000000000000000000000000 可能表示 Cloudflare。
* 异常检测: 已知服务的指纹一旦偏离预期,就值得深入排查。
JARM 与代理行为
JARM 与代理服务之间的相互作用,是其价值的核心所在。
正向代理
- 不做拦截的正向代理: 这类代理只是把客户端的 ClientHello 转发给目标服务器。在这种情况下,客户端观察到的 JARM 指纹(假如客户端为该服务器生成 JARM)将是源站服务器的指纹。不过,即便代理服务本身作为客户端为某个目标生成 JARM,得到的仍然是源站服务器的 JARM。
- 进行拦截的正向代理(TLS 检查): 当正向代理执行 TLS 检查时(例如用于安全扫描或策略执行),它就充当中间人(MITM)。它会终止客户端的 TLS 连接,并与源站服务器另建一条连接。此时客户端观察到的 JARM 指纹是该正向代理 TLS 栈的指纹;同样,源站服务器观察到的 JARM 指纹也是该正向代理 TLS 栈的指纹。这一行为是检测 TLS 检查的关键线索。
反向代理、负载均衡器与 CDN
这类服务位于一个或多个源站服务器之前,负责处理进入的客户端连接。
* 客户端连接到由反向代理、负载均衡器或 CDN 承接的服务时,得到的 JARM 指纹始终反映该中间层的 TLS 栈。
* 源站服务器的 JARM 指纹对外部观察是被屏蔽的。这正是识别此类服务的主要机制。例如,连接 google.com 得到的是 Google 边缘基础设施的 JARM 指纹,而不是实际处理请求的后端服务器的指纹。
局限与注意事项
JARM 虽然强大,但也有局限:
* 主动探测: JARM 需要向目标发送主动探测包,这在某些环境中未必可行或被允许。
* 动态变化: 服务器配置、软件更新或负载均衡器变更都可能改变 JARM 指纹,因此需要持续监控并重新采集指纹。
* 伪造: 老练的攻击者可以把自己的 TLS 栈配置成模仿已知的 JARM 指纹,不过这需要专门的投入。
* 覆盖范围: JARM 只捕获 TLS 握手的特定方面,无法呈现全部 TLS 配置细节的完整图景。
JARM 与 JA3 的对比
JARM 和 JA3 都是 TLS 指纹识别技术,但用途不同。
| 特性 | JARM | JA3 |
|---|---|---|
| 目标 | 服务器(识别服务器或代理的 TLS 栈) | 客户端(识别客户端应用的 TLS 栈) |
| 方法 | 发送 10 个特制的不同 ClientHello 探测包,并对服务器的 ServerHello 响应做哈希。 | 分析客户端发送的单个 ClientHello 数据包,提取 TLS 版本、cipher suite、扩展、椭圆曲线和椭圆曲线点格式的有序列表,然后做哈希。 |
| 指纹类型 | 由多个 ServerHello 响应推导出的 62 字符字符串。 | 拼接字符串的 MD5 哈希(例如 TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats)。 |
| 适用场景 | 识别服务器软件、检测代理/CDN、追踪 C2 基础设施、网络测绘。 | 识别特定客户端应用(例如浏览器、恶意软件、IoT 设备)、检测僵尸网络、应用画像。 |
| 观测位置 | 需要由客户端主动向目标服务器发起连接。 | 需要服务器或网络设备观察来自客户端的 ClientHello。 |
| 来源 | Salesforce(2020) | Salesforce(2017) |
JARM 聚焦于服务器的响应行为,因此非常适合识别终止 TLS 连接的各类实体,包括代理服务。
