JA4+ 的强大之处在于,它将分析焦点从“数据包在说什么”转向了“数据包是如何构建的”。在一切都加密、攻击者在几秒内就能轮换IP和域名的时代,JA4+为网络流量提供了稳定的指纹。通过将TCP和TLS握手转换为模块化的 $a\_b\_c$ 格式,它使我们能够识别连接背后的特定软件、库或工具。即使攻击者更改了加密密钥或隐藏在新的代理后面,指纹识别仍然有效。JA4+本质上是将看似随机的网络数据噪声,转化为清晰、可搜索且易于人类阅读和机器追踪的特征。再加上Zeek强大的检测引擎,实时威胁检测成为可能。
今天,我们正在实验一个独立的Zeek实现以及个人编写的Python脚本,以查找已知恶意JA4+指纹的可能匹配项。这是一项正在进行中的工作,Zeek的连接ID(UID)是这些脚本的核心。有望更快、更简洁地揭示额外的威胁情报。
更多信息请参阅FoxIO的GitHub:github.com/FoxIO-LLC/j…
以下是一些初始数据,您可能觉得重要:
thedr@gallifrey:~/zeek$ zeek -v
zeek version 8.0.4
thedr@gallifrey:~/zeek$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
thedr@gallifrey:~/zeek$ tshark -v
TShark (Wireshark) 4.6.2.
2023-10-03-Pikabot-infection-with-Cobalt-Strike.pcap
此Zeek实例以独立模式运行。为了让Zeek正常运行,我们需要安装Tshark。我将会发布另一篇文章,展示如何通过CLI在新的虚拟机上安装Zeek和Wireshark。Zeek没有GUI。当Zeek处理一个PCAP文件时,它会根据PCAP中观察到的流量输出到一个文件夹中的多个文件。
Zeek对 2023–10–03-Pikabot-infection-with-Cobalt-Strike.pcap 的输出
我将展示一个从我追踪相关恶意软件最喜欢的网站获取的文件:Malware-Traffic-Analysis.net
请注意!这是真实的恶意软件。除非您清楚自己在做什么,否则切勿导出或操作此PCAP文件。
[图片:2023–10–03 Pikabot infection with Cobalt Strike]
我选择了 2023–10–03-Pikabot-infection-with-Cobalt-Strike.pcap,因为我确实知道Cobalt Strike有多个JA4+指纹。这是一个很好的示例。
让我们实时运行它。
thedr@gallifrey:~/pcap/pikabot$ zeek -r ../2023-10-03-Pikabot-infection-with-Cobalt-Strike.pcap ../../zeek/
## ../xxxxx 告诉shell请查找上一个父目录
## ../../xxxx 请回溯2层目录
thedr@gallifrey:~/pcap/pikabot$ ls
conn.log files.log ldap.log packet_filter.log ssl.log
dce_rpc.log http.log ldap_search.log smb_files.log weird.log
dns.log kerberos.log ocsp.log smb_mapping.log x509.log
现在,您看到了Zeek为我们创建的所有不同的日志文件。这就是Zeek如此特别的原因。它将所有重要信息分组汇总。现在轮到我自己开发的Python脚本出场了,它不仅可以提取已处理的JA4+指纹,还可以将其与我的本地数据库进行比较。
thedr@gallifrey:~/pcap/pikabot$ python3 ~/petes_python/ja4_threat_hunter_v7.py
================================================================================
[*] Analysis Complete
[*] Total connections checked: 280
[*] Malicious connections detected: 203
================================================================================
================================================================================
Finding #1
================================================================================
Connection ID (UID): Clj9x532tezTvr9dbe
Log Source: ssl.log
Timestamp: 2023-10-03 16:11:39 UTC
[PRIMARY DETECTION]
JA4S Match: t120300_c030_52d195ce1d92
Application: Cobalt Strike v4.9.1 beacon
Notes: Cobalt Strike v4.9.1 over winhttp
[RELATED DATA]
JA4T: 65535_2-1-3-1-1-4_1460_8 (from conn.log)
JA4TS: 64240_2-1-1-4-1-3_1396_7 (from conn.log)
[CONNECTION DETAILS]
10.1.2.147:49388 -> 179[.]60.149[.]244:443
这是 ja4_threat_hunter_v7.py。这实际上是一个全面的JA4+威胁狩猎工具,覆盖了整套JA4指纹(TLS、HTTP、SSH、X.509和TCP),而不仅仅是SSL/TLS指纹。数据库 /home/thedr/db/ja4+_db.json 包含了我们想要检测的已知恶意指纹(ja4_fingerprint、ja4s_fingerprint、ja4h_fingerprint 等)。
Zeek上的JA4X目前无法工作。 我使用了FoxIO提供的Python脚本中的 JA4.py,从Zeek出发进一步识别额外的JAX证书基础设施。
这不是一个实时数据库。我从 Ja4db.com 下载了文件。未来,我将修改脚本以使用API,以便所有潜在的指纹都能使用最新的威胁情报。
thedr@gallifrey:~/pcap/pikabot$ cat ~/petes_python/ja4_threat_hunter_v7.py
#!/usr/bin/env python3
"""JA4 Threat Hunter v4 - Compare Zeek JA4 fingerprints against known database
Works with FoxIO-style database format (array of objects)
Usage: python3 ja4_threat_hunter_v4.py /path/to/zeek/logs
"""
import json
import gzip
import sys
import os
from pathlib import Path
from collections import defaultdict
from datetime import datetime
# JA4 field mappings for each log type
# Primary fingerprints (can trigger detections)
PRIMARY_FIELDS = {
'ssl.log': ['ja4', 'ja4s'],
'http.log': ['ja4h'],
'ssh.log': ['ja4ssh'],
'x509.log': ['ja4x']
}
# All other logs that contain related data
RELATED_LOGS = {
'conn.log': ['ja4t', 'ja4ts'], # TCP fingerprints - always related to any connection
'ssl.log': ['ja4', 'ja4s'], # TLS fingerprints
'x509.log': ['ja4x'] # Certificate fingerprints
}
def load_malicious_db(db_path):
"""Load the malicious JA4 database (FoxIO format - array of objects)"""
try:
with open(db_path, 'r') as f:
db_array = json.load(f)
# Convert array format to lookup dictionaries for fast searching
fingerprint_db = {
'ja4': {}, 'ja4s': {}, 'ja4h': {},
'ja4ssh': {}, 'ja4x': {}, 'ja4t': {}, 'ja4ts': {}
}
for entry in db_array:
if entry.get('ja4_fingerprint'):
fingerprint_db['ja4'][entry['ja4_fingerprint']] = entry
if entry.get('ja4s_fingerprint'):
fingerprint_db['ja4s'][entry['ja4s_fingerprint']] = entry
if entry.get('ja4h_fingerprint'):
fingerprint_db['ja4h'][entry['ja4h_fingerprint']] = entry
if entry.get('ja4ssh_fingerprint'):
fingerprint_db['ja4ssh'][entry['ja4ssh_fingerprint']] = entry
if entry.get('ja4x_fingerprint'):
fingerprint_db['ja4x'][entry['ja4x_fingerprint']] = entry
if entry.get('ja4t_fingerprint'):
fingerprint_db['ja4t'][entry['ja4t_fingerprint']] = entry
if entry.get('ja4ts_fingerprint'):
fingerprint_db['ja4ts'][entry['ja4ts_fingerprint']] = entry
return fingerprint_db
except FileNotFoundError:
print(f"[!] Database not found: {db_path}")
sys.exit(1)
except json.JSONDecodeError:
print(f"[!] Invalid JSON in database: {db_path}")
sys.exit(1)
def parse_zeek_log(log_path):
"""Parse a Zeek log file (handles both plain and gzipped)"""
# ... 函数体见原文 ...
pass
def extract_ja4_fingerprints(log_entry, ja4_fields):
"""Extract JA4 fingerprints from a log entry"""
# ... 函数体见原文 ...
pass
def check_against_db(fingerprints, malicious_db):
"""Check if any fingerprints match the malicious database"""
# ... 函数体见原文 ...
pass
def find_log_files(directory):
"""Find all Zeek log files recursively"""
# ... 函数体见原文 ...
pass
def convert_ts_to_utc(timestamp_str):
"""Convert Zeek timestamp to UTC format"""
# ... 函数体见原文 ...
pass
def main():
if len(sys.argv) < 2:
print("Usage: python3 ja4_threat_hunter_v6.py <zeek_log_directory>")
sys.exit(1)
log_dir = sys.argv[1]
db_path = "/home/thedr/db/ja4+_db.json"
malicious_db = load_malicious_db(db_path)
log_files = find_log_files(log_dir)
if not log_files['primary']:
print(f"[!] No JA4-related log files found in {log_dir}")
sys.exit(1)
# PHASE 1: Scan primary logs for matches
total_checked = 0
findings = []
matched_uids = set()
for log_path, log_type in log_files['primary']:
for entry in parse_zeek_log(log_path):
fingerprints = extract_ja4_fingerprints(entry, PRIMARY_FIELDS[log_type])
if fingerprints:
total_checked += 1
matches = check_against_db(fingerprints, malicious_db)
if matches:
uid = entry.get('uid', 'Unknown')
matched_uids.add(uid)
findings.append({
'log_file': log_path,
'log_type': log_type,
'log_entry': entry,
'matches': matches,
'uid': uid,
'related': []
})
# PHASE 2: Find related data
if matched_uids:
uid_related = defaultdict(list)
for log_path, log_type in log_files['related']:
for entry in parse_zeek_log(log_path):
uid = entry.get('uid', '')
if uid in matched_uids:
fingerprints = extract_ja4_fingerprints(entry, RELATED_LOGS[log_type])
if fingerprints:
uid_related[uid].append({
'log_type': log_type,
'fingerprints': fingerprints,
'entry': entry
})
for finding in findings:
if finding['uid'] in uid_related:
finding['related'] = uid_related[finding['uid']]
# Report findings
# ... 输出报告部分见原文 ...
pass
if __name__ == "__main__":
main()
该脚本分两个阶段工作:
- 扫描主要日志,与我们的恶意数据库进行匹配。
- 对于任何找到的匹配项,它会使用连接UID从其他日志中提取相关的JA4数据。
一旦匹配到恶意指纹,我们就可以利用Zeek的连接ID进行数据透视,了解有关这些连接的其他重要信息。这就是Zeek的便利之处。
[图片:输出截图]
因此,即使确切的JA4S指纹和IP完全相同,也确认了203个不同的匹配项。这是TLS服务器问候(Server Hello)。客户端正在与恶意服务器通信。每隔一秒左右,就会发出一个信标。观察这个过滤后的PCAP,可以看到唯一变化的是客户端机器上的“临时”端口。这显示了连接大约每秒发出一次信标。
[图片:Wireshark捕获显示JA4S服务器问候]
请记住,对于JA4与JA4S、JA4T与JA4TS,需要捕获握手过程以及TLS客户端和服务器问候才能正确地给连接打指纹。客户端上用于C2的应用程序使用的TLS库可能是恶意的,但无法从看到的流量中确认。我假设那是Cobalt客户端在与C2服务器通信。我在这个Zeek实例上使用JA4X遇到了困难,但我可以转向Wireshark捕获或使用FoxIO专用的Python脚本来提取X509信息。我们想知道这个PCAP中的其他哪些流量可能是恶意的。JA4X不识别X509证书中的内容,而是识别其结构——证书的制作方式。这是可重现的识别信息。证书可能完全不同,但如果它们以相同的方式制作,我们就知道它们是由同一个恶意行为者(同一个应用程序)制作的。
如果Zeek的JA4X插件可用,这可以通过连接ID完成。这里,让我们看看FoxIO提供的这个Python脚本,用于从PCAP中提取X509证书。
thedr@gallifrey:~/foxio_python/ja4-sparse-clone/python$ cat ja4x_out.json | grep -i -a2 -b10 '179[.]60.149[.]244'
## 这是具有已知Cobalt Strike JA4S指纹的IP地址
## 我们想要找到这里连接的任何其他指纹
[FoxIO GitHub 带有 JA4+ 指纹示例]
这是我最喜欢JA4+的一点。我们可以基于其他指纹进行数据透视。
"stream": 76,
"src": "10.1.2.147",
"dst": "179[.]60.149[.]244",
"srcport": "49404",
"dstport": "443",
"client_ttl": "128",
"server_ttl": "41",
"domain": "zzerxc[.]com",
"JA4.1": "t00d190700_4d06a43e2d88_b4cdb2b2a0c3",
"JA4_r.1": "t00d190700_,,,,,161,162,171,172,187,188,191,192,195,196,199,200,6,7_000a,000b,000d,0017,0023,ff01_52,53,54,25,81,3,27,83,5,4,37,39",
"JA4_o.1": "t00d190700_be9ffe69a4f5_45dadac4372d",
"JA4_ro.1": "t00d190700_196,195,200,199,188,187,192,191,162,161,172,171,7,6,,,,,_0000,000a,000b,000d,0023,0017,ff01_52,53,54,25,81,3,27,83,5,4,37,39",
"JA4X.1": "a373a9f83c6b_7022c563de38_821a8ec155c6",
-- snip --
"server_ttl": "102",
"JA4.1": "t00i190600_4d06a43e2d88_b4cdb2b2a0c3",
"JA4_r.1": "t00i190600_,,,,,161,162,171,172,187,188,191,192,195,196,199,200,6,7_000a,000b,000d,0017,0023,ff01_52,53,54,25,81,3,27,83,5,4,37,39",
"JA4_o.1": "t00i190600_be9ffe69a4f5_2621bef74af5",
"JA4_ro.1": "t00i190600_196,195,200,199,188,187,192,191,162,161,172,171,7,6,,,,,_000a,000b,000d,0023,0017,ff01_52,53,54,25,81,3,27,83,5,4,37,39",
"JA4X.1": "1a59268f55e5_1a59268f55e5_795797892f9c"
######### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#### "JA4X.1": "1a59268f55e5_1a59268f55e5_795797892f9c"
### 这与PIKABOT证书100%匹配。
攻击者可以轻松更改文件的SHA256哈希值,但他们更难更改其证书颁发机构的模板。JA4X.2和.3允许我们对提供证书的基础设施进行指纹识别,而不仅仅是单个站点。现在我们可以使用JA4X进行数据透视。让我们看看发现了什么。
thedr@gallifrey:~/foxio_python/ja4-sparse-clone/python$ grep -B15 '1a59268f55e5_1a59268f55e5_795797892f9c' ja4x_out.json | grep -E '"dst"|JA4X.1'
"JA4X.1":"a373a9f83c6b_2bab15409345_0ce9ea683d50", "dst":"167[.]86.96[.]3",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c"
"JA4X.1":"2bab15409345_2166164053c1_68ccbb907075", "dst":"167[.]86.96[.]3",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"79[.]141.175[.]96",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"209[.]126.9[.]47",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"167[.]86.96[.]3",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"79[.]141.175[.]96",
"JA4X.1":"2bab15409345_2166164053c1_68ccbb907075", "dst":"209[.]126.9[.]47",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"167[.]86.96[.]3",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"79[.]141.175[.]96",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"209[.]126.9[.]47",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c", "dst":"167[.]86.96[.]3",
"JA4X.1":"1a59268f55e5_1a59268f55e5_795797892f9c"
仅基于JA4X和最初的JA4S匹配,我们已经确定其他多个IP地址也连接到了Cobalt和Pikabot。
这个最初的Pikabot感染和IOC列表归功于Palo Alto Unit 42。
[Unit42 GitHub 链接]
这是展示感染链的LinkedIn帖子。
[LinkedIn 帖子链接]
以下是基于Unit42 GitHub中的原始参考资料。
原始参考资料:
感谢这里所做的所有工作。我利用这些来更好地理解感染链以及JA4+如何在事件发生后用于继续发现更多信息。真正有趣的是,JA4+仅通过对加密流量的指纹识别,就检索到了与此感染直接相连的所有完全相同的IP地址。所以,如果你认为因为流量被加密就无法获取任何信息,那你就大错特错了。
感谢您花时间阅读此文。我不在乎点赞或分享,只希望帮助下一个读者理解。与他人分享你正在学习的东西就足够了。即使你对此完全陌生,你也有能力为社区做出贡献。参与进来,也帮助其他人参与进来。
如有任何问题或想打个招呼,欢迎来我的LinkedIn看看!www.linkedin.com/in/pagaudio… CSD0tFqvECLokhw9aBeRqiHxKuChFS1BHMrXqLpuZRSQopvII++BkDJZmWN7m+/eMmnzI0z1wxbfihqPPShIVArQoOJX66nT221TJ8z1MsdVblDBSVyHbryBqL639KY6