Windows应急响应 - 20 Windows服务后门

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

查看特定服务配置:

1
sc qc <ServiceName>

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
// 恶意服务DLL最小化实现
#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);

// 恶意payload在这里执行
// CreateThread(..., PayloadFunction, ...);

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
# 设置FailureCommand
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

# 1. 检查可执行文件路径
$exePath = $null
if ($path -match '^"([^"]+)"') {
$exePath = $Matches[1]
} elseif ($path -match '^(\S+)') {
$exePath = $Matches[1]
}

# 2. 检查路径是否在可疑位置
if ($path -match "(?i)(Users\\Public|\\Temp\\|AppData|ProgramData)") {
$flags += "SUSPICIOUS_PATH"
}

# 3. 检查数字签名
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"
}

# 4. 检查Unquoted Path
if ($path -and $path -notmatch '^"' -and $path -match '\s' -and $path -notmatch '^C:\\Windows\\') {
$flags += "UNQUOTED_PATH"
}

# 5. 检查FailureCommand
$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
# 枚举所有DLL服务
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 因为PowerShell的sc是Set-Content别名
& sc.exe delete "MaliciousService"

注意: PowerShell中 scSet-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

# 导出Event ID 7045
wevtutil epl System "$evidence\System.evtx" "/q:*[System[(EventID=7045)]]"

# 复制可疑服务二进制
# Copy-Item "C:\path\to\suspicious.exe" "$evidence\" -Force

六、高级技术补充

6.1 服务DLL幻影加载(Phantom DLL in svchost)

在svchost组中注册一个不存在的DLL名,然后放置恶意DLL:

1
2
3
4
# 创建新的svchost组
$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验证服务自启动项

[ ] 证据收集后清除恶意服务和文件


上一章 目录 下一章
19-计划任务后门 Windows应急响应 21-WMI事件订阅后门