通过 JA3(S) 实现 TLS 指纹识别

前言

HW期间的空余时间在看 Suricata 的 Docs, 其中提到了 Suricata 集成了 JA3,JA3 用于识别 TLS/SSL 客户端的指纹信息,这些指纹信息可用于威胁情报。

我之前在使用 Suricata 时从未用过这个关键词,所以比较好奇,来学习一下。

JA3 实际是一个开源项目,对应的 Github 路径为:https://github.com/salesforce/ja3,其README中提到了 John Althouse 前辈的一篇Blog《TLS Fingerprinting with JA3 and JA3S》,这篇文章写的很清晰,我们来看一看。


TLS/SSL 指纹信息

在了解 JA3/JA3S 之前,我们需要先了解下什么是 TLS/SSL 指纹信息。

正常的应用会通过TLS/SSL对通信流量进行加密,以确保数据安全传输; 恶意软件也会对其通信流量进行TLS/SSL加密,以此来躲避检测。

为了建立TLS/SSL连接,客户端与服务端之间会先完成TCP三次握手,建立可靠的通信; 然后进入TLS/SSL握手阶段,客户端会发送 Client Hello 请求数据包,如果服务端同意建立TLS/SSL连接,则会根据请求数据包和服务端配置生成并发送 Server Hello响应数据包。

举个例子,我们访问 https://www.csdn.net 并捕获相关TLS/SSL数据包。

从上图中我们可以看到,一个完整的TLS/SSL握手协议阶段有以下这么几个过程。

  1. 客户端发送 Client Hello 请求数据包。

  2. 服务端返回 Server Hello 响应数据包,包含 Server HelloCertificateServer Key ExchangeServer Hello Done信息。

    Server Hello : 基本信息(TLS版本号信息、随机数信息、SessionID信息、支持的密码套件信息等)。
    Certificate : 证书信息。
    Certificate Status : 用于校验证书吊销状态。
    Server Key Exchange : 密钥协商信息(例如要使用DH密钥协商,那么就返回DH参数和公钥)。
    Server Hello Done : 标明 Server Hello 结束。

  3. 客户端返回 Client Key Exchange 数据包,包含 Client Key ExchangeChange Cipher SpecEncrypted Handshake Message信息。

    Client Key Exchange : 使用服务端提供的参数完成密钥协商。
    Change Cipher Spec : 标明客户端的加密参数都准备好了,可以对数据进行加密,并将连接状态修改为可读可写状态。
    Encrypted Handshake Message : 校验所有的握手消息未被篡改,至此 TLS/SSL 握手完毕。

上面大概的介绍了一下 TLS/SSL 的握手流程,如果想要进一步了解细节,可以参考 大力海棠 前辈的《抓HTTPS报文分组来分析TLS/SSL协议》一文,写的非常详细。

回顾上面的握手流程,我们会发现:TLS/SSL协商的过程是明文传输的,并未被加密; 因此我们可以使用 TLS Client Hello 数据包中的详细信息对客户端应用程序进行指纹识别。


为什么是 Client Hello

TLS/SSL协商的整个过程都是明文传输的,那么为什么仅选择 TLS Client Hello 数据包来用于指纹识别呢?

Lee Brotherston 前辈在《TLS fingerprintingSmarter Defending & Stealthier Attacking》一文中给出了答案。

原因主要有以下三点:

  • TLS Client Hello 数据包是 TLS/SSL 握手协商过程的第一个数据包,如果想要对整个数据流进行后续操作( 比如流量阻断或者协议欺骗等 ),选择第一个数据包进行分析判定无疑是最好的。

  • 在很多资源受限或者非对称路由的情况下,采集数据包的探针可能会丢包。而此时无论探针监听哪个端口的数据,都可以快速判断是否为 TLS Client Hello数据包并捕获他们。

  • TLS Client Hello 数据包产生的次数较少(只在 TLS/SSL 握手协商时会产生),且容量最小; 因此能够以最小的代价捕获并存储网络通信中所有的 TLS Client Hello数据包。


JA3 计算原理

我们先从 Github 上拖一份 JA3 工具来测试一下,以我们之前捕获的csdn_https.pcap为例,来看一下输出信息。

其中不仅有一些数据包的基本信息(源目IP地址、源目端口号等),还有两个信息的键值对 : ja3ja3_digest

  • 先来说说 ja3 这个键值是怎么计算的

    在计算该键值时,ja3会从TLS Client Hello报文中获取一些字段与字段值,不同的字段之间以逗号()拼接,同一个字段不同字段值之间以间隔符(-)拼接,字段值以十进制的方式保存。

    提取信息的字段有:

    • TLSVersion : TLS版本信息

    • Ciphers : 支持的密码套件

    • Extensions : 支持的扩展项

    • Elliptic_Curves / Supported Groups : 支持的椭圆曲线类型

      值得一提的是:Elliptic_CurvesSupported Groups实际上是同一个东西,只是在不同的TLS版本中名称不同。在 RFC-8446 中有提到这一点:

    • EC_Point_Formats : 是否对椭圆曲线参数进行压缩

    查看上面 csdn_https.pcap 第一个Client Hello的报文详情,其对应信息如下:

    1. TLSVersion : 771

    2. Cipher : 4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53-10

    3. Extensions : 0-23-65281-10-11-35-16-5-51-43-13-45-28-21

    4. Supported Groups : 29-23-24-25-256-257

    5. EC_Point_Formats : 0

    将上面这些转换后的数据拼接起来,就是JA3的键值。

    JA3 == 771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0

  • 再来看下ja3_digest的键值是如何计算的

    ja3_digest的键值长度为 32 位,那么很有可能就是ja3字符串的MD5校验和了,来看下是不是这样的。

    可见确实是这样的:

    JA3_DIGEST == aa7744226c695c0b2e440419848cf700

    关于为什么使用ja3_digest与为什么使用MD5校验和,John Althouse前辈在文中也有说明:

    简单来说原因有如下两点:

    • MD5校验和长度固定,便于标注和存储。
      由于Client Hello中的Cipher(支持的密码套件)和Extensions(支持的扩展项)可能很多且数量无法确定,如果仅使用ja3来标注或存储,则字符串可能很长; 而MD5检验和的长度固定为32位,用起来比较方便。

    • MD5使用范围广,不存在兼容性问题。
      John Althouse前辈曾尝试使用模糊哈希(Fuzzy Hashing)来替代MD5校验和,但由于很多较老的设备不支持模糊哈希的计算,因此最终没有选择使用它。

总结一下,JA3是从Client Hello数据包中提取的指纹信息,而JA3_DIGEST是这组指纹信息的MD5校验和。


JA3S 计算原理

JA3S 实际上就是从Server Hello数据包中提取的指纹信息。来看一看其对csdn_https.pcap的分析结果。

结果中同样存在ja3(S)ja3(S)_digest这两个键值对,但是ja3(S)键值的内容和前文不同,Server Hello数据包中提取的ja3(S)由三个部分组成。

  • TLSVersion : TLS版本信息
  • Ciphers : 支持的密码套件
  • Extensions : 支持的扩展项

下面以csdn_https.pcap的第一个Server Hello数据包,其对应信息如下。

  1. TLSVersion : 771

  2. Cipher : 49199

  3. Extensions : 0-65281-11-35-5-16

将上面这些转换后的数据拼接起来,就是JA3(S)的键值。

JA3(S) == 771,49199,0-65281-11-35-5-16

同理,JA3(S)_DIGESTJA3(S)指纹信息的MD5校验和。

JA3(S)_DIGEST == 76cc3e2d3028143b23ec18e27dbd7ca9

总结一下,JA3(S)是从Server Hello数据包中提取的指纹信息,而JA3(S)_DIGEST是这组指纹信息的MD5校验和。


JA3(S) 的使用原理

在上文中我们提到了JA3(S)的计算方法,那么为什么JA3(S)的计算结果能够用于识别 TLS/SSL 指纹信息呢?

这其实源于John Althouse的一个研究结果:同一个服务器对同一个客户端的多次请求返回相同的响应信息。

可以通过以下一些Demo来验证上述理论。

  • 一些RAT工具在创建自定义的TLS/SSL加密通信时,通常会指定专门的Ciphers,比如PupyRAT中就会指定一个特殊的 Ciphers,这使得生成的JA3信息中存在某一段特征。

  • 如果是某些使用公开库或者OS Socket,则可以使用JA3(S)信息来识别通信行为。

    以Windows10平台为例,如果一个程序通过WinSocket来建立TLS/SSL连接,那么通常会存在以下计算结果。

    • 与IP地址建立TLS/SSL连接 : JA3_Digest == 72a589da586844d7f0818ce684948eea

    • 与域名地址建立TLS/SSL连接 : JA3_Digest == a0e9f5d64349fb13191bc781f81f42e1

    这里的JA3_Digest是通用的; 也就是说,无论是恶意程序客户端还是正常应用程序,都会得到这一个计算结果。因此仅通过JA3JA3_Digest, 不足以将恶意程序客户端区分出来。

    安全研究人员发现,根据C2服务器返回的Server Hello数据包信息生成的JA3(S)是独特的。

    例如 MetaSploit-Framework.MeterpreterCobaltStrike.Beacon 在使用 WinSocket 进行TLS协商时,生成的JA3(S)就是独特且相对固定的。

    1. MetaSploit-Framework.Meterpreter From Windows10 to Kali-Linux

      JA3_Digest == 72a589da586844d7f0818ce684948eea | a0e9f5d64349fb13191bc781f81f42e1

      JA3(S)_Digest == 70999de61602be74d4b25185843bd18e

    2. CobaltStrike.Beacon From Windows10 to Kali-Linux

      JA3_Digest == 72a589da586844d7f0818ce684948eea | a0e9f5d64349fb13191bc781f81f42e1

      JA3(S)_Digest == b742b407517bac9536a77a7b0fee28e9

    根据这个相对独特的JA3(S)信息,就能快速判断某一个TLS/SSL数据流是否是恶意程序发起的。

    当然,这种判断方法存在一定程度的误报,因为正常的应用程序也有可能使用这样的JA3(S);,John Althouse前辈也在文中提到了这一点。

    事实上,我们可以把JA3(S)信息当作User-Agent字符串来使用; 如果同时对JA3信息和JA3(S)信息进行判断,那么检测结果的误报率将会大大降低。


Suricata-JA3 使用方法及 Demos

Suricata5.0.6中,有以下关键词可用于支援JA3(S)

  • ja3.hash : 匹配 JA3 指纹信息的 MD5 校验和。

  • ja3.string : 匹配 JA3 指纹信息。

  • ja3s.hash : 匹配 JA3(S) 指纹信息的 MD5 校验和。

  • ja3s.string : 匹配 JA3(S) 指纹信息。

此外,在使用Suricata-JA3之前,需要在suricata.yaml中开启对JA3的支持。


Demo1 : 识别 csdn_https 通信

通过JA3.py跑一下JA3_Digest信息,然后拿ja3.hash关键字就能直接匹配。


Demo2 :识别 CobaltStrike Https Beacon(Based On Windows7) 通信

拿默认的CobaltStrike HTTPS Beacon为例。

捕获对应Pcap流量包,并计算其JA3JA3(S)

根据获取的信息编写规则,即可命中相关通信流量。


Demo3 :识别 MSF Meterpreter/reverse_https(Based On Windows7) 通信

指定通信Payload为windows/x64/meterpreter/reverse_https,然后建立连接。

捕获对应的Pcap数据包,计算其JA3JA3(s)值,由于TLS/SSL的通信并不是默认端口(443),因此需要指定 -a 参数。

根据获取的信息编写规则,即可命中相关通信流量。


总结

第一次接触 JA3 / JA3(S) ,感觉还是蛮好用的。之后在搞规则时可以多用用,深入学习一下。

上面讲的东西只是一个基础概念,有兴趣的师傅可以参考一下 Fingerprinting Encrypted Channels with Bro for High Fidelity Detection,讲的非常有意思~

评论

  1. 2h0ng
    3周前
    2021-9-06 15:52:58

    好文章,感谢认真严谨的分享

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇