作为我们对 IBM i 平台持续研究的一部分,我们会监控与该平台相关的新闻和更新。两周前,IBM 发布了一篇支持文章,介绍了在 Windows 11 24H2 上运行时影响 IBM i Access Client Solutions (ACS) 的兼容性问题。系统边界之间的“无人区”始终是黑客的游乐场,这篇文章非常有趣,因为它指出了 Windows 的本地安全机构子系统:
IBM ACS 应用程序包 *WINLOGON 支持(“使用 Windows 用户名和密码,无提示”)与 LSA 保护不兼容。
LSA 负责系统上凭据的安全处理,因此它显然是攻击的目标 - 例如,臭名昭著的Mimikatz工具因巧妙地解析 LSASS.exe 进程内存中存放凭据的各种机密而出名。LSA Protection 是 Microsoft 开发的一组安全功能,用于保护凭据,即使是特权本地攻击者也无法对其进行攻击。
难道 IBM 正在像 Mimikatz 一样利用 LSASS 玩弄阴谋诡计?!这个问题开启了我们进入 ACS 的旅程。
侦察
在深入字节的海洋之前,通常值得寻找可以帮助我们工作的现有研究和相关文档。
首先,支持文章还指出了这一点:
Windows 11 24h2 更新在安装时启用了本地安全机构 (LSA):https://support.microsoft.com/en-us/windows/inside-this-update-93c5c27c-f96e-43c2-a08e-5812d92f220d
链接的MS 文章中唯一相关的点似乎是:
升级时启用本地安全机构 (LSA) 保护:系统升级期间自动增强安全性
目前尚不清楚,而且很奇怪,某些“系统升级期间”的限制如何影响第三方软件的整体可用性。我们找不到有关此 Windows 附加保护的更多信息,并且根据我们的进一步结果,此特定更改似乎不太可能影响 ACS。
在搜索*WINLOGON身份验证模式时,我们发现了一篇来自 IBM 的更有用的文章:
此更新禁用了用于启用 *WINLOGON 身份验证支持的“客户端访问网络”网络提供程序。要重新启用,请按照以下步骤操作:
打开 regedit.exe
导航到 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlNetworkProviderOrder
编辑 Provid
erOrder 值并将 Cwbnetnt 添加到以逗号分隔的提供商列表的开头
重新启动系统如
果遇到问题,请联系 IBM 支持寻求帮助。
现在注册表路径看起来非常有希望!另一次搜索将我们带到了Grzegorz Tworek和网络提供程序 DLL 攻击技术 (T1556.008)的研究!
简而言之,特权用户可以通过之前的注册表项在系统上注册网络提供商 DLL。用户登录时,新生成的进程会自动加载已注册的 DLL ,并通过DLL 导出的回调将明文凭据mpnotify.exe传递给用户。NPLogonNotify()
因此,好消息是 ACS 可能不会利用我们最敏感的 Windows 进程来玩弄 Mimikatz。坏消息是 ACS 可能会收集纯文本 Windows 凭据,而安全处理此类数据非常困难。
我们的高层理论现在是这样的:
-
当用户通过 登录时, ACS会获取纯文本 Windows 密码mpnotify.exe。由于 Windows 和 IBM i 使用的加密算法不同,因此需要纯文本凭据。
-
ACS 希望使用这些凭证登录远程 IBM i 系统。请注意,此时,mpnotify.exe其中加载的网络提供商不再运行。
-
因此,ACS必须保留凭证,以便以后可以用于远程系统连接。
为了验证这个理论,我们应该检查实施情况!
一个简单的方法是启动 ProcMon,过滤mpnotify.exe,并查找可能保存密码的任何持久数据存储操作。果然,记录的注册表写入这个值看起来有点可疑:
HKLMSOFTWAREWow6432NodeIBMClient AccessCurrentVersionVolatileCommunicationTime Stamps.windows<USER>Function Admin Timestamp
现在,如果您比我聪明,您可以在网上搜索“功能管理时间戳”,并找到Tenable 于 2016 年发表的这篇文章,其中描述了 IBM System i Navigator 使用弱混淆将 Windows 密码存储在所有本地用户可读的注册表值中。如果您不那么聪明,您最终会自己逆转整个过程,甚至可能编写一个反混淆器:)
此时,我们可以将我们的工作记为 DUPLICATE 并喝一杯,但时间线仍然困扰着我们:这个问题在 2016 年就有一个 CVE,但 IBM 直到 2024 年才决定弃用该*WINLOGON功能,甚至在 2025 年初他们还必须记录兼容性问题。这应该意味着该功能仍然存在,并且可能仍会为我们提供一些不错的泄漏!
测试环境
IBM 的产品命名很……特殊,并且会随着时间而变化,所以让我们总结一下我们这个小游戏的主要参与者:
System i Navigator是 Tenable 调查的产品。
-
iSeries Navigator是 V5 时代的老产品(IBM i 现在使用的是 V7 版本)。我们的测试实验室安装了 2005 版本。我们用这个版本逆转了原来的混淆,这对我们新的 ACS 产品有很大帮助。
-
现在,如果您下载 IBM i Access Client Solutions,您将获得一个基于 Java 的应用程序(IBM i Navigator),它不会与登录过程交互。
-
有一个名为“ ACS Windows App Pkg”(又名“应用程序包”)的单独包,它为 Windows 开发人员提供集成工具(如 ODBC 驱动程序),以便他们的软件可以与 IBM i 交互。
按照本指南,您可以配置已安装 Navigator 和应用程序包的“共享登录”(在 IBM i 上使用 Windows 凭据)。
受影响的配置
我们无法获取最新版本的应用程序包来注册网络提供程序 DLL - 这可能是弃用的结果,并修复了所描述的所有问题。我可以通过查找cwbnetnt.dll仍在自行注册的 2019 年应用程序包(应用程序包版本 11.26.00、版本 13.64.26.0)来确认 2024 年之前版本的相关性。
您是否知道还有GUI可以查看注册提供商?
此版本对“函数管理时间戳”下的值设置了严格的 ACL,这可能是对 Tenable 的 CVE-2016-0287 的修复。由于我认为这不足以缓解问题(本文末尾有更多相关内容),因此我也尝试为该版本编写了一个反混淆器。
逆转
显而易见的起点是CWBNETNT.DLL(参见上面的引文)实现网络提供程序。查看反编译的NPLogonNotify导出,我们立即看到相关库的动态解析,这可能会完成繁重的工作。在 2005 版本中,此导出解析了结构lpAuthentInfo,而 2019 版本只是将整个内容传递给核心 DLL:
hModule = LoadLibraryW(L"cwbcore.dll");
if (hModule != (HMODULE)0x0) {
coreFuncPtr = GetProcAddress(hModule,(LPCSTR)0x5b7);
if (coreFuncPtr != (FARPROC)0x0) {
(*coreFuncPtr)(lpAuthentInfoType,lpAuthentInfo,lpStationName);
FreeLibrary(hModule);
}
}
return 0;
通过在注册表写入事件中从 ProcMon 获取堆栈跟踪,我们可以看到密码数据的去向。
注册表操作的堆栈跟踪(嘿,这是 Win11 吗?!)
我们将其称为“ Admin_Timestamp()”函数,因为此处直接引用了注册表值的名称来进行操作RegSetValue。通过使用调试器中断此函数,mpnotify.exe我们可以看到该函数是使用明文密码数据调用的。但是我们如何调试呢mpnotify.exe?
有两个小技巧:
-
目标进程以 SYSTEM 身份运行,因此我们使用 PSExec 生成具有相同权限的调试器(psexec -i -s)。
-
我们静态修补NPLogonNotify()以包含无限循环,从而让我们有时间进行连接。我们采用这种方法是因为自动注入进程(例如通过 AppInit DLL)不能保证我们可以与调试器交互,因为我们无法访问 SYSTEM 桌面(并且我们不想为此设置 KD……)。连接调试器后,可以禁用无限循环。专业提示:修补后不要退出测试系统(我无意中这样做了),因为没有调试器,后续登录将永远无法完成
这很简单,那么现在我们的数据会发生什么呢?通过浏览Admin_Timestamp()这个导出函数的调用树(两个版本中的序号为#1470),我们注意到了它奇怪的异或运算:
char * Ordinal_1470(char *key0,char *key1,char *pw0,char *pw1,ulonglong size)
{
char cVar1;
char cVar2;
uint counter;
ulonglong i;
if (size != 0) {
i = 0;
counter = 0;
do {
cVar2 = key1[(uint)i & 7];
cVar1 = pw0[i];
pw1[i] = cVar2 + cVar1;
pw1[i] = key0[counter % 7] ^ cVar2 + cVar1;
i = (ulonglong)(counter + 1);
counter = counter + 1;
} while (i < size);
}
return pw1;
}
该函数被调用两次Admin_Timestamp(),我们可以确认第一次调用接收明文密码,第二次调用返回的缓冲区存储在注册表中。
所以这不是加密质量,但也许密钥是以某种特殊的、牢不可破的方式生成的?
嗯,不完全是。在第一轮中,和Ordinal_1470参数来自当前时间戳和滴答计数。现在这是一个问题 - 至少对于合法用户来说 - 因为当您想要访问存储的凭据时,您将不再拥有滴答计数。因此在第二轮中,这些第一个密钥被附加到第一轮结果的开头,并且操作使用主机名的前 8 个字符作为密钥,并且key0key1
-
在 2005 版本中:一个看似随机的 2 字节值“endian-mirrorred”为两个 DWORDS(例如DEAD0000 0000ADDE:)
-
在 2019 版本中:从机器的 Build GUID 和 Product ID 派生出的 8 字节值。
请注意,2005 版本的密钥仍然具有随机性 - 难怪我们无法在实验室中使连接工作……2019 版本中的推导通过将连接的输入字符串“XOR 折叠”到目标缓冲区来实现 - 可以通过查看引用所使用的全局变量的函数来找到生成算法Admin_Timestamp():
while (currentChar != 0) {
(&obfuscator_round2_key0)[idx] = (&obfuscator_round2_key0)[idx] ^ *wBuildGuidprodId;
wBuildGuidprodId = wBuildGuidprodId + 2;
idx = (ulonglong)((int)idx + 1U & 7);
currentChar = *(short *)wBuildGuidprodId;
}
要进行反混淆,我们需要注册表中的 blob、主机名、Build GUID 和 OS 产品 ID。有了这些,我们可以反转第二轮以获取基于时间的密钥,并反转第一轮以获取明文密码。
从这些输入来看,密钥可以被视为公共数据,但自 2019 年起,blob 已受注册表 ACL 保护。这有什么大不了的?
红队成员的看法
到目前为止,我们已经看到 IBM ACS 基本上将 Windows 密码存储为纯文本,但该产品的较新版本使用注册表 ACL 保护这些数据。
红队(和现实世界的攻击者)倾向于按照已建立的信任关系进行操作,因此他们在通往王冠明珠的每一步中都不需要昂贵且可能嘈杂的漏洞(这将允许突破安全边界)。文章开头提到的 LSA 保护旨在限制对本地存储凭据的访问,即使攻击者获得管理权限也是如此。请注意,本地凭据访问是攻击者倾向于使用的最常见和最有用的技术之一,难怪微软投入了相当大的精力来强化这一领域。
存储纯文本凭证会使所有这些努力付诸东流,而使用模糊处理和看似无害的注册表项主要欺骗的是安全团队,而不是攻击者(逆向工程这种廉价技巧是我们赖以生存的手段!)。值得一提的是,纯文本凭证特别有用,因为它们可以在传递哈希不起作用的接口上使用(例如:RDP、Web 应用程序)。在许多情况下(例如使用RAT时),攻击者无法访问纯文本凭证。
因此,如果您是蓝队成员,请务必快速扫描“功能管理时间戳”键并尽快将其删除。
但是有一个问题:LSASS 内存不会保存在备份中,而注册表配置单元会……
凭证存储的替代方法
正如我们之前所写,保护 Windows 凭据(考虑到现代的、野外的威胁)是一个难题,因此我们只是快速强调了一些替代方法及其一些缺点:
-
我们不能要求用户提供额外的密码并使用对称加密来保护收集到的凭证,因为我们的目的*WINLOGON就是为了让用户免于输入凭证的麻烦。
-
Windows DPAPI为开发人员提供了将存储的机密与当前用户的登录凭据绑定的工具。理论上,网络提供商可以使用 DPAPI 存储明文密码,但其合法所有者和(在某些情况下)管理员仍可访问这些密码。
-
对密码进行散列处理,以使其对 IBM i 身份验证仍然有用(就像 NTLM 的情况一样),解决了纯文本问题,但仍然可能为攻击者提供可有效破解或重用的数据,至少可以连接到 IBM i。
请注意,我们在这里处理的是单点登录问题:使用 Kerberos 可以消除密码处理问题,而不是重新发明轮子(很糟糕)。不幸的是,甚至 IBM 也承认Kerberos 在他们这边“配置起来并不容易”……
那么 Windows 11 怎么样?
如果我们观察 Windows 11 24H2 上的注册表访问,我们会发现一些奇怪的事情:
-
mpnotify.exe尝试打开注册表项
-
由于密钥不存在,因此失败
-
然后它不会像预期的那样尝试创建路径,而是直接退出
Win11 24H2 放弃注册表访问
我们尝试手动创建子树,发现即使“Function Admin Timestamp”值存在,也要mpnotify.exe 删除它然后退出!
我们还观察到一些有趣的行为:注册表操作发生在 SOFTWAREWow6432Node 树中,如果 SOFTWARE 下不存在相同的子树,则该过程打开 ...windowsuserb1 键,但开始对 ...windowsNew Value #1 oO 进行操作,这可能值得进一步调查!
ProcMon 显示的堆栈有点混乱,但我们可以再次追溯该行为Admin_Timestamp():
void credhandler_Admin_TimeStamp_1800d9440
(wchar_t *buf,wchar_t *path,wchar_t *password0,longlong param_4,int param_5)
{
// ... Variable declarations ...
if ((path != (wchar_t *)0x0) && (password0 != (wchar_t *)0x0)) { // NULL ptr check
pwVar1 = buf + 4;
Ordinal_1645((longlong *)pwVar1,path);
if (*password0 == L' ') { // Zero-length passowrd - We hit this path!
local_3f8 = 7;
local_400 = 0;
local_410 = L' ';
pw_len = 0xffffffffffffffff;
do {
pw_len = pw_len + 1;
} while (L"Function Admin Timestamp"[pw_len] != L' ');
FUN_1800088c0((ulonglong *)&local_410,L"Function Admin Timestamp",pw_len);
Reg_Open_Delete_180075f40((longlong)buf,&local_410,0x10,4); // Registry access
if (7 < local_3f8) {
operator_delete((void *)CONCAT62(uStack_40e,local_410));
}
}
else {
// ... Store obfuscated password ...
}
调试入口点cwbnetnt.dll也确认密码信息不再传递给网络提供商!
Win11 24H2 中 cwbcore.dll 的 lpAuthentInfo 传递了零长度
微软在2024 年 3 月记录了这一变化,我们认为 IBM 应该在他们的备忘录中引用此文档。这是微软的一个重要变化——希望没有太多应用程序依赖这个后门,并且它们的不安全工件得到妥善清理!在进攻方面,快速搜索WinbindexNPLogonNotify()上的实施者可能会产生一些额外的有趣研究目标
与往常一样,我们的代码在 GitHub 上:silentsignal/ACS-dump-https://github.com/silentsignal/ACS-dump
标题图片来自Fortepan,展示了匈牙利传统“偷酒器”的正确使用方法
感谢您抽出
.
.
来阅读本文
点它,分享点赞在看都在这里
原文始发于微信公众号(Ots安全):漏洞考古:利用 IBM i Access Client 解决方案窃取密码
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论