深入解析JWT DoS漏洞与Linux内核KASLR绕过技术

本文详细分析了JOSE库中因PBES2迭代次数未验证导致的拒绝服务漏洞,以及Linux内核中权限容器内KASLR绕过漏洞的技术原理、影响范围和修复方案,涵盖加密算法实现和内核权限控制机制。

从内核到令牌:Trail of Bits漏洞挖掘纪实

揭秘JOSE库中的拒绝服务漏洞

JSON Web Tokens(JWT)和JSON对象签名与加密(JOSE)是描述基于JSON的加密和/或签名令牌创建与使用的广泛标准。虽然这些标准被广泛使用且相比之前的身份声明解决方案有显著改进,但它们并非没有缺陷,存在多个众所周知的陷阱,如JWT"none"签名算法。

我们的发现涉及Tom Tervoort在BlackHat USA 2023上提出的新型JWT攻击系列中的一种:“针对JSON Web令牌的三种新攻击”。其中"十亿哈希攻击"由于JWT密钥加密缺乏验证导致拒绝服务,引起了我们同事Matt Schwager的注意。进一步检查后,他发现该漏洞还影响Go和Rust生态系统中的多个库:go-jose、jose2go、square/go-jose和josekit-rs。

这些库都支持使用PBES2进行密钥加密,该功能旨在允许基于密码对JSON Web加密(JWE)中的内容加密密钥(CEK)进行加密。首先通过PBES2方案从密码派生密钥,该方案执行一定次数的PBKDF2迭代。然后使用该密钥加密和解密令牌内容。

这通常不会成为问题,但不幸的是,迭代次数作为令牌的一部分包含在p2c头部参数中,攻击者可以轻松操纵。例如,考虑下面显示的令牌头部:

图1:显示使用大量迭代进行PBES2密钥加密的JWE令牌头部

通过在p2c字段中使用非常大的迭代计数,攻击者可以对任何尝试处理此令牌的应用程序造成拒绝服务。任何接收并尝试验证此令牌的人都首先需要执行2,147,483,647次PBKDF2迭代来派生CEK,然后才能验证令牌是否有效,这将消耗大量的计算时间。

我们向go-jose、jose2go和josekit-rs库维护者报告了此问题,并通过限制p2c的最大值进行了修复:go-jose/go-jose版本3.0.1(提交65351c27657d);dvsekhvalnov/jose2go版本1.6.0(提交a4584e9dd712和8e9e0d1c6b39);hidekatsu-izuno/josekit-rs版本0.8.5(提交1f3278a33f0e、8b60bd0ea8ce和7e448ce66c1c)。square/go-jose仍未修复,因为该库已弃用,鼓励用户迁移到go-jose/go-jose。

或者,也可以通过不纯粹依赖令牌的alg参数来降低风险。毕竟,如果您的应用程序不期望接收使用PBES2或任何较少使用算法的令牌,就没有理由尝试处理一个。jose2go允许今天实现对alg和enc参数的选择性更严格验证,而go-jose的下一个主要版本将在处理令牌时要求传递可接受算法列表,允许开发人员明确列出一组预期算法。

无权限容器中的KASLR绕过

接下来是一个自2020年以来已修复但从未被Linux内核维护者分配CVE的漏洞。在以下段落中,我们将深入探讨一个先前未知但已修复的KASLR绕过细节。

早在2020年,Trail of Bits工程师Dominik Czarnota(又名disconnect3d)发现Linux内核中的一个漏洞可能暴露无权限Docker容器内的内部指针地址,允许恶意行为者绕过内核模块的内核地址空间布局随机化(KASLR)。

KASLR是操作系统中的重要防御机制,主要用于阻止利用尝试。它是一种安全技术,在内核重启之间随机化内核内存地址位置。除此之外,内核地址必须对用户空间隐藏;否则,缓解措施将毫无意义,因为此类内核地址披露将有效绕过KASLR缓解。

虽然有些地方内核地址会显示给用户空间程序,但在许多系统上,它们应该仅在用户具有CAP_SYSLOG Linux能力时才可用。(能力拆分root用户权限,因此可能成为root用户或具有uid 0的用户,同时拥有有限的权限集。)特别是,CAP_SYSLOG能力的手册页写道:“当/proc/sys/kernel/kptr_restrict的值为1时,查看通过/proc和其他接口暴露的内核地址。“这意味着只有使用CAP_SYSLOG能力执行的进程才能读取内核地址。

然而,Dominik发现,在Docker容器中情况并非如此,从root用户运行但没有CAP_SYSLOG的进程能够观察内核地址。默认情况下,Docker容器是无权限的,这意味着root用户在其能做的事情上受到限制(例如,他们无法执行需要CAP_SYSLOG的操作)。这也可以在不使用Docker的情况下通过使用从root用户运行的capsh工具移除CAP_SYSLOG能力来演示:

该问题的根本原因是凭据检查不正确。sysctl切换kernel.kptr_restrict指示是否对暴露内核地址施加限制:值"2"表示地址始终隐藏;“1"表示仅当用户具有CAP_SYSLOG时才显示;“0"表示始终显示。不是在显示地址之前确保用户具有CAP_SYSLOG能力,而是仅考虑kptr_restrict的值来决定是否显示或隐藏地址。如果kptr_restrict为1,地址总是暴露,而如果用户没有CAP_SYSLOG,它们应该被隐藏。该问题在提交b25a7c5af905中修复。

发现此漏洞后,我们与Docker和Linux内核安全团队遵循了协调披露流程。Dominik最初通知了Docker团队,因为他认为漏洞源自Docker,并报告了其他sysfs文件系统泄漏(其他sysfs路径泄漏信息,如容器外运行的服务名称、其他容器ID和设备信息)。披露时间线在本文末尾提供。

尽管我们多次请求更新,但Docker仅保持沉默,而Linux社区迅速在内核中纠正了问题。KASLR绕过错误修复被反向移植到各种Ubuntu LTS版本,而Docker的其他sysfs泄漏根本没有修复。然而,Linux内核4.19之前的版本容易受到KASLR绕过的影响。使用内核4.15的Ubuntu 18仍然容易受到攻击,因为修复没有被反向移植。

无权限容器中KASLR绕过的披露时间线

  • 2020年6月6日:向Docker报告漏洞
  • 2020年6月11日:Docker回复称他们可能通过"屏蔽路径"功能阻止泄漏信息的sysfs路径,内存地址披露应报告给Linux内核开发人员
  • 2020年6月11日:告知意图联系security@kernel.org关于kASLR绕过
  • 2020年6月11日至18日:对kASLR绕过进行更深入分析
  • 2020年6月18日:向security@kernel.org报告错误
  • 2020年6月18日:Kees Cook确认错误
  • 2020年6月19日至21日:内核开发人员讨论如何修补问题
  • 2020年6月30日:向Docker请求更新
  • 2020年7月3日至14日:修复问题的补丁进入Linux内核
  • 2020年7月11日:再次向Docker请求更新关于其他sysfs泄漏,并告知KASLR绕过问题已在Linux 4.19、5.4和5.7内核中修复
  • 2020年12月3日:再次向Docker请求更新,并告知意图公开披露问题。Docker未回复

2024年需要审计吗?

这两个漏洞截然不同:DoS问题涉及解析和解释用户输入,而内核漏洞是信息泄漏(严格来说,是访问控制漏洞)。这些差异影响错误的可检测性:如果您导致DoS,您可能会立即注意到,因为服务的可用性将受到损害。相比之下,如果攻击者利用访问控制漏洞,您可能不会注意到服务被利用时。

这种可检测性差异对于自动化测试很重要。例如,如Trail of Bits测试手册中展示的模糊测试,通常要求程序崩溃或挂起。因此,我们主要在内存安全程序中发现DoS错误。通过模糊测试自动找到访问控制错误更具挑战性,因为它需要实现模糊测试不变量。

安全审计仍然是查找漏洞不可或缺的工具,就像模糊测试一样!我们的审计尽可能集成模糊测试,并寻找强制执行不变量的机会以捕获讨厌的逻辑错误。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计