2022 年 6 月,Sonar Research 团队在多个加密电子邮件解决方案中发现了关键代码漏洞,包括 Proton Mail、Skiff 和 Tutanota。
这些面向隐私的网络邮件服务提供端到端加密,使通信在传输过程中和静态时都安全。我们的发现影响了他们的网络客户端,其中的消息是用用户的密钥解密的;移动客户端不受影响。
如果攻击者与恶意消息交互,这些漏洞将允许攻击者窃取电子邮件并冒充受害者。
该问题已得到解决,并且没有任何野外利用的迹象。
我们的前两篇文章讨论了端到端加密邮件提供商的风险,并展示了我们在 Proton Mail和Skiff中发现的两个跨站点脚本漏洞的详细信息。
这篇博文通过介绍我们在 Tutanota 桌面客户端中发现的漏洞的技术细节来结束我们的三部分系列。我们展示了一段看似无辜的代码如何导致跨站点脚本问题,使攻击者能够窃取解密的电子邮件、冒充受害者,甚至在受害者使用 Tutanota 桌面客户端的情况下在受害者的计算机上执行任意代码。
我们还在Black Hat Asia 2023上以演讲的形式介绍了本博文系列的内容;视频录制将在未来几天内提供,我们将使用链接更新本出版物。
Sonar 研究团队在 Tutanota 基于 Web 的客户端的开源代码中发现了跨站脚本漏洞。由于客户端是用户输入密码后进行电子邮件解密的地方,因此它也是电子邮件以其解密形式存在的地方。因此,攻击者可以窃取解密的电子邮件并冒充受害者,绕过端到端加密。
在这种情况下,攻击者可以通过将 XSS 漏洞与我们发现的其他错误链接起来来进一步实施。这将导致在受害者的计算机上执行任意代码。
攻击者必须发送一封电子邮件,受害者必须使用 Tutanota 桌面客户端查看该电子邮件。一旦电子邮件被打开并且受害者在应用程序中的任意位置执行两次单击,攻击者控制的有效负载就会在其系统上执行。有关利用要求的更多详细信息可以在本文后面找到。
对于端到端加密和注重隐私的网络邮件程序来说,这甚至更加危险,因为用户对服务更加信任。如果攻击者可以在此类应用程序的上下文中执行任意 JavaScript,他们就有可能窃取解密的电子邮件和私钥、取消用户的匿名性并冒充受害者。
为了避免这一切,网络邮件发送者花费了大量精力来确保恶意 HTML 无法通过。大多数使用最先进的 HTML 清理程序(例如DOMPurify)来消除恶意 HTML。这是一个很好的第一步,但即使是经过清理的数据也非常脆弱,处理数据时的细微错误可能会危及整个应用程序的安全。
以下部分将解释我们在Tutanota中发现的代码漏洞,特别是在桌面客户端中。我们还将强调现代网络防御机制的重要性,它们如何让攻击者的日子变得更艰难,以及当正确的明星对齐时如何仍然可以绕过它们。最后,我们研究 Tutanota 团队如何修复这些问题以及如何避免代码中出现此类漏洞。
准备好了解有关解析器差异、Electron 安全性和阻止列表绕过的故事!
为了确保用户可以安全地阅读电子邮件,Tutanota 实施了多项保护措施。第一步是使用 HTML 清理程序(在本例中为 DOMPurify)清理电子邮件正文。然后在清理后的 HTML 中搜索文本链接,将其转换为<a>
标签:
src/mail/view/MailViewerViewModel.ts:
private async setSanitizedMailBodyFromMail(/* [...] */): /* [...] */ { const {htmlSanitizer} = await import("../../misc/HtmlSanitizer") const sanitizeResult = htmlSanitizer.sanitizeFragment(this.getMailBody(), /* [...] */) const {html, inlineImageCids, links, externalContent} = sanitizeResult // [...] const text = await locator.worker.urlify(stringifyFragment(html)) // [...]}
Tutanota 使用该linkifyjs
库来实现此目的。他们传递经过清理的 HTML 字符串并返回链接的 HTML 字符串:
src/api/worker/Urlifier.ts:
import linkifyHtml from "linkifyjs/html"export function urlify(html: string): string { return linkifyHtml(html, { attributes: { rel: "noopener noreferrer", }, target: "_blank", })}
因此,Linkify 库必须解析 HTML 字符串。通过使用以下有效负载,我们可以观察到解析器的行为与浏览器不同:
<svg><style><a alt="</style><i x><img src onerror=alert(1)>" /></style></svg>
浏览器将<svg>
使用 SVG 解析规则正确解析元素下的任何内容,从而将<style>
元素的内容解析为进一步的子元素。DOMPurify 使用浏览器的解析器,因此清理程序不会看到任何恶意内容:
然而,Linkify 看到了这一点:
正如我们所看到的,Linkify 错误地将元素的内容解析<style>
为原始内容,导致字节序列第一次出现时</style>
关闭元素。这会过早结束样式元素,从而显示之前隐藏在属性中的<i>
和标签。<img>
到目前为止,这并不是什么大问题,因为 Linkify 库仅解析 HTML,但不渲染它,因此处理程序onerror
在此阶段永远不会执行。但为了完成其工作,Linkify 必须将解析后的 HTML 序列化回字符串。这是它应用一些修改来标准化 HTML 的地方:
我们可以看到该库通过添加默认空值 ( x=""
) 或将属性值括在双引号 ( onerror="alert(1)"
) 中来规范化多个属性。最终的 HTML 字符串如下所示:
<svg><style><a alt="</style><i x=""><img src="" onerror="alert(1)">" /></style></svg>
当浏览器最终呈现此 HTML 时,它会按如下方式解析它:
我们可以看到,之前包含和标签的<a>
标签属性现在短多了。Linkify 插入的用于标准化标签属性的双引号现在结束标签,并且标签的右括号 ( ) 结束标签。这会导致标记后面的所有内容都被解析为 HTML 元素,包括标记及其处理程序。alt
<i>
<img>
<i>
x
alt
<i>
<
<a>
<i x>
<img>
onerror
浏览器(由 DOMPurify 使用)和 Linkify 之间的解析器差异可能会被攻击者滥用,将任意 HTML(包括 JavaScript)偷运到受害者的 DOM 中。
幸运的是,任何攻击者控制的 JavaScript 都不会被执行。Tutanota 有一个非常严格的内容安全策略(CSP),只允许从 Tutanota 本身加载脚本,并且不允许内联脚本。这是使用指令完成的script-src 'self'
。
在 Web 客户端中,这非常可靠,而且我们没有发现 CSP 的绕过,使得消毒剂绕过对攻击者毫无用处。但是 Tutanota 也有一套基于 Web 客户端的桌面客户端,所以让我们来看看它们!
这些桌面客户端是使用Electron构建的,Electron 是一个允许使用 Web 技术构建跨平台桌面应用程序的框架。它基本上是 Node.js 和 Chromium 浏览器的混合体,并作为单个可执行文件发布。开发人员将其与应用程序捆绑在一起,然后应用程序可以利用 Web 生态系统的优势以及通过 Node.js 的 API 直接访问系统的灵活性。
Tutanota 的桌面客户端如下所示:
启动时,桌面客户端会将捆绑的 Web 应用程序解压到临时目录,然后通过file://
在集成 Chromium 浏览器中加载 URL 来呈现它。
Tutanota 的 Web 和桌面客户端共享相同的 CSP,所以让我们来比较一下这两种情况!在 Web 上,页面是从https://mail.tutanota.com加载的,因此 CSP 只允许从此源加载 JavaScript 文件。
在桌面客户端中,页面是从类似 的 URL 加载的file:///C:/Users/Paul/AppData/Local/…
。但是'self'
CSP 值对于file://
URL 意味着什么?事实证明,它允许加载文件系统中的任何文件!这意味着如果攻击者可以控制已知路径上的文件内容,他们就可以绕过 CSP。
控制文件的一种方法是向电子邮件添加附件并希望受害者单击“保存”按钮,但攻击者可以更进一步。在 Proton Mail 和 Skiff 中,电子邮件正文被插入到将其与应用程序隔离的 iframe 中。对于 Tutanota,应用程序本身和电子邮件正文之间没有隔离,因此电子邮件中包含的任何 CSS 样式也可能适用于 UI 的其他元素。
攻击者可能会滥用此功能,使“保存附件”按钮变得透明,并将其拉伸到整个应用程序的 UI 上。这种形式的 UI 修复让受害者别无选择,如果他们想继续使用邮件客户端,只能在不知不觉中单击不可见的按钮(以红色显示):
下载附件后,攻击者就知道其文件路径,因为 Tutanota 将文件保存到包含文件名的已知位置。这允许攻击者将保存的附件作为脚本包含在内,从而绕过 CSP。
由于在呈现攻击者的电子邮件时该文件不存在,因此页面必须尝试连续包含该脚本。这可以通过包含一个 iframe 来完成,该 iframe 包含<script>
引用该文件的标记以及<meta>
每秒重新加载 iframe 的标记。一旦 Tutanota 客户端将文件保存到磁盘,脚本就会被包含并在 iframe 再次重新加载时运行。
此时,攻击者可以读取解密的电子邮件,以受害者的名义发送电子邮件,甚至可能窃取加密密钥。这在端到端加密电子邮件解决方案中已经至关重要,但由于攻击针对的是桌面客户端,我们想知道攻击者是否可以更进一步并危害整个系统。
在 Electron 中,UI 运行的“网络世界”可以与“主世界”隔离。主世界可以访问 Node.js API,这些 API 可以直接访问文件系统和其他操作系统接口。这种隔离被认为是良好的做法,因为它增加了一个额外的屏障,可以降低 XSS 漏洞的影响。图塔诺塔在这里表现出了良好的安全卫生,为此设置了正确的选项。启用了上下文隔离,禁用了节点集成,等等。
其余的攻击面是可以在 UI 和主世界之间发送的进程间通信 (IPC) 调用。需要这些,以便应用程序仍然可以在用户单击相应按钮时执行保存或打开附件等操作。
我们映射了所有可用的 IPC 调用并发现了两个有趣的调用:download
和open
。第一个,download
采用 URL 和路径,然后将文件从该 URL 下载到指定路径。第二个 IPC 调用 ,open
采用路径,并要求操作系统打开该文件。
在 Windows 上,攻击者可以轻松地使用这两个调用的组合来下载并运行恶意可执行文件。然而,有一个最终的安全机制可以防止这种情况发生。IPCopen
调用实现了一个阻止列表,试图阻止打开任何可执行文件格式。阻止列表是通过检查文件扩展名来实现的:
src/desktop/PathUtils.ts:
export function looksExecutable(file: string): boolean { if (process.platform === "win32") { const ext = path.extname(file).toLowerCase().slice(1) return [ "exe", "bat", // [...] ].includes(ext) } return false}
为了获取扩展,应用程序使用path.extname()
Node.js 中的函数。它以路径作为参数并返回扩展名。如果我们查看该函数的文档,我们可以看到以下内容:
如果有一个名为 的文件C:\Temp\.exe
,则将path.extname()
返回一个空字符串。检查Tutanota的文件扩展名阻止列表,我们可以观察到空字符串没有被阻止。Windows 很乐意将相同的文件作为可执行文件运行,从而使攻击者能够绕过阻止列表并使用 IPC 调用在受害者的系统上执行任意代码open
。
从浏览器和 Linkify 库之间的解析器差异导致的清理程序绕过开始,攻击者可以将任意 HTML 注入应用程序的 DOM 中。注入点周围没有 iframe,因此攻击者控制的 CSS 样式可能会影响应用程序的外观。
要使用 绕过 CSP script-src 'self'
,攻击者必须控制文件系统上的文件。他们通过将有效负载附加到电子邮件并使用 CSS 强制受害者单击附件的下载按钮来做到这一点。保存附件后,它将作为脚本包含在内,从而开始第二阶段。第二阶段将使用可用的 IPC 调用下载恶意可执行文件并运行它,绕过进程中的阻止列表。
由于我们发现的代码漏洞造成了严重影响,因此让我们了解如何修复该漏洞以及如何避免代码中出现类似问题。
Tutanota 团队采用了一种可以应用于所有类似情况的通用方法。他们在完成所有修改后移动了清理程序,以确保最终的 HTML 是安全的:
private async setSanitizedMailBodyFromMail(mail: Mail, blockExternalContent: boolean): Promise<SanitizeResult> { const {htmlSanitizer} = await import("../../misc/HtmlSanitizer") const urlified = await locator.worker.urlify(this.getMailBody()) const sanitizeResult = htmlSanitizer.sanitizeFragment(urlified, { /* ... */ }) // ...}
维护者还采取了额外的强化措施:
他们在电子邮件正文周围引入了 Shadow DOM,以防止包含的 CSS 样式影响整个应用程序的 UI。
他们现在处理导致可执行阻止列表绕过的边缘情况。
该应用程序是从特殊asset://
协议加载的,该协议仅提供与 Tutanota 捆绑在一起的文件。因此, CSP 指令script-src 'self'
不允许来自file://
URL 的脚本。
下载附件的文件路径现在是随机的,防止攻击者预测附件的路径。
为了避免代码中的 HTML 清理程序绕过,我们有一些建议:
如果可能,请在客户端而不是服务器上进行清理。HTML 解析器是复杂的野兽;使用两个不同的就像要求解析器差异一样。
使用最先进的消毒剂。这可以是DOMPurify,也可以是未来将内置到浏览器中的即将推出的Sanitizer API 。如果您使用晦涩或过时的消毒剂,它们可能会错过奇怪的怪癖,让您容易受到伤害。
清理数据后切勿修改数据。这并非特定于 HTML,而是特定于任何需要清理的数据。数据结构越复杂,清理后修改它就越危险。
如果可能的话,在清理 HTML 后甚至不要重新解析它。DOMPurify 可以配置为返回清理后的 DOM 树而不是字符串。如果直接将此树插入到页面的 DOM 中,浏览器将不会改变其内容,从而减少 mXSS 的机会。
Copyright © 2022 All Rights Reserved. 地址:上海市浦东新区崮山路538号808 苏ICP123456 XML地图