Windows应急响应/20-Windows服务后门
tags:: #Windows应急响应 #持久化 #服务 #Service #Backdoor
category:: 持久化与后门检测
difficulty:: 中级-高级
platform:: Windows 7/10/11, Server 2012-2022
概述
Windows Service(服务)以SYSTEM权限运行,是攻击者最青睐的持久化目标之一
服务在系统启动时自动运行,无需用户登录
MITRE ATT&CK: T1543.003 - Create or Modify System Process: Windows Service
攻击手法包括: 创建新服务、修改已有服务、DLL服务注入、FailureCommand滥用等
Linux对比: 17-Systemd-Service后门 是Linux下的类似机制
一、Windows服务基础
1.1 服务类型
EXE服务 (SERVICE_WIN32_OWN_PROCESS)
独立的可执行文件作为服务运行
有自己的进程空间
DLL服务 (SERVICE_WIN32_SHARE_PROCESS)
以DLL形式加载到svchost.exe进程中
多个DLL服务共享同一个svchost实例
更隐蔽,因为恶意代码隐藏在svchost进程中
Kernel Driver (SERVICE_KERNEL_DRIVER)
内核驱动服务
需要签名(在启用了DSE的系统上)
1.2 服务注册表路径
所有服务的配置存储在:
1
| HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>
|
关键注册表值:
ImagePath — 服务可执行文件路径(或DLL宿主命令行)
Start — 启动类型(0=Boot, 1=System, 2=Automatic, 3=Manual, 4=Disabled)
Type — 服务类型
ObjectName — 运行账户(默认LocalSystem)
Parameters\ServiceDll — DLL服务的DLL路径
FailureActions — 服务失败时的操作
FailureCommand — 服务失败时执行的命令
1.3 服务状态查询
基本查询:
1
| sc query type= service state= all
|
查看特定服务配置:
PowerShell:
1
| Get-Service | Select-Object Name, DisplayName, Status, StartType | Format-Table -AutoSize
|
WMI查询:
1
| Get-WmiObject Win32_Service | Select-Object Name, DisplayName, State, StartMode, PathName | Format-Table -AutoSize
|
二、攻击手法详解
2.1 创建恶意EXE服务
使用sc create:
1 2 3
| sc create "WindowsUpdateSvc" binPath= "C:\Users\Public\beacon.exe" start= auto obj= LocalSystem DisplayName= "Windows Update Service" sc description "WindowsUpdateSvc" "Enables the detection, download, and installation of updates for Windows." sc start "WindowsUpdateSvc"
|
注意sc create中=后面必须有一个空格
攻击者通常会:
使用与系统服务相似的名称
添加看起来合理的description
设置为auto启动
2.2 DLL服务后门
创建基于svchost的DLL服务(更隐蔽):
1 2 3
| sc create "NetProfileSvc2" binPath= "C:\Windows\system32\svchost.exe -k netsvcs" type= share start= auto
reg add "HKLM\SYSTEM\CurrentControlSet\Services\NetProfileSvc2\Parameters" /v ServiceDll /t REG_EXPAND_SZ /d "C:\Windows\System32\evil.dll" /f
|
将DLL加入svchost组:
1
| reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" /v netsvcs /t REG_MULTI_SZ /d "... NetProfileSvc2" /f
|
DLL需要导出ServiceMain函数:
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
| #include <windows.h>
SERVICE_STATUS_HANDLE hStatus; SERVICE_STATUS serviceStatus;
void WINAPI ServiceHandler(DWORD control) { if (control == SERVICE_CONTROL_STOP) { serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(hStatus, &serviceStatus); } }
void WINAPI ServiceMain(DWORD argc, LPWSTR *argv) { hStatus = RegisterServiceCtrlHandlerW(L"NetProfileSvc2", ServiceHandler); serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(hStatus, &serviceStatus); while (serviceStatus.dwCurrentState == SERVICE_RUNNING) { Sleep(1000); } }
|
2.3 修改已有服务的ImagePath
篡改合法但未运行/禁用的服务:
1 2 3
| sc config "SensorService" binPath= "C:\Users\Public\payload.exe" sc config "SensorService" start= auto sc start "SensorService"
|
或者通过注册表直接修改:
1 2
| Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SensorService" -Name "ImagePath" -Value "C:\Users\Public\payload.exe" Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SensorService" -Name "Start" -Value 2
|
这种方法的优点: 不创建新服务,仅修改已有条目
检测难度更高,因为服务名是合法的
2.4 FailureCommand滥用
利用服务故障恢复机制执行恶意命令:
1
| sc failure "LegitService" reset= 0 actions= run/5000 command= "C:\Users\Public\payload.exe"
|
或者通过注册表设置:
1 2
| Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LegitService" -Name "FailureCommand" -Value "powershell.exe -w hidden -c IEX(...)"
|
然后故意让服务崩溃或设置一个会失败的服务
这是一个经常被忽略的持久化位置
很多安全工具不检查FailureCommand
2.5 Unquoted Service Path利用
当ImagePath包含空格且未用引号包裹时,可以劫持:
1 2 3 4 5
| 原始路径: C:\Program Files\Legit App\service.exe Windows解析顺序: 1. C:\Program.exe 2. C:\Program Files\Legit.exe 3. C:\Program Files\Legit App\service.exe
|
攻击者在 C:\Program Files\Legit.exe 放置payload
查找存在漏洞的服务:
1 2 3 4 5 6
| Get-WmiObject Win32_Service | Where-Object { $_.PathName -and $_.PathName -notmatch '^"' -and $_.PathName -match '\s' -and $_.PathName -notmatch '^C:\\Windows\\system32' } | Select-Object Name, DisplayName, PathName, StartMode | Format-Table -AutoSize -Wrap
|
修复:
1
| sc config "VulnService" binPath= "\"C:\Program Files\Legit App\service.exe\""
|
三、检测方法
3.1 服务枚举与异常检测
全面服务审计脚本:
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
| Write-Host "=== Windows Service Security Audit ===" -ForegroundColor Cyan
$services = Get-WmiObject Win32_Service
foreach ($svc in $services) { $flags = @() $path = $svc.PathName $exePath = $null if ($path -match '^"([^"]+)"') { $exePath = $Matches[1] } elseif ($path -match '^(\S+)') { $exePath = $Matches[1] } if ($path -match "(?i)(Users\\Public|\\Temp\\|AppData|ProgramData)") { $flags += "SUSPICIOUS_PATH" } if ($exePath -and (Test-Path $exePath)) { $sig = Get-AuthenticodeSignature $exePath -ErrorAction SilentlyContinue if ($sig.Status -ne 'Valid') { $flags += "UNSIGNED" } } elseif ($exePath -and $exePath -notmatch "svchost\.exe") { $flags += "FILE_NOT_FOUND" } if ($path -and $path -notmatch '^"' -and $path -match '\s' -and $path -notmatch '^C:\\Windows\\') { $flags += "UNQUOTED_PATH" } $failCmd = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\$($svc.Name)" -Name FailureCommand -ErrorAction SilentlyContinue).FailureCommand if ($failCmd) { $flags += "HAS_FAILURE_CMD" } if ($flags.Count -gt 0) { Write-Host "`n[!] $($svc.Name) ($($svc.DisplayName))" -ForegroundColor Red Write-Host " Path: $path" Write-Host " Start: $($svc.StartMode) | State: $($svc.State)" Write-Host " Flags: $($flags -join ', ')" -ForegroundColor Yellow if ($failCmd) { Write-Host " FailureCmd: $failCmd" -ForegroundColor Magenta } } }
|
3.2 sc qc逐一检查
查看特定服务的详细配置:
1 2 3
| sc qc "SuspiciousService" sc qfailure "SuspiciousService" sc sdshow "SuspiciousService"
|
sc qfailure 可以显示故障恢复配置,包括FailureCommand
sc sdshow 显示服务的安全描述符
3.3 服务二进制签名检查
使用sigcheck批量检查所有服务二进制:
1
| sigcheck.exe -e -u -s C:\Windows\System32\*.exe > unsigned_system32.txt
|
PowerShell方式检查所有服务EXE签名:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Get-WmiObject Win32_Service | ForEach-Object { $path = $_.PathName $exe = $null if ($path -match '^"?([^"]+\.exe)') { $exe = $Matches[1] } if ($exe -and (Test-Path $exe)) { $sig = Get-AuthenticodeSignature $exe if ($sig.Status -ne 'Valid') { [PSCustomObject]@{ Service = $_.Name Path = $exe SigStatus = $sig.Status Signer = $sig.SignerCertificate.Subject } } } } | Format-Table -AutoSize -Wrap
|
3.4 DLL服务检查
专门检查ServiceDll参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Services" | ForEach-Object { $paramPath = "$($_.PSPath)\Parameters" if (Test-Path $paramPath) { $dll = (Get-ItemProperty $paramPath -Name ServiceDll -ErrorAction SilentlyContinue).ServiceDll if ($dll) { $resolvedDll = [Environment]::ExpandEnvironmentVariables($dll) $sig = $null if (Test-Path $resolvedDll) { $sig = (Get-AuthenticodeSignature $resolvedDll).Status } [PSCustomObject]@{ Service = $_.PSChildName ServiceDll = $dll Exists = (Test-Path $resolvedDll) SigStatus = $sig } } } } | Where-Object { $_.SigStatus -ne 'Valid' -or -not $_.Exists } | Format-Table -AutoSize -Wrap
|
3.5 Windows事件日志
Event ID 7045: 新服务安装(System日志)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Get-WinEvent -FilterHashtable @{ LogName = 'System' Id = 7045 } -MaxEvents 50 | ForEach-Object { $xml = [xml]$_.ToXml() [PSCustomObject]@{ TimeCreated = $_.TimeCreated ServiceName = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'ServiceName' }).'#text' ImagePath = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'ImagePath' }).'#text' ServiceType = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'ServiceType' }).'#text' StartType = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'StartType' }).'#text' AccountName = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'AccountName' }).'#text' } } | Format-Table -AutoSize -Wrap
|
Event ID 4697: 服务安装(Security日志,需要审核策略)
1 2 3 4
| Get-WinEvent -FilterHashtable @{ LogName = 'Security' Id = 4697 } -MaxEvents 50 | Select-Object TimeCreated, Message | Format-List
|
Event ID 7040: 服务启动类型变更
Event ID 7034: 服务意外终止(可能与FailureCommand配合使用)
四、真实案例分析
4.1 Cobalt Strike服务后门
Cobalt Strike默认的psexec横向移动会创建服务:
1 2
| Service Name: <random 7 chars> (如 "a1b2c3d") ImagePath: %COMSPEC% /b /c start /b /min powershell.exe -nop -w hidden -encodedcommand <base64>
|
特征:
随机短名称
使用%COMSPEC%间接调用
PowerShell encoded command
定制版可能使用更隐蔽的名称和路径
4.2 PsExec服务
PsExec会创建PSEXESVC服务:
1 2
| Service Name: PSEXESVC ImagePath: C:\Windows\PSEXESVC.exe
|
自定义名称的PsExec:
1
| psexec \\target -r CustomSvcName -s cmd.exe
|
检查是否有PSEXESVC或其变体
4.3 勒索软件服务持久化
部分勒索软件家族通过服务实现持久化:
1 2 3
| Service Name: WinDefService ImagePath: C:\ProgramData\Microsoft\Windows\WinDef.exe Start: Auto
|
模仿Windows Defender命名
路径选择在ProgramData下的Microsoft目录
五、清除与修复
5.1 停止并删除恶意服务
标准流程:
1 2 3 4 5 6 7 8 9 10 11
| :: 1. 停止服务 sc stop "MaliciousService"
:: 2. 查看进程信息 sc queryex "MaliciousService"
:: 3. 终止残留进程 taskkill /PID <pid> /F
:: 4. 删除服务 sc delete "MaliciousService"
|
PowerShell方式:
1 2 3
| Stop-Service "MaliciousService" -Force
& sc.exe delete "MaliciousService"
|
注意: PowerShell中 sc 是 Set-Content 的别名,必须使用 sc.exe
5.2 修复被篡改的服务
如果合法服务被修改了ImagePath:
1 2 3 4 5
| :: 恢复原始路径 sc config "LegitService" binPath= "C:\Windows\System32\original.exe"
:: 清除恶意FailureCommand sc failure "LegitService" reset= 0 actions= restart/60000 command= ""
|
从备份或其他相同版本系统恢复服务配置
5.3 证据收集
在清除前收集证据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $evidence = "C:\IR_Evidence\Services" New-Item -ItemType Directory -Path $evidence -Force
reg export "HKLM\SYSTEM\CurrentControlSet\Services" "$evidence\Services.reg"
Get-WmiObject Win32_Service | Export-Csv "$evidence\services.csv" -NoTypeInformation
wevtutil epl System "$evidence\System.evtx" "/q:*[System[(EventID=7045)]]"
|
六、高级技术补充
6.1 服务DLL幻影加载(Phantom DLL in svchost)
在svchost组中注册一个不存在的DLL名,然后放置恶意DLL:
1 2 3 4
| $existing = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" -Name "netsvcs").netsvcs $updated = $existing + "EvilSvc" Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" -Name "netsvcs" -Value $updated -Type MultiString
|
这使得恶意DLL在svchost.exe进程中加载,混在众多合法DLL中
6.2 服务权限后门
修改服务的安全描述符,允许低权限用户修改服务配置:
1
| sc sdset "VulnService" "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;RPWPCR;;;BU)"
|
这为后续的权限维持提供便利
检测: 对比服务的DACL与默认值
七、关联检查
服务后门通常与以下内容关联:
08-计划任务与服务审计 — 基础审计方法
18-Registry-Run后门 — 多种持久化并用
22-DLL劫持与侧加载 — 服务DLL劫持
19-计划任务后门 — 常常同时部署
17-Systemd-Service后门 — Linux等效机制
八、应急响应Checklist
[ ] 使用 sc query type= service state= all 列出所有服务
[ ] 检查所有非系统签名的服务二进制文件
[ ] 检查DLL服务的ServiceDll路径和签名
[ ] 查找Unquoted Service Path漏洞
[ ] 检查所有服务的FailureCommand
[ ] 检查svchost组注册表中的异常条目
[ ] 查询Event ID 7045/4697事件
[ ] 查询Event ID 7040(启动类型变更)
[ ] 使用Autoruns验证服务自启动项
[ ] 证据收集后清除恶意服务和文件