跳转到帖子

Windows 服务目录弱权限导致 SYSTEM 本地提权的验证与修复

精选回复

发布于

背景

Windows 服务通常由 LocalSystem、LocalService 或 NetworkService 账户运行。如果普通用户对服务可执行文件、上级目录或服务对象拥有写入权限,就可能跨越本地权限边界。典型结果是:攻击者先获得普通域用户或本地用户权限,再借助服务重启或系统重启,使受控文件以高权限账户执行。

该问题通常属于安装程序 ACL 配置不当,可参考 CWE-732 和 CWE-276。它不同于“未加引号的服务路径”:前者关注文件、目录和服务对象权限,后者关注路径解析顺序,排查时不应混为一谈。

适用场景:已获得明确授权的 Windows 终端或服务器检查。生产环境验证应避免替换服务文件,优先使用 ACL、服务 DACL 和日志证据完成确认。

典型漏洞场景

某资产管理代理安装在 C:\Program Files\Acme Agent\agent.exe,服务名为 AcmeAgent,登录账户为 LocalSystem。安装程序错误地向 Users 或 Authenticated Users 授予了目录 Modify 权限。

漏洞成立通常需要同时满足以下条件:

  • 服务运行账户权限高于当前测试账户,例如 LocalSystem。
  • 低权限主体可修改服务二进制文件,或可在其目录中删除、重命名并重新创建该文件。
  • 攻击者能够重启服务、等待管理员重启服务,或等待主机重启。
  • 终端防护、应用控制或文件完整性机制未阻止异常文件执行。

另一条独立链路是服务对象 DACL 过宽。如果普通用户具有 SERVICE_CHANGE_CONFIG,并且还能启动服务,就可能修改 ImagePath。即使原始服务目录不可写,这种情况仍应按高危权限边界问题处理。

问题定位

首先记录服务名称、运行账户、启动方式和原始二进制路径。不要只检查注册表中的 ImagePath,因为环境变量、参数和引号可能影响最终解析结果。

whoami /user
whoami /groups

sc.exe qc AcmeAgent
sc.exe sdshow AcmeAgent

powershell.exe -NoProfile -Command "Get-CimInstance Win32_Service -Filter 'Name=''AcmeAgent''' | Format-List Name,DisplayName,State,StartMode,StartName,PathName"

reg.exe query "HKLM\SYSTEM\CurrentControlSet\Services\AcmeAgent" /v ImagePath

重点核对以下三类权限:

  • 二进制文件 ACL:普通用户是否拥有 Write、Modify、Delete、ChangePermissions 或 FullControl。
  • 父目录 ACL:即使文件本身仅允许读取,只要目录允许删除、重命名或创建文件,也可能完成替换。
  • 服务对象 DACL:普通用户是否拥有修改配置、启动、停止或完全控制服务的权限。

批量筛查高权限服务的弱 ACL

下面的 PowerShell 脚本枚举由内置高权限账户运行的服务,并检查服务文件及其父目录中授予 Everyone、Authenticated Users 和本地 Users 组的允许型写权限。脚本只做读取,不修改服务配置。

$weakSids = @(
    'S-1-1-0',       # Everyone
    'S-1-5-11',      # Authenticated Users
    'S-1-5-32-545'   # BUILTIN\Users
)

$writeMask = [System.Security.AccessControl.FileSystemRights]::WriteData `
    -bor [System.Security.AccessControl.FileSystemRights]::AppendData `
    -bor [System.Security.AccessControl.FileSystemRights]::WriteAttributes `
    -bor [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes `
    -bor [System.Security.AccessControl.FileSystemRights]::Delete `
    -bor [System.Security.AccessControl.FileSystemRights]::ChangePermissions `
    -bor [System.Security.AccessControl.FileSystemRights]::TakeOwnership

Get-CimInstance Win32_Service |
Where-Object {
    $_.StartName -match 'LocalSystem|LocalService|NetworkService'
} | ForEach-Object {
    $svc = $_
    $raw = [Environment]::ExpandEnvironmentVariables($svc.PathName.Trim())
    $bin = $null

    if ($raw -match '^"([^"]+)"') {
        $bin = $Matches[1]
    }
    elseif ($raw -match '^([A-Za-z]:\\.*?\.exe)\b') {
        $bin = $Matches[1]
    }

    if (-not $bin -or -not (Test-Path -LiteralPath $bin)) {
        return
    }

    $targets = @($bin, (Split-Path -LiteralPath $bin -Parent)) |
        Select-Object -Unique

    foreach ($target in $targets) {
        try {
            $acl = Get-Acl -LiteralPath $target -ErrorAction Stop
        }
        catch {
            continue
        }

        foreach ($ace in $acl.Access) {
            if ($ace.AccessControlType -ne 'Allow') {
                continue
            }

            try {
                $sid = $ace.IdentityReference.Translate(
                    [System.Security.Principal.SecurityIdentifier]
                ).Value
            }
            catch {
                continue
            }

            if (($weakSids -contains $sid) -and
                (($ace.FileSystemRights -band $writeMask) -ne 0)) {
                [pscustomobject]@{
                    Service   = $svc.Name
                    RunAs     = $svc.StartName
                    Target    = $target
                    Principal = $ace.IdentityReference
                    Rights    = $ace.FileSystemRights
                    Inherited = $ace.IsInherited
                }
            }
        }
    }
} | Format-Table -AutoSize

该脚本用于发现候选项,不等同于 Windows 的最终有效权限计算。显式拒绝 ACE、组嵌套、所有者权限和特殊权限可能改变结果。发现候选项后,应使用 icacls 或 AccessChk 复核。

非破坏性验证步骤

在授权维护窗口内,可按以下顺序验证。建议先保存 ACL、文件哈希和服务配置,便于回滚和审计。

sc.exe qc AcmeAgent > C:\Windows\Temp\AcmeAgent_sc_qc.txt
sc.exe sdshow AcmeAgent > C:\Windows\Temp\AcmeAgent_sddl.txt

icacls.exe "C:\Program Files\Acme Agent"
icacls.exe "C:\Program Files\Acme Agent\agent.exe"

powershell.exe -NoProfile -Command "Get-FileHash -Algorithm SHA256 -LiteralPath 'C:\Program Files\Acme Agent\agent.exe'"

随后切换到待验证的低权限账户,只在目标目录创建并删除一个探针文件。该操作可以证明目录创建权限,但不会证明一定能够覆盖正在运行且被锁定的服务文件。

whoami
cmd.exe /c "echo acl_probe>C:\Program Files\Acme Agent\.acl_probe"
type "C:\Program Files\Acme Agent\.acl_probe"
del "C:\Program Files\Acme Agent\.acl_probe"

如果探针文件创建成功,再检查该账户是否具有服务控制权限。不要直接停止生产服务,也不要修改 ImagePath。

sc.exe query AcmeAgent
sc.exe stop AcmeAgent

正常情况下,普通用户执行停止操作应返回 Access is denied。如果业务不允许实际发出停止请求,可以使用 Sysinternals AccessChk 只读检查服务对象权限:

accesschk64.exe -accepteula -uwcqv "Authenticated Users" AcmeAgent
accesschk64.exe -accepteula -uwcqv "BUILTIN\Users" AcmeAgent

判定时应区分以下情况:

  • 普通用户可修改服务二进制文件,并可停止和启动 LocalSystem 服务:通常判定为严重。
  • 普通用户可修改二进制文件,但无法控制服务,服务会随主机启动:通常判定为高危,利用依赖重启条件。
  • 普通用户具有 SERVICE_CHANGE_CONFIG 和启动服务权限:通常判定为严重,即使服务目录不可写。
  • 仅具有读取、读取并执行权限:不构成该漏洞。
  • 仅路径未加引号,但路径各级目录均不可写:应单独记录为配置缺陷,不能直接认定可利用。

日志留痕与溯源

服务弱权限被利用时,最有价值的证据通常位于服务注册表项、目标目录和进程创建日志中。Windows 默认日志未必完整,关键服务器建议预先启用进程创建命令行、注册表对象审核和目标目录 SACL。

可优先检查以下事件:

  • System 7035、7036:服务收到控制请求及状态变化。
  • System 7045、Security 4697:新服务安装。它们不能覆盖所有现有服务配置修改场景。
  • Security 4657:注册表值被修改,需要提前配置注册表审核。
  • Security 4688:新进程创建,建议启用命令行记录。
  • Security 4663:文件对象访问,需要对服务目录配置 SACL。
  • Sysmon 1、11、13:进程创建、文件创建和注册表值设置。
wevtutil.exe qe System /q:"*[System[(EventID=7035 or EventID=7036 or EventID=7045)]]" /f:text /c:50

powershell.exe -NoProfile -Command "Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4657,4688,4697} -MaxEvents 100 | Format-List TimeCreated,Id,Message"

reg.exe query "HKLM\SYSTEM\CurrentControlSet\Services\AcmeAgent" /v ImagePath

调查时应将服务状态变化时间、ImagePath 修改时间、二进制文件创建时间、SHA-256 哈希和 4688/Sysmon 1 中的父子进程关系进行关联。如果服务进程的父进程、签名或文件路径与软件基线不一致,应先隔离资产并保全文件,不要立即覆盖现场。

修复建议

修复重点是收紧文件系统 ACL 和服务对象 DACL,而不是简单删除可疑文件。修改前应确认软件升级程序、补丁代理和专用服务账户是否需要写权限。

  • 服务程序目录仅向 SYSTEM、Administrators 和受控的软件更新账户授予写权限。
  • 普通 Users 通常只需 ReadAndExecute,不应拥有 Modify、Delete 或 Write。
  • 从父目录继承了过宽权限时,应在软件部署模板或安装包中修正,避免升级后再次出现。
  • 服务对象 DACL 应从同版本、同组件的可信主机或厂商基线恢复,不建议盲目套用通用 SDDL。
  • 非必要服务不要以 LocalSystem 运行,优先使用虚拟服务账户或权限受限的专用账户。
  • 结合 WDAC、AppLocker、代码签名校验和 EDR 文件完整性监控限制异常二进制执行。

以下命令展示一种常见的 ACL 收敛方式。使用 SID 可以避免中英文系统组名差异,但执行前仍需核对应用更新机制,并先在测试环境验证。

icacls.exe "C:\Program Files\Acme Agent" /save C:\Windows\Temp\AcmeAgent_acl_backup.txt /t

icacls.exe "C:\Program Files\Acme Agent" /remove:g *S-1-1-0 *S-1-5-11
icacls.exe "C:\Program Files\Acme Agent" /grant:r "*S-1-5-18:(OI)(CI)F" "*S-1-5-32-544:(OI)(CI)F" "*S-1-5-32-545:(OI)(CI)RX"

icacls.exe "C:\Program Files\Acme Agent"
icacls.exe "C:\Program Files\Acme Agent\agent.exe"

其中 S-1-5-18 为 SYSTEM,S-1-5-32-544 为本地 Administrators,S-1-5-32-545 为本地 Users。若软件使用专用更新账户,应单独为该账户配置最小必要权限,而不是重新向 Authenticated Users 开放写入。

修复验证

修复后应重新执行候选项筛查,并使用原低权限账户重复探针文件测试。预期结果是创建文件失败,服务仍可正常启动,软件升级和自更新功能不受影响。

  • 确认普通用户无法在服务目录创建、删除或重命名文件。
  • 确认普通用户无法修改、停止或启动高权限服务。
  • 确认服务二进制哈希和数字签名与可信版本一致。
  • 重启服务或测试主机,检查服务功能及依赖项。
  • 将修复后的 ACL、服务 SDDL、文件哈希和验证时间写入整改记录。

总结

Windows 服务弱权限的核心不是“服务以 SYSTEM 运行”本身,而是低权限主体获得了影响高权限执行路径的能力。排查时需要同时覆盖服务二进制、父目录和服务对象 DACL,并结合服务重启条件判断实际风险。生产环境优先采用只读枚举、ACL 复核和日志关联完成验证;修复后还需测试软件更新流程,防止安装程序再次写回过宽权限。

服务器与云环境安全示意图
服务器、云资产与权限边界梳理示意
代码审计与漏洞定位示意图
代码审计、调用链与关键函数定位示意

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

最近浏览 0

  • 没有会员查看此页面。