跳转到帖子

Windows 远控木马逆向分析:从计划任务持久化到 C2 配置提取与日志闭环

精选回复

发布于

背景

在一次已授权的终端排查中,EDR 发现某台 Windows 10 测试主机周期性访问外部 HTTPS 地址。进程树显示 explorer.exe 启动了位于 C:\ProgramData\UpdateCache\UpdateHost.exe 的未知程序,随后该程序创建计划任务,并间歇拉起 powershell.exe。样本没有有效数字签名,文件时间与所在目录的创建时间也不一致。

本文以这一类典型远控木马为分析对象,重点说明如何在隔离环境中确认样本入口、持久化方式、C2 配置和主机留痕。涉及的地址、任务名和互斥体均为实验环境示例,操作仅适用于已授权分析。

不要直接在办公网络或可访问生产凭据的虚拟机中运行未知样本。建议使用无共享剪贴板、无共享目录、快照可回滚的隔离虚拟机,并将网络接入 INetSim、FakeNet-NG 或受控代理。

一、现场定位:先保留证据,再处理文件

发现可疑程序后,不建议立即删除。直接删除会损失父子进程、任务创建时间、网络目的地址和配置数据等关键信息。应先记录文件元数据、进程树、网络连接、计划任务以及相关事件日志。

$sample = 'C:\ProgramData\UpdateCache\UpdateHost.exe'

# 文件摘要与基本属性
Get-Item $sample | Format-List FullName,Length,CreationTimeUtc,LastWriteTimeUtc,Attributes
Get-FileHash $sample -Algorithm SHA256
Get-AuthenticodeSignature $sample | Format-List Status,StatusMessage,SignerCertificate

# Sysinternals 签名和版本信息
sigcheck64.exe -accepteula -nobanner -h -i -m $sample

# 当前进程与命令行
Get-CimInstance Win32_Process |
  Where-Object {$_.ExecutablePath -eq $sample} |
  Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine,CreationDate

# 关联网络连接
Get-NetTCPConnection |
  Where-Object {$_.OwningProcess -in (Get-Process UpdateHost -ErrorAction SilentlyContinue).Id} |
  Select-Object State,LocalAddress,LocalPort,RemoteAddress,RemotePort,OwningProcess

# 导出日志,避免后续清理或覆盖
wevtutil epl System C:\IR\System.evtx
wevtutil epl Security C:\IR\Security.evtx
wevtutil epl Microsoft-Windows-Sysmon/Operational C:\IR\Sysmon.evtx
wevtutil epl Microsoft-Windows-TaskScheduler/Operational C:\IR\TaskScheduler.evtx

如果进程仍在运行,可在隔离后制作内存转储。对于存在运行时解密、反射加载或配置仅驻留内存的样本,内存通常比磁盘文件更有价值。

mkdir C:\IR -ErrorAction SilentlyContinue
procdump64.exe -accepteula -ma (Get-Process UpdateHost).Id C:\IR\UpdateHost.dmp
Get-FileHash C:\IR\UpdateHost.dmp -Algorithm SHA256

转储文件可能包含令牌、Cookie、主机信息或其他敏感数据,应按取证材料控制访问,不要上传到公开平台。

二、静态分析:确认 PE 结构与可疑能力

先用 Detect It Easy、PE-bear 或 pefile 检查 PE 类型、编译时间、节区、导入表和 TLS 目录。编译时间只能作为弱线索,木马经常伪造 TimeDateStamp。更值得关注的是节区熵、RawSize 与 VirtualSize 的差异、入口点所在节区以及是否存在 TLS Callback。

  • 入口点位于高熵自定义节区,且导入函数很少:可能经过壳或运行时解析 API。
  • 存在 VirtualAlloc、VirtualProtect、WriteProcessMemory、CreateRemoteThread:需要检查是否有解包、注入或远程线程行为。
  • 存在 WinHttpOpen、WinHttpConnect、WinHttpSendRequest:可从参数交叉引用定位 HTTP 配置。
  • 存在 RegSetValueExW、CreateServiceW、Task Scheduler COM 接口:重点追踪持久化逻辑。
  • 存在 CryptUnprotectData:配置或凭据可能通过当前用户或本机 DPAPI 解密。

可先用 FLOSS 和 capa 缩小范围。FLOSS 对栈字符串、简单异或字符串和运行时构造字符串通常比普通 strings 更有效;capa 则适合快速识别网络通信、服务创建、互斥体和进程注入等能力。

floss.exe UpdateHost.exe > floss.txt
capa.exe -v UpdateHost.exe > capa.txt
strings64.exe -n 6 UpdateHost.exe > strings.txt

# 查看 PE 头和导入表
llvm-readobj --file-headers --sections --coff-imports UpdateHost.exe

本例样本的导入表较小,但在入口函数附近可以看到 LoadLibraryW 和 GetProcAddress 的循环调用。继续跟踪哈希比较函数后,可确认样本动态解析 winhttp.dll 中的网络 API。这类实现会削弱基于静态导入表的检测效果,但 API 名称、DLL 名称或哈希常量仍可能出现在解包后的内存中。

三、动态调试:从关键 API 反推木马行为

在 x64dbg 或 WinDbg 中,不必一开始逐指令跟踪。针对远控样本,更高效的方法是先在行为边界设置断点,再根据调用栈回到样本代码。建议关注以下位置:

  • kernel32!CreateProcessW:观察子进程命令行及隐藏窗口参数。
  • advapi32!RegSetValueExW:定位 Run 键或其他注册表持久化。
  • taskschd 相关 COM 调用:确认计划任务名称、触发器和执行路径。
  • winhttp!WinHttpConnect、winhttp!WinHttpOpenRequest、winhttp!WinHttpSendRequest:提取主机名、URI、请求头和请求体。
  • ws2_32!connect:对自定义 TCP 协议记录目标地址和端口。
  • crypt32!CryptUnprotectData:在函数返回后读取明文缓冲区。
  • kernel32!VirtualProtect:寻找解包结束点或修改为可执行权限的内存区域。
# WinDbg 示例:命中后打印关键参数并查看调用栈
bp kernel32!CreateProcessW
bp advapi32!RegSetValueExW
bp winhttp!WinHttpConnect
bp winhttp!WinHttpOpenRequest
bp winhttp!WinHttpSendRequest
bp crypt32!CryptUnprotectData
bp kernel32!VirtualProtect

g
# 断点触发后
k
r

在 x64 Windows 调用约定下,前四个整数或指针参数通常位于 RCX、RDX、R8、R9。以 WinHttpConnect 为例,第二个参数 RDX 通常指向服务器名称宽字符串,可直接跟随内存查看。WinHttpOpenRequest 的对象路径同样可以从对应参数中取得。

本例中,WinHttpConnect 收到的主机名并未直接保存在磁盘明文中,而是在调用前由一个循环解码到堆缓冲区。对该缓冲区设置硬件写断点后回溯,可定位到配置解密函数。解密后的结构包含版本号、轮询间隔、主机名、端口、URI 和互斥体名称。轮询间隔为 60 秒,并加入约 0 至 15 秒随机抖动,因此流量并非严格等间隔。

四、持久化分析:计划任务与权限边界

样本通过当前用户权限创建计划任务,任务路径为 \Microsoft\Windows\Maintenance\UpdateCheck,登录后延迟执行 UpdateHost.exe。由于样本运行在普通用户上下文,它无法直接写入 HKLM 服务项,但仍可利用用户可写目录和登录触发器维持驻留。

# 定位任务定义及执行动作
Get-ScheduledTask |
  Where-Object {$_.TaskName -like '*UpdateCheck*'} |
  ForEach-Object {
    $_ | Select-Object TaskPath,TaskName,State,Author
    $_.Actions | Format-List Execute,Arguments,WorkingDirectory
    $_.Triggers | Format-List *
    $_.Principal | Format-List UserId,LogonType,RunLevel
  }

# 查询任务创建、更新和删除事件
Get-WinEvent -FilterHashtable @{
  LogName='Microsoft-Windows-TaskScheduler/Operational'
  Id=106,140,141
} | Select-Object TimeCreated,Id,Message

# 同时检查常见自启动位置
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -ErrorAction SilentlyContinue
Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run' -ErrorAction SilentlyContinue
Get-CimInstance Win32_Service | Where-Object {$_.PathName -match 'ProgramData|AppData|Temp'}

权限分析不能只看样本当前是否提权。还应检查任务动作指向的目录是否允许普通用户覆盖、任务是否以高权限账户运行,以及服务路径是否存在弱 ACL。如果高权限任务执行用户可写文件,即使当前木马仅获得普通用户权限,也可能形成后续提权条件。

icacls C:\ProgramData\UpdateCache
icacls C:\ProgramData\UpdateCache\UpdateHost.exe
sc.exe qc SuspiciousService
accesschk64.exe -accepteula -uwdqs Users C:\ProgramData

五、C2 行为与网络侧验证

动态调试显示样本使用 WinHTTP 发送 HTTPS POST,请求路径为 /api/v2/checkin。首次请求体包含主机名、用户名、系统版本、进程位数和样本生成的终端标识。后续响应决定是否执行命令、上传文件或调整轮询时间。

如果流量由 TLS 加密,不建议仅依赖抓包内容。可以结合 DNS、代理、TLS 元数据和主机 API 参数进行关联:

  • Sysmon 事件 ID 22:DNS 查询。
  • Sysmon 事件 ID 3:进程级网络连接。
  • 代理日志:HTTP 方法、目标域名、URI、状态码和传输字节数。
  • 防火墙日志:目标 IP、端口、连接频率和持续时间。
  • Schannel 或 TLS 采集设备:SNI、证书指纹和 JA3/JA4 等辅助特征。

域名、IP 和证书都可能快速变化,不宜将单个地址作为唯一检测依据。更稳定的组合特征是:异常目录中的无签名进程、固定 URI、特定请求头顺序、近似固定的心跳周期,以及同一进程创建计划任务的行为链。

六、日志关联:还原完整攻击时间线

建议以样本首次落盘时间为中心,向前后各扩展至少 30 分钟。重点关联文件创建、进程启动、DNS、网络连接、注册表修改和计划任务事件。若启用了 Sysmon,可优先检查以下事件:

  • 事件 ID 1:进程创建,确认父进程、命令行和哈希。
  • 事件 ID 3:网络连接,关联 UpdateHost.exe 的远端地址。
  • 事件 ID 11:文件创建,追踪样本落盘及更新文件。
  • 事件 ID 13:注册表值设置,确认自启动或配置写入。
  • 事件 ID 22:DNS 查询,补充 C2 域名。
  • 事件 ID 25:进程篡改,辅助发现空洞化等行为。
$start = (Get-Date).AddDays(-2)

Get-WinEvent -FilterHashtable @{
  LogName='Microsoft-Windows-Sysmon/Operational'
  StartTime=$start
  Id=1,3,11,13,22,25
} | Where-Object {
  $_.Message -match 'UpdateHost.exe|UpdateCache|UpdateCheck'
} | Select-Object TimeCreated,Id,Message |
  Export-Csv C:\IR\timeline.csv -NoTypeInformation -Encoding UTF8

如果未部署 Sysmon,仍可从安全日志 4688、任务计划日志、PowerShell 4103/4104、Defender Operational 日志、Prefetch、Amcache 和 SRUM 中补充证据。需要注意,4688 是否包含命令行取决于审计策略;PowerShell 脚本块日志也必须在事件发生前启用。

七、用于内网排查的 YARA 规则

根据逆向得到的互斥体、任务路径和 URI,可以编写初步 YARA 规则扫描磁盘样本。规则应使用多个独立特征组合,避免只凭一个通用字符串产生大量误报。

rule Win_RAT_UpdateHost_Triage
{
    meta:
        description = "Triage rule for analyzed UpdateHost RAT sample family"
        scope = "authorized incident response only"

    strings:
        $task = "Microsoft\\Windows\\Maintenance\\UpdateCheck" wide ascii
        $uri  = "/api/v2/checkin" ascii
        $mtx  = "Global\\UpdateHost_Instance_" wide ascii
        $ua   = "WindowsUpdateClient/10.0" ascii

    condition:
        uint16(0) == 0x5A4D and
        filesize < 10MB and
        3 of ($task,$uri,$mtx,$ua)
}

该规则适合对同批次样本进行快速排查,不应单独作为恶意判定依据。部署前应在正常软件集上回归测试,并记录命中路径、签名状态、文件哈希和首次出现时间。

八、处置与修复建议

  • 先隔离受影响终端,再终止进程、禁用计划任务和封禁网络指标,避免样本收到新的远控指令。
  • 保留样本、内存转储、任务 XML、事件日志和代理日志,并计算 SHA-256,确保后续分析可复核。
  • 删除持久化前导出任务定义,确认任务是否由其他高权限账户创建,以及相同任务是否存在于其他终端。
  • 若样本具备凭据访问、浏览器数据读取或键盘记录能力,应轮换在该主机上使用过的域账户、VPN、邮箱和运维凭据。
  • 检查 C:\ProgramData、用户 AppData、Temp 和公共启动目录的 ACL,避免高权限任务或服务执行普通用户可覆盖的文件。
  • 为关键终端启用进程命令行审计、PowerShell 脚本块日志和合理裁剪的 Sysmon 配置,并集中保存日志。
  • 对出站网络实施代理或主机防火墙控制,普通办公终端不应能够任意直连外部高位端口。
  • 处置完成后持续观察至少一个原轮询周期的数倍时间,确认 DNS、TLS 和任务触发行为不再出现。

删除计划任务和文件的操作应在取证完成后进行:

# 先导出任务定义
schtasks.exe /Query /TN "\Microsoft\Windows\Maintenance\UpdateCheck" /XML > C:\IR\UpdateCheck.xml

# 完成取证并确认任务恶意后再删除
schtasks.exe /Delete /TN "\Microsoft\Windows\Maintenance\UpdateCheck" /F
Stop-Process -Name UpdateHost -Force -ErrorAction SilentlyContinue
Remove-Item 'C:\ProgramData\UpdateCache\UpdateHost.exe' -Force

# 最后复查残留
Get-ScheduledTask | Where-Object {$_.TaskName -like '*UpdateCheck*'}
Get-NetTCPConnection | Where-Object {$_.RemotePort -eq 443 -and $_.State -eq 'Established'}

总结

远控木马分析的关键不是单独识别某个字符串或外联地址,而是建立从样本入口、配置解密、持久化、进程链到网络心跳的完整证据链。实际排查时,应先保留文件与日志,再通过静态分析筛选能力,通过 API 断点和内存观察提取运行时配置,最后把逆向结果转化为 YARA、日志查询和权限修复措施。只有同时清理持久化、控制凭据风险并修复可被滥用的权限边界,才能避免样本被简单删除后再次落地。

逆向分析与样本观察示意图
逆向分析、样本行为与调试流程示意
服务器与云环境安全示意图
服务器、云资产与权限边界梳理示意

这类样本做“日志闭环”时,建议不要只记录任务名和 C2。计划任务名很容易随机化,真正稳定的是任务 XML 中的 Exec.Command、Arguments、Principal/UserId、触发器,以及落地文件哈希。

现场可以同时保全任务文件、TaskCache 注册表和事件日志,避免样本自删除任务后只剩零散记录:

# 导出当前计划任务定义
Get-ScheduledTask | ForEach-Object {
    $name = ($_.TaskPath + $_.TaskName).TrimStart('\').Replace('\', '_')
    Export-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath |
        Out-File ".\tasks\$name.xml" -Encoding utf8
}

# 保全计划任务文件及注册表
robocopy C:\Windows\System32\Tasks .\TasksBackup /E /COPYALL
reg save "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache" .\TaskCache.hiv

# 导出关键事件日志
wevtutil epl Microsoft-Windows-TaskScheduler/Operational .\TaskScheduler.evtx
wevtutil epl Security .\Security.evtx
wevtutil epl Microsoft-Windows-Sysmon/Operational .\Sysmon.evtx

时间线关联上重点看 Security 4698/4699,TaskScheduler/Operational 106、140、141、200、201,再用 Sysmon 1 的 ProcessGuid 串到 3(网络连接)、11(文件创建)、13(注册表修改)和 22(DNS)。尤其注意任务动作是直接启动木马,还是通过 cmd.exe /c、powershell.exe、rundll32.exe、regsvr32.exe 做了一层代理执行。

C2 配置提取建议同时做静态和动态验证。静态侧先定位 URL、域名、User-Agent、端口和互斥体附近的交叉引用;如果只看到高熵数据,就继续追配置解密函数的调用链,关注 Base64 后的 XOR、RC4/AES,以及硬编码 key/IV。动态侧可在解密函数返回后或 WinHTTP/WinINet 调用前下断点,直接取明文缓冲区;常见位置包括 WinHttpConnect、WinHttpOpenRequest、InternetConnectW、HttpSendRequestW 和 WSAConnect。

最终 IOC 最好区分“样本配置值”和“实际观测值”:配置里存在备用域名不代表本次运行访问过,反过来 DNS 解析结果和重定向地址也未必直接出现在配置中。报告中把任务创建时间、首次进程启动、首次 DNS、首次外连和回连周期放到同一条时间线上,闭环会更扎实,也更方便后续转成 Sysmon/Sigma 检测规则。

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。