TLS指纹模拟

深蓝实验室@lz520520

介绍

https等协议通过tls加密传输,大家可能觉得数据被全加密了应该可以保证无恶意流量特征,流量侧通信不会被发现,但在加密前,会有一个tls协商过程,client和server协商交互才会生成最后加密用的密钥,而这个协商过程就可以切切实实进行指纹提取。这就是tls指纹。

JA3是一种创建SSL/TLS客户端指纹的方法,它易于在任何平台上生成,并且可以轻松共享以获取威胁情报。JA3 由三位 Salesforce 成员(John Althouse、Jeff Atkinson 和 Josh Atkins)开发,是一种用于根据 ClientHello 数据包生成 SSL 指纹以识别建立加密连接的客户端的技术。相同的客户端工具所产生的JA3指纹总是一致的。JA3 指纹从一开始就是为了证明客户端应用程序是否存在恶意。

我们在使用burpsuite等工具时,经常会遇到网站无法打开,去掉代理又变正常的情况,很可能是网站的WAF使用了JA3指纹识别到了代理工具。常见的如cloudfare waf就使用了该指纹技术。

go默认ja3指纹89be98bbd4f065fe510fca4893cf8d9b

chromeja3指纹cd08e31494f9531f560d64c695473da9

分析

ja3

tls指纹生成过程

JA3方法用于收集Client Hello数据包中以下字段的十进制字节值:版本、可接受的密码、扩展列表、椭圆曲线密码和椭圆曲线密码格式。然后,它将这些值串联在一起,使用“,”来分隔各个字段,同时,使用“-”来分隔各个字段中的各个值。

版本、可接受的密码、扩展列表、椭圆曲线密码和椭圆曲线密码格式
SSLVersion,Cipher-Suites,SSLExtension,EllipticCurve,EllipticCurvePointFormat

例子

769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0

然后使用MD5 hash生成指纹

ada70206e40642a3e4461f35503241d5

具体分析

这里以go开发的frp为例

Client Hello数据包如下,ja3就是由以下圈出来的部分生成的

SSLVersion

这里显示是 TLS1.2,对应实际值是0x0303

转换成十进制为

771

Cipher-Suites

如下加密算法套件有19个,根据ja3规则,每个之间用"-"连接

转换成果如下

49199-49200-49195-49196-52392-52393-49171-49161-49172-49162-156-157-47-53-49170-10-4865-4867-4866

SSLExtension

这个扩展列表是由所有扩展类型值组成,如下扩展类型值有8个

生成结果为

5-10-11-13-65281-18-43-51

EllipticCurve

椭圆曲线密码

即扩展type值为10对应的数据,这里写的是supported_group

生成结果为

29-23-24-25

EllipticCurvePointFormat

椭圆曲线格式

即扩展type只为11对应的数据,这里数据值为0

生成结果为

0

最后将上面所有部分用","拼接组成最终需要hash的字符串

771,49199-49200-49195-49196-52392-52393-49171-49161-49172-49162-156-157-47-53-49170-10-4865-4867-4866,5-10-11-13-65281-18-43-51,29-23-24-25,0

进行hash计算如下

通过ja3工具解析数据包结果,可以看到和我们计算结果一致

ja3s

JA3S方法会收集Server Hello数据包中以下各个字段的十进制字节值:版本、可接受的加密算法和扩展列表。然后,它将这些值串联在一起,使用“,”来分隔各个字段,并通过“-”来分隔每个字段中的各个值。

SSLVersion,Cipher,SSLExtension
769,47,65281–0–11–35–5–16

PS: 因为Server Hello会因为Client Hello不同和不同,所以只能做一个参考。

计算

FRP的Server Hello数据包如下,计算需要的三部分如下

SSLVersion

版本

生成结果

771

Cipher

可接受的加密算法,只有一个

生成结果

4865

SSLExtension

扩展列表,扩展列表只有两个

根据类型值,生成结果

43-51

将上述拼接起来

771,4865,43-51

但ja3s工具生成,加密套件这块额外添加了参数实际算法名称

如下,但实际表示意义也是唯一的

771,CipherSuite(0x1301, TLS_AES_128_GCM_SHA256),43-51

绕过

文章

https://mp.weixin.qq.com/s/og2IKo8lcydh8PROUPD7jQhttps://segmentfault.com/a/1190000041699815

https://github.com/refraction-networking/utls

这个库是基于crypto/tls进行开发的,可以模拟绝大部分情况下的ja3指纹。

和官方库差不多的用法,如下在封装时除了conn连接和config配置以外,需要传入ClientHelloID,这个ID是有一些内置的指纹可以直接调用,或者也可以自定义。

conn, _ := net.DialTimeout("tcp", "121.14.77.201:443", 10*time.Second)
uConn := tls.UClient(conn, &tls.Config{ServerName: "www.qq.com", InsecureSkipVerify: true}, tls.HelloChrome_102)
uConn.Write([]byte("aaa"))

这是官方库

u_common.go,如下有非常全的现成指纹信息可以用。

这些ID最终对应到这个函数utlsIdToSpec里

后续可参考这里编写实现自己的。

并且该库还支持解析数据包中的client hello信息,来自动化构建一个自定义参数,实现模拟各种ja3指纹

浏览器访问,然后抓包找到client hello包,选中tls层的数据,然后复制成hex stream即可。

将tls的hex数据粘贴到以下位置,通过fingerprinter.FingerprintClientHello即可解析生成一个自定义spec,封装到tlsConn里直接使用。

要注意的是ClientHelloID还设置成HelloCustom即自定义

conn, _ := net.DialTimeout("tcp", "121.14.77.201:443", 10*time.Second)
rawCapturedClientHelloBytes, _ := hex.DecodeString("1603010200010001fc03037741eebedfb7afbcfcd0f49f9b59d7a9c13eb3ccd8f207b8c692ffb1b9b9f5c22017bd40c3ec96ca8c21df97de564ce5e4e88bc945ca902d7d4260f77fb980631400221a1a1301130213021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035010001918a8a000000000012001000000d7370312e62616964752e636f6d00170000ff01000100000a000a0008dada001d00170018000b00020100002300a00efd73f80bf561e25ea122de025cb65678ebfcd201e8c49325fbabe586918cdb8cfdeaac64d4798b351295c62d94aa3c48a8f4181bee25d4202025cbf7eaf074233d576018c8adfe0d4527daa496e1b05162c0490a00fb108522a31e0bf369482a97a77d62f147f1657e927b45223545e7ad54f99239d820ed81b41c172a15dc3762f5d8fd1d333e082f55daca4e38ae11456fa4caf6be4419b56e5ed36a08580010000e000c02683208687474702f312e31000500050100000000000d0012001004030804040105030805050108060601001200000033002b0029dada000100001d0020ecb49f62fef0dc89ff3e6084d99e39b27820e68d9c4d8bf24b6d367e286ce05a002d00020101002b000706baba03040303001b0003020002446900050003026832caca0001000015002800000000000000000000000000000000000000000000000000000000000000000000000000000000")
uConn := tls.UClient(conn, &tls.Config{ServerName: "www.qq.com", InsecureSkipVerify: true}, tls.HelloCustom)
fingerprinter := &tls.Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
t.Fatalf("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
t.Fatalf("applying generated spec failed: %v", err)
}
uConn.Write([]byte("aaa"))

可以看到完全一致(wireshark好像3.6以上就支持ja3的指纹生成了)

PS: 要注意一点,SNI也会参与计算,如果ServerName为空会不插入Extension里,导致ja3指纹计算结果不一样

这个库还有些其他玩法,可以自行参考文档或者他的example.go

存在小bug,如果自动握手,可能会出现握手失败的问题,建议手动握手

uConn := tls.UClient(c, tlsConfig, tls.HelloChrome_Auto)
// 错误姿势,如此可能会报错
uConn.Write([]byte("a"))
// 正确姿势

err := uConn.Handshake()
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值