CVE-2024-53704

2025 年 1 月 8 日,Sonicwall 公开了影响 SonicOS GEN6、GEN7、GEN8 硬件与虚拟化设备的一批漏洞,其中 CVE-2024-53704 被描述为 SSLVPN 身份验证绕过,本文对此漏洞进行简要分析。
没有证据表明该漏洞存在在野利用,但鉴于 PoC 已经公开,建议用户及时更新最新版本。
基本信息
Sonicwall 发布的公告中提到该漏洞为 SSLVPN 服务的身份验证绕过,影响 SonicOS GEN7 7.1.x 以及 GEN8 版本,该公告中没有给出任何具体信息。2025 年 1 月 9 日,ZDI 公开了关于该漏洞更详细的信息,我们可以得知漏洞出现在 SSLVPN 服务验证 session 的过程中,具体来说是 BASE64 格式的 session 验证存在缺陷,导致身份验证绕过。
SSLVPN 鉴权分析
本站的历史文章已经分析过 SonicOS GEN7 7.1.x 版本开始添加的安全启动策略,以及如何解密硬盘获取文件系统,因此这里不再赘述,根据公告,我们分别解包得到 NSv 7.1.2-7019 和 7.1.3-7015 两个版本的 sonicosv 程序,留作后续补丁分析。
由于 7.1.x 版本系统中已经不再留有 sonicosv 的 debug 版本,我们可以先分析 7.0.x 中的程序,看看 SSLVPN 服务是如何对请求进行鉴权的。
在 sonicosv 中,HTTP 请求会首先来到 httpServer 函数处理,它同时负责处理 api、sslvpn 等 web 请求。当我们尝试从网页端登录 SSLVPN 服务时,通过抓包发现发送的请求基本都是 /api/sonicos/xxx
,即 API 请求。进一步分析 httpServer 函数,会发现以下代码
1 | // ... |
当访问 /cgi-bin 路由时,程序会调用 processSslvpnHttpRequest 函数,从函数名称来看它是用于处理 SSLVPN 请求的。我们知道一般情况下 SSLVPN 服务都会有客户端应用,Sonicwall 也不例外。这个 VPN 客户端叫做 Netextender,用户可以使用客户端连接 vpn 服务器访问内网资源。
可以推测 /cgi-bin 下面的端点可能是客户端应用才会使用的,安装客户端并尝试连接服务,连接成功效果如下
进一步分析 processSslvpnHttpRequest 函数,发现其中注册了很多 /cgi-bin 端点,包括 /cgi-bin/welcome
、/cgi-bin/homepage
等,除了少数几个不需要身份验证之外,其它端点都需要进行 session 验证。
以 /cgi-bin/sessionStatus
为例,它对应的处理代码如下
1 | unsigned __int64 __fastcall sub_2F2EA51(unsigned int a1, __int64 a2, const char *haystack) |
从名称来看这个功能应该是用于获取 session 对应会话状态的,除去函数中大量的 LOG 信息,实际上它所执行的操作非常简单,首先使用 getSwapCookieFromHttpreq
函数尝试从请求中获取 Cookie 数据
1 | const char *__fastcall getSwapCookieFromHttpreq(const char *haystack) |
可以推断 Cookie 中应该包含一个 swap 参数,为 session 值。获取到 session 值以后使用 maybe_url_decode
进行 URL 解码,随后调用 sslvpnVerifyTunnelSession
进行 session 鉴权。
1 | __int64 __fastcall sslvpnVerifyTunnelSession(__int64 a1, const char *session, char a3) |
这个函数的实现也相对简单,它会遍历保存在内存中的 session_list
,尝试和用户提交的 session 进行比较,如果比较成功说明找到了对应会话,表示鉴权成功,反之失败。
7.0.x 实现的是一个相对常规的验证逻辑,我们再来看看 7.1.x 版本此处逻辑是否有修改。
分析 7.1.2-7019 版本程序,sessionStatus 处理函数代码如下
1 | __int64 __fastcall sessionStatus(unsigned int n2147483646, _DWORD *a2, double a3) |
新版本逻辑存在一些变动,例如 get_param
获取参数的方式、移除了很多 LOG 信息等。但最关键的是 session 验证逻辑发生了变化。
getSslvpnSessionFromCookie
函数内容如下
1 | __int64 __fastcall getSslvpnSessionFromCookie(const char *raw_str) |
现在 session 有两种解析方式,当长度为 32 时说明是未编码的数据,当长度为 44 时说明是 BASE64 编码的数据。数据解码之后都会调用 verifyCookieCheckSum
先来检查数据格式
1 | __int64 __fastcall verifyCookieCheckSum(_BYTE *raw_str, int size) |
传入的 32 字节 session 数据最后一个字节表示 checksum,计算方式为从第一个字节到第 31 个字节两两异或,最终得到的一个字节数据使用 BASE64 编码,编码后的结果第一个字节就是 checksum,放在 session 的第 32 个字节位置,接着会调用 find_cookie
函数开始校验 session。
漏洞分析
find_cookie
函数内容如下:
1 | __int64 __fastcall find_cookie(__int64 raw_str, unsigned int a2) |
我在代码中添加了一些注释,下面我们来详细解释一下查找 session 的流程。
session_list
是一个全局列表,用于保存已经登录的 VPN 用户会话信息,代码先使用 lock_list
锁定 session 列表,然后进入一个循环,先获取列表中的第一个成员,存放到 obj 变量。随后开始逐字节遍历从外部传入的 session 数据,和 obj 中保存的 session 比较,如果 32 个字节都比较成功,则说明找到了正确的会话信息,鉴权成功。
这里的问题在于,代码获取了外部传入 session 的一个字节之后,会先在 if 条件中判断获取到的这个字节是否为 NULL,如果是就释放 session 列表并返回 obj 对象。假设我们传入 session 为 \x00abcd
,那么 find_cookie
函数就会直接返回 session 列表中的第一个对象,跳过后续的字符验证,这样就可以直接获得列表中的第一个用户会话信息,造成身份验证绕过。此外,由于 session 由小写字母组成,我们可以控制第二个字节为 NULL,遍历第一个字节来获取 session 列表中的其它用户信息。
参考链接
https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2025-0003
https://www.zerodayinitiative.com/advisories/ZDI-25-012/
https://attackerkb.com/topics/UB3P3xHVAo/cve-2024-53704/rapid7-analysis
- Title: CVE-2024-53704
- Author: Catalpa
- Created at : 2025-01-28 00:00:00
- Updated at : 2025-01-29 08:46:43
- Link: https://wzt.ac.cn/2025/01/28/cve-2024-53704/
- License: This work is licensed under CC BY-SA 4.0.