JWT与Linux内核漏洞:DoS攻击与KASLR绕过技术解析

本文深入分析JSON Web Tokens(JWT)中的拒绝服务漏洞与Linux内核KASLR绕过漏洞的技术细节,涵盖PBES2密钥加密机制缺陷、内核地址泄露原理及修复方案,为开发者提供安全实践指导。

走出内核,深入令牌 - Trail of Bits博客

Emilio López, Max Ammann
2024年3月8日
应用安全, Linux, 漏洞披露

我们正在挖掘Trail of Bits多年来报告的漏洞档案。本文分享两个此类问题的故事:隐藏在JSON Web Tokens(JWT)中的拒绝服务(DoS)漏洞,以及Linux内核中可能绕过关键内核安全机制(KASLR)的疏忽。

解析JOSE库中的DoS漏洞

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

我们的发现涉及Tom Tervoort在BlackHat USA 2023上提出的新JWT攻击系列中的一种攻击:“三种针对JSON Web Tokens的新攻击”。“十亿哈希攻击”由于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字段中使用非常大的迭代计数,攻击者可以对任何尝试处理此令牌的应用程序造成DoS。任何接收并尝试验证此令牌的人首先需要执行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_SYSQLinux能力时才可用。(能力拆分root用户权限,因此可能成为root用户或具有uid 0的用户,同时拥有有限的权限集。)特别是,CAP_SYSQLOG能力的手册页写道:“当/proc/sys/kernel/kptr_restrict的值为1时,查看通过/proc和其他接口暴露的内核地址。”这意味着只有使用CAP_SYSQLOG能力执行的进程才能读取内核地址。

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

该问题的根本原因是凭据检查不正确。sysctl切换kernel.kptr_restrict指示是否对暴露内核地址施加限制:值“2”表示地址始终隐藏;“1”表示仅当用户具有CAP_SYSQLOG时才显示;而“0”表示它们始终显示。不是在显示地址之前确保用户具有CAP_SYSQLOG能力,而是仅考虑kptr_restrict的值来决定是否显示或隐藏地址。如果kptr_restrict为1,则地址始终暴露,而如果用户没有CAP_SYSQLOG,则应该隐藏它们。该问题在提交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日至6月18日:对kASLR绕过进行了更深入的分析。
  • 2020年6月18日:向security@kernel.org报告错误。
  • 2020年6月18日:Kees Cook确认错误。
  • 2020年6月19日至6月21日:内核开发人员讨论如何修补问题。
  • 2020年6月30日:向Docker请求更新。
  • 2020年7月3日至7月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错误。通过模糊测试自动找到访问控制错误更具挑战性,因为它需要实现模糊测试不变量。

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

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

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