随着Ja3指纹应用范围越来越广,修改请求指纹以绕开防护逐渐成为需求。而由于大部分库和框架抽象化了TLS交互,使得修改不易。为此,笔者实现了一种基于HTTP代理的快速方案来解决这个问题。
简介
关于 TLS 指纹,最开始是 Lee Brotherston 在2015年的一次 talk 中提到的,同时给出了对应的工具。
而后 Salesforce 的安全研究员 John Althouse、Jeff Atkinson 和 Josh Atkins 三人在2017年一篇名为 TLS Fingerprinting with JA3 and JA3S 的博客文章中详细介绍了这项技术,并提供了一个开源工具,用于生成和分析tls指纹。
因为他们三人的名字都是ja开头,所以该指纹也被称为ja3指纹,对应的服务端指纹被称为ja3指纹。
计算
JA3
ja3设计思路主要基于这样一个事实:尽管加密流量的内容可能被隐藏,但SSL/TLS客户端和服务器在握手阶段的交互过程却是可见的。这个握手过程包含了大量的元数据,这些元数据可以被用来生成一个唯一的指纹,即JA3指纹。
具体来说,计算流程如下:
- 从 Client Hello 中提取TLS版本号,加密套件列表,扩展列表,以及椭圆曲线点格式列表(TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats)
- 将这些值转换为十进制,并用逗号分隔。
- 将这些值按照特定的顺序连接在一起,形成一个字符串。顺序是:SSL版本号,加密套件列表,扩展列表,椭圆曲线点格式列表(如果存在)。
- 对这个字符串进行MD5哈希,生成一个32位的哈希值。
其中TLS参数规范是由IANA(Internet Assigned Numbers Authority)维护的,可以在对应 站点 查看。
JA3S
JA3S是JA3的服务器端版本,用于识别和分类网络中的SSL/TLS服务器。与JA3类似,JA3S也是通过分析握手阶段的元数据来生成指纹的。不过,JA3S关注的是服务器在握手阶段发送的 Server Hello 包。
Server Hello 包中包含了SSL/TLS版本号、选定的加密套件和扩展列表等信息。JA3S通过收集这些信息,然后将它们按照特定的顺序连接在一起,形成一个字符串。接着,JA3S会对这个字符串进行MD5哈希,生成一个32位的哈希值,这就是JA3S指纹。
具体的计算流程如下:
- 从 Server Hello 包中提取SSL版本号,选定的加密套件和扩展列表。
- 将这些值转换为十进制,并用逗号分隔。
- 将这些值按照特定的顺序连接在一起,形成一个字符串。顺序是:SSL版本号,选定的加密套件,扩展列表。
- 对这个字符串进行MD5哈希,生成一个32位的哈希值。
JARM
不难想到,JA3S指纹并不是固定的,它会受到客户端在 Client Hello 消息中提供的信息的影响。这是因为在收到 Client Hello 后, 服务器会根据这些信息来选择一个共同支持的配置,并在 Server Hello 消息中进行确认。因此, Client Hello 消息中的内容会直接影响 Server Hello 消息中的选择。于是研究者们提出了 jarm ,同样开源了对应的工具 。
JARM使用预设的10个不同的 Client Hello 向服务器发起请求,记录服务器的响应,最后输出一个62个字符的字符串。其中前30个字符是服务器返回的 TLS 版本和 cipher,后32个字符是sha256哈希。这让JARM在一定程度上有模糊哈希的特征,可以通过前32个字符来聚类类似的服务器。
应用
Ja3指纹技术在识别爬虫、扫描器和攻击工具方面有着广泛的应用。通常情况下,爬虫代码会修改UA,但很少会修改TLS配置。因此,Ja3指纹技术可以轻松地识别这些爬虫。此外,攻击工具如特定版本的Burp也很容易被Ja3指纹技术识别,从而被WAF等防护设施拦截。除此之外,Jarm指纹技术还可以用于识别僵尸网络、矿池等通信。这些特性使得Ja3指纹技术成为一种非常有用的安全工具。
攻防对抗
以上是JA3指纹的科普部分,下面让我们聊聊攻防对抗的思路。
服务端修改
作为攻击者,C2服务器容易被识别。而对于防守方来说,蜜罐等服务器的特征也容易被识别。然而,在当前的网络架构中,大多数服务器前端都是CDN,客户端实际上是与非实际服务器的前端握手。因此,在大多数情况下,只需使用前置反向代理即可绕过JARM识别。
客户端修改
在客户端修改方面,有两种思路。第一种是将指纹修改为任意的指纹。在TLS交互中,cipher等配置都是有序的,因此在测试中往往对安全要求不高。也就是说,无论是交换cipher的优先级,还是在cipher中增加低优先级的废弃cipher配置,都可以容易地配置。这种方式的缺点是容易被聚类,但几乎可以绕过大部分黑名单机制。但是实际很少有防守方会将少见的工具加入到黑名单列表中。
第二种思路是将指纹修改为几乎不会被封的指纹,例如主流浏览器的指纹,大多数工具都采用了这种思路。
修改方案
相对于服务端的指纹修改方案,客户端的修改方案要复杂一些。如果是自研代码,可以相对容易地修改指纹,例如修改库的配置等。这种方式有开源实现可以参考,例如 Max Harley 等人提出了伪造 ja3 的方法 Impersonating JA3 Fingerprints 并给出了工具 ja3transport。对于前文提到的 burp 对抗,也有人给出了工具 Burp Awesome TLS 。
但是,如果是第三方工具,大多数不会暴露对应的接口,而TLS也没有很好的hook的方案。那么有没有一种合适的方案呢?如果不修改工具本身,修改请求的一个常见方案是代理。但是,无修改的socks代理由于会直接转发报文,所以不能实现这种能力。而HTTP代理则可以相对方便地劫持客户端HTTP连接,实际上客户端和代理握手,代理和目标服务器握手,从而实现了修改TLS指纹的目的。
本文基于golang做了一个简单的demo,相关代码已经开源,可以在这里查看。
参考链接
- https://twitter.com/synackpse
- https://blog.squarelemon.com/tls-fingerprinting/
- https://github.com/LeeBrotherston/tls-fingerprinting
- https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967/
- https://github.com/salesforce/ja3
- https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
- https://engineering.salesforce.com/easily-identify-malicious-servers-on-the-internet-with-jarm-e095edac525a/
- https://github.com/salesforce/jarm
- https://medium.com/cu-cyber/impersonating-ja3-fingerprints-b9f555880e42
- https://github.com/CUCyber/ja3transport
- https://github.com/sleeyax/burp-awesome-tls
- https://github.com/LyleMi/ja3proxy