发布于10月9日10月9日 背景 最近在一次授权内网渗透复盘中,甲方 EDR 对一台办公网终端报出异常外联:进程名为 svchost.exe,但路径位于用户目录下的 %APPDATA%\Microsoft\Windows\,并且周期性访问一个非常规端口。该主机此前被用于邮件附件验证,怀疑存在远控木马落地。 这类样本不适合只看进程名和端口下结论,重点是确认三件事:样本是否具备远控行为、C2 配置如何保存、主机侧有哪些持久化和横向痕迹。下面记录一次比较完整的分析流程,偏向逆向木马远控板块的实战排查,不展开泛泛科普。 问题定位 现场拿到的信息如下: 异常进程:C:\Users\user\AppData\Roaming\Microsoft\Windows\svchost.exe 父进程:explorer.exe 外联地址:185.x.x.x:443,SNI 为空,TLS 指纹异常 落地时间:与用户打开压缩包附件时间接近 启动项:注册表 Run 项存在可疑值 第一步不是直接删除,而是做证据保全:样本、内存、网络连接、注册表持久化、计划任务、日志都要留一份,避免后续无法还原攻击链。 技术分析 样本为 64 位 PE,未加壳但做了字符串轻度混淆。导入表中能看到以下 API 组合,基本符合远控木马常见行为: WinHttpOpen、WinHttpConnect、WinHttpSendRequest:用于 C2 通信 RegSetValueExW:写入启动项 CreateToolhelp32Snapshot、Process32FirstW、Process32NextW:枚举进程 GetAsyncKeyState:疑似键盘记录能力 CreateProcessW、ReadFile、WriteFile:命令执行与结果回传 在 x64dbg 中跟到网络初始化逻辑,发现 C2 配置并非明文保存,而是以异或后的字节数组存在 .rdata 区。解密函数特征比较明显:循环读取数组,每个字节与固定 key 做异或,直到遇到 0x00。 伪代码逻辑: for (i = 0; enc[i] != 0; i++) { dec[i] = enc[i] ^ 0x37; } dec[i] = 0; 解密后得到: /api/checkin 185.x.x.x 443 Windows Update Service 这里需要注意:很多远控样本会把真实 C2、URI、互斥体、服务名、注册表项统一放在配置块里。配置提取比单纯查字符串更可靠,尤其是在样本做了字符串加密或分段拼接的情况下。 操作步骤 建议按以下顺序处理,避免漏掉关键证据。 1. 主机侧快速留痕 rem 查看异常进程及命令行 wmic process where "name='svchost.exe'" get ProcessId,ParentProcessId,ExecutablePath,CommandLine rem 查看网络连接与 PID 对应关系 netstat -ano | findstr ESTABLISHED rem 查询当前用户 Run 项 reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" rem 查询全局 Run 项 reg query "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" rem 导出可疑 Run 项所在注册表 reg export "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" run_hkcu.reg rem 查看计划任务中最近创建的任务 schtasks /query /fo LIST /v | findstr /i "TaskName Task To Run Author" 如果条件允许,优先拉取完整内存或至少对可疑进程做 dump。因为部分远控会只在内存中解密 C2 或二阶段模块,磁盘样本不一定包含完整信息。 2. 样本静态检查 使用 Detect It Easy 或 pefile 判断壳和编译信息。 检查节区熵值,重点关注异常高熵节区和可写可执行节区。 查看导入表中网络、注册表、进程、服务、键盘相关 API。 提取 .rdata、.data 中的可疑字节数组。 python 简单提取 PE 节区信息: import pefile pe = pefile.PE("svchost.exe") for s in pe.sections: name = s.Name.rstrip(b"\x00").decode(errors="ignore") print(name, hex(s.VirtualAddress), hex(s.Misc_VirtualSize), "entropy=", round(s.get_entropy(), 2)) print("Imports:") for entry in pe.DIRECTORY_ENTRY_IMPORT: dll = entry.dll.decode(errors="ignore") for imp in entry.imports: if imp.name: name = imp.name.decode(errors="ignore") if name in ["WinHttpOpen", "WinHttpConnect", "RegSetValueExW", "CreateProcessW", "GetAsyncKeyState"]: print(dll, name) 3. 配置解密定位 如果字符串窗口只能看到零散乱码,可以在调试器里从以下位置切入: 对 WinHttpConnect、WinHttpOpenRequest 下断点,观察传入的 host、URI。 对 RegSetValueExW 下断点,观察写入的启动项路径和值名。 对 CreateMutexW 下断点,记录互斥体名称,后续可用于批量排查。 对 Sleep 下断点或修改返回,缩短心跳等待时间。 x64dbg 常用断点: bp winhttp.WinHttpConnect bp winhttp.WinHttpOpenRequest bp advapi32.RegSetValueExW bp kernel32.CreateMutexW bp kernel32.CreateProcessW bp kernel32.Sleep 本次样本在调用 WinHttpConnect 前,RCX 指向会话句柄,RDX 指向解密后的 C2 域名或 IP,R8 为端口。命中断点后查看 RDX 指向的 Unicode 字符串即可拿到真实配置。 4. 行为验证 在隔离环境中运行样本,使用 FakeNet-NG 或 INetSim 承接网络请求。样本启动后会先创建互斥体,随后写入 Run 项,再发起心跳包。 行为观察结果风险判断 互斥体Global\WinUpdSvc_2024避免重复运行,可作为排查 IOC 持久化HKCU\...\Run\Windows Update Service用户登录后自启动 外联POST /api/checkin远控心跳与任务拉取 命令执行调用 CreateProcessW 启动 cmd.exe /c具备远程命令执行能力 日志留痕排查 Windows 侧建议重点查以下日志: Security 4688:进程创建,关注异常路径下的 svchost.exe、cmd.exe /c、powershell.exe。 Sysmon Event ID 1:进程创建,能看到哈希、父子进程关系。 Sysmon Event ID 3:网络连接,定位 C2 IP、端口、进程路径。 Sysmon Event ID 11:文件创建,定位落地路径。 Sysmon Event ID 13:注册表值修改,定位 Run 项持久化。 PowerShell 查询可疑 Run 项和异常进程: Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" | Format-List Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -like "*AppData*" -and $_.Name -eq "svchost.exe" } | Select-Object ProcessId, ParentProcessId, ExecutablePath, CommandLine Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; Id=3} | Where-Object { $_.Message -match '185\.' } | Select-Object TimeCreated, Message 风险与修复建议 先隔离主机,不建议立即关机。若进程仍在运行,优先采集内存、进程 dump、网络连接和注册表。 删除持久化项前,记录键值名称、路径、时间戳,便于后续横向排查。 对同网段终端批量检索互斥体名称、样本哈希、Run 项名称、异常路径。 在出口设备上封禁 C2 IP、域名、URI 特征,同时回溯历史连接。 检查被控主机上的凭据暴露风险,尤其是浏览器保存密码、RDP 凭据、VPN 客户端配置。 若样本具备命令执行能力,需要继续排查是否执行过信息收集、凭据抓取、横向移动命令。 可用于批量检索的 IOC 示例: FilePath: C:\Users\*\AppData\Roaming\Microsoft\Windows\svchost.exe Registry: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Windows Update Service Mutex: Global\WinUpdSvc_2024 Network: 185.x.x.x:443 /api/checkin 总结 逆向远控木马时,不要只停留在“有外联就是木马”的判断。更可靠的做法是把样本配置、持久化位置、C2 通信、命令执行能力和主机日志串起来。这样既能确认远控能力,也能给出可落地的清理和溯源依据。 本次样本的关键点在于:配置经过简单异或隐藏,真实 C2 需要在调用网络 API 前后观察;持久化使用 HKCU Run 项;命令执行通过 CreateProcessW 完成。实际处置时,建议把这些点转成 IOC 和日志检索条件,在全网范围内做一次横向排查。 逆向分析、样本行为与调试流程示意
10月9日10月9日 可以补一条我在内网排查远控样本时比较常用的思路:先别急着跑全量杀软,优先把“配置”和“落点链路”固定下来,后面溯源、横向排查会省很多时间。 样本配置提取建议重点看这几类位置: 1. 字符串里找 C2 / mutex / install path 如果样本没有重度混淆,先直接过一遍: strings -a sample.exe | findstr /i "http https tcp udp mutex key install appdata programdata cmd powershell" 如果是宽字符: strings -a -el sample.exe | findstr /i "http https .com .cn .top .xyz mutex" 2. 看资源段和 overlay 很多远控会把配置放在资源、末尾 overlay 或简单异或加密区域。可以先确认 PE 结构: pefile sample.exe diec sample.exe sigcheck -m sample.exe 如果怀疑末尾追加配置,可以对比最后一个 section 的 raw size 与文件实际大小,存在 overlay 时单独切出来分析: python - << "EOF" import pefile, sys p="sample.exe" pe=pefile.PE(p) last=max(pe.sections, key=lambda s: s.PointerToRawData+s.SizeOfRawData) end=last.PointerToRawData+last.SizeOfRawData data=open(p,'rb').read() print("file size:", len(data), "section end:", end, "overlay:", len(data)-end) if len(data)>end: open("overlay.bin","wb").write(data[end:]) EOF 然后对 overlay 做 strings / 熵值 / XOR 猜测。很多配置是单字节 XOR,先爆破可读字符串命中率就够用。 3. 动态提取配置 如果样本会解密后再联网,可以在沙箱或隔离 VM 里盯几个 API: - WinHttpOpenRequest / WinHttpSendRequest - InternetConnectW / HttpOpenRequestW - connect / send - RegSetValueExW - CreateServiceW - CreateProcessW x64dbg 下可以给这些 API 下断点,重点看参数和返回后的内存。通常能直接拿到 C2、端口、URI、User-Agent、分组名、上线密码之类。 留痕定位上,建议按“启动项、服务、计划任务、WMI、进程注入、网络连接”六条线走,命令可以直接落地: :: 启动项 reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" reg query "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" reg query "HKLM\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run" :: 服务 wmic service get name,displayname,pathname,startmode,state | findstr /i "appdata programdata temp users" :: 计划任务 schtasks /query /fo LIST /v | findstr /i "TaskName Task To Run appdata programdata powershell wscript rundll32" :: WMI 持久化 wmic /namespace:\\root\subscription PATH __EventFilter get Name,Query wmic /namespace:\\root\subscription PATH CommandLineEventConsumer get Name,CommandLineTemplate wmic /namespace:\\root\subscription PATH __FilterToConsumerBinding get Filter,Consumer :: 当前网络连接 netstat -ano | findstr ESTABLISHED tasklist /svc 如果主机开了 Sysmon,建议重点查这些事件: - Event ID 1:进程创建 - Event ID 3:网络连接 - Event ID 7:模块加载,尤其异常路径 DLL - Event ID 11:文件创建 - Event ID 13:注册表修改 - Event ID 22:DNS 查询 PowerShell 查询示例: Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Sysmon/Operational"; Id=1,3,11,13,22} | Where-Object { $_.TimeCreated -gt (Get-Date).AddDays(-7) } | Select-Object TimeCreated, Id, ProviderName, Message | Out-File .\sysmon_check.txt -Encoding utf8 还有一点容易漏:远控常见落点不一定在 exe 本体,可能是 rundll32/regsvr32/mshta/powershell 拉起的二阶段。排查时不要只盯样本文件名,建议按异常路径和父子进程关系看,比如: - `C:\Users\*\AppData\Roaming\` - `C:\Users\*\AppData\Local\Temp\` - `C:\ProgramData\` - 用户目录下伪装成系统名的文件,如 `svchost.exe`、`RuntimeBroker.exe` 最后配置提取后,建议把这些字段整理成 IOC 表:C2、端口、URI、User-Agent、mutex、服务名、计划任务名、注册表键、文件 hash、落地路径。内网横向排查时可以直接用这些 IOC 去扫日志和 EDR,比单纯拿 hash 查要稳一些。
10月10日10月10日 补一条留痕侧的做法:配置提取完以后,建议把“样本声明的持久化位置”和“系统真实落点”做一次交叉验证,很多远控会改名落在正常目录里,但注册表/任务计划里的参数能把链路串起来。 可以优先查这几块: 1. 启动项和服务参数 reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /s reg query "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /s reg query "HKLM\SYSTEM\CurrentControlSet\Services" /s | findstr /i "ImagePath DisplayName ObjectName" wmic service get Name,DisplayName,PathName,StartMode,State | findstr /i "appdata programdata temp powershell rundll32 regsvr32 mshta" 重点看这些特征: - ImagePath 指向 %AppData%、%ProgramData%、%Temp% - 服务名正常但路径异常,比如伪装成 update、audio、driver - 参数里带 -enc、-nop、rundll32 xxx.dll,Start 这类形式 2. 任务计划不要只看名字,要导出 XML 很多远控不会直接暴露在 schtasks /query 的摘要里,动作参数在 XML 里更清楚: schtasks /query /fo LIST /v mkdir C:\Temp\tasks_xml for /f "tokens=*" %i in ('schtasks /query /fo csv ^| findstr /v "TaskName"') do @echo %i dir /s /b C:\Windows\System32\Tasks > C:\Temp\tasks_list.txt findstr /s /i "powershell wscript cscript mshta rundll32 regsvr32 appdata programdata temp" C:\Windows\System32\Tasks\* 如果发现可疑任务,直接看文件内容即可,里面会有 Command 和 Arguments。 3. 用 Amcache / ShimCache / Prefetch 补执行时间线 这一步对定位“谁先落地、谁拉起谁”比较有帮助。尤其是样本已经被删掉时,Amcache 和 Prefetch 经常还能留下痕迹。 可以先收这些文件: C:\Windows\AppCompat\Programs\Amcache.hve C:\Windows\Prefetch\*.pf C:\Windows\System32\config\SYSTEM C:\Windows\System32\winevt\Logs\Security.evtx C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx 排查时重点关联: - 可疑 EXE 首次执行时间 - Prefetch 中的运行次数和路径 - PowerShell 4104/4103 事件里是否有下载、解码、反射加载 - Security 4688 如果开启了进程命令行记录,可以直接还原父子进程 4. 网络侧别只看当前连接,查 DNS 缓存和代理日志 远控 C2 如果是域名,机器本地可能还有 DNS 缓存: ipconfig /displaydns | findstr /i ".top .xyz .icu .shop .cn .com" netstat -ano | findstr "ESTABLISHED" tasklist /fi "PID eq 进程PID" 如果内网有 DNS 日志、代理日志或防火墙会话日志,建议拿配置里提取到的 C2、URI、User-Agent、端口去反查,同一时间段命中的主机往往就是横向排查范围。 5. 样本配置建议落成 IOC 表 最后别只记录文件 Hash,最好整理成这种结构,方便后续批量排查: FileName: SHA256: CompileTime: C2: Port: URI: Mutex: InstallPath: ServiceName: TaskName: RegistryKey: UserAgent: Persistence: FirstSeenHost: FirstSeenTime: 实践里比较有效的是:先用配置确定通信特征,再用持久化位置和执行痕迹确定感染链路,最后用网络日志扩散排查范围。这样比单纯全盘查杀更容易把同批落点和二阶段载荷找出来。
创建帐户或登录后发表意见