Enigmail渗透测试报告

 

一、前言

本文为Cure53发布的Enigmail 渗透测试报告,本报告得到了Posteo以及Mozilla安全开源基金(MOSS)的支持,涵盖了有关Thunderbird及Enigmail渗透测试方面的内容。在本文撰写时(即2017年12月10日),我们发现的漏洞并没有全部被修复,因此本文中只介绍了Enigmail中需要修复的漏洞情况。一旦所有问题得到解决,我们将发布完整版报告。

Enigmail Project在这里郑重感谢Pasteo以及MOSS对这份报告的支持。

 

二、漏洞信息

接下来我们会介绍测试过程中我们发现的漏洞以及代码实现上的一些问题。需要注意的是,我们是按照漏洞发现的先后顺序来介绍这些漏洞,并不是根据安全等级及严重程度来排序。我们会在每个漏洞标题后面的括号内给出漏洞的严重等级。与此同时,我们使用唯一的标识符(如TBE-01-001)来表示每个漏洞,以便后续对照参考。

<a name=”2.1 TBE-01-002 Enigmail:解析问题导致凭据伪造(严重)” class=”reference-link”>2.1 TBE-01-002 Enigmail:解析问题导致凭据伪造(严重

测试过程中我们发现了Enigmail在邮件解析过程中存在的一个脆弱性。具体说来,利用这个漏洞,攻击者可以强迫Enigmail使用受攻击者控制的PGP公钥以及相应的私钥。典型的攻击场景如下所示:

1、Bob向Alice发送了一封电子邮件。这封电子邮件看上去的确出自Bob之手,但使用的是Mallory的PGP身份信息来签名及加密。

2、Mallory是一名网络攻击者,他只能修改SMTP通信数据中Bob的“Full Name”(全名)字段。利用这个漏洞,Mallory可以通过非常隐蔽的方式修改这一字段。换句话说,Alice无法发现这个字段已被修改。

3、Alice回复了Mallory的邮件。由于Mallory悄然篡改了Bob的“Full Name”字段,因而Alice的回信最终使用的并不是最初Bob的密钥,而是完全不同的PGP密钥。这个PGP密钥可以是受Mallory控制的密钥,也可以是其他任意一个PGP密钥。

如上所述,这个漏洞会导致邮件使用过程中存在完全隐蔽的中间人(Man-in-the-Middle,MitM)攻击场景。因此,我们将这个漏洞的定位为“严重(Critical)”等级,会造成严重后果。

Enigmail中,有两个正则表达式与这个漏洞息息相关。攻击者可以使用这两个表达式来伪造任意的邮件地址。下面我们会给出这个问题的具体原理,但需要注意的是,这个问题会造成更深远的影响,具体可以参考TBE-01-004中描述的内容。

Enigmail在funcs.jsm中定义了一个stripEmail函数,这个函数原来的作用是从以逗号分隔的电子邮件列表中,提取出<evil@example.com>中包含的邮件地址。在第一次过滤检查过程中,Enigmail使用一个正则表达式来确保列表中不存在两个<>紧挨在一起的情况。具体原理是确保各个尖括号组之间使用逗号进行分隔:

EnigmailFuncsRegexTwoAddr = new RegExp("<[^>,]*>[^,<]*<[^>,]*>");

然而问题在于,如果攻击者额外注入了一对<>,并且在邮件地址中包含逗号符,那么就可以欺骗这个正则表达式:

<good@example.com,><evil@example.com>

然后,Enigmail使用第二个正则表达式来匹配并提取<>中的邮件地址:

EnigmailFuncsRegexExtractPureEmail = new RegExp("(^|,)[^,]*<([^>]+)>[^,]*","g");

这将导致带有逗号的第一个邮件地址被成功匹配,并没有匹配正确的evil@example.com这个地址:

<good@example.com,><evil@example.com>

为了能够正确解析邮件地址,Enigmail完全剔除了逗号,然而实际上这会让情况变得更加糟糕:

mailAddrs.replace(/[,;]+/g, ",").replace(/^,/, "").replace(/,$/, "");

因此,我们可以认为Bob的“Full Name”字段已经从Bob Bobbington变成了Bob Bobbington <mallory@gmail.com,>

不仅Mallory可以利用这个缺陷,如果Bob想欺骗Alice的话,他自己也可以这么做。如果Bob的“Full Name”如上所述,那么当Alice想回复Bob时,Enigmail将会查找位于mallory@gmail.com名下的PGP密钥,然后使用该密钥来加密。目前这个例子还远没有达到前面我们提到的“隐蔽”程度,然而,我们还需要注意到Mallory同样可以将Bob的“Full Name”字段修改为Bob Bobbington <bob@gmail.com,>

经过上述操作,看上去攻击者已经可以完全模仿Bob的邮件地址,即bob@gmail.com。然而事实并非如此,因为用户在“gmail”中看到的字符“a”并实际上是UTF-8编码的西里尔(Cyrillic)字符“a”。因此,上述字符串并不能完全匹配原始的bob@gmail.com这个字符串,后者才是Bob真正的邮件地址。为了优化攻击效果,Mallory可以向PGP密钥服务器上传冒充Bob的一个新的身份。如果Mallory故意使用Cyrillic字符来区分不同身份,那么Enigmail就会被误导,自动使用虚假的身份信息。实际上,Enigmail会加密这些信息,从而进一步加深该漏洞利用的隐蔽程度。

如果仔细核查解析过程中使用的这些正则表达式,那么就可以修复这个漏洞。Enigmail应该执行这种验证过程,以防止攻击者恶意注入电子邮件标识符。需要注意的是,Enigmail使用电子邮件地址作为基础标识符来查找SQLite数据库,在内部处理流程中来检索对应的PGP身份信息。因此,如前文所述,解析过程中存在的这类缺陷可能导致非常致命的后果。

<a name=”2.2 TBE-01-005 Enigmail:重放加密内容导致明文信息泄露()” class=”reference-link”>2.2 TBE-01-005 Enigmail:重放加密内容导致明文信息泄露(

如果受害者之前收到过加密邮件,那么攻击者可以利用该漏洞获取加密邮件的明文内容。将加密数据嵌入邮件正文中后,如果受害者回复邮件时没有舍弃原始邮件信息,那么攻击者就可以知晓解密后的内容。Enigmail支持部分加密邮件功能,这种情况下,只有部分邮件正文会经过加密处理。这样一来,攻击者可以将经过加密的信息隐藏在长篇会话中,完成攻击过程。

重现步骤:

1、Mallory截获到Alice发给Bob的加密信息。

2、Mallory开始与Bob对话。为了成功完成攻击,Bob在回复邮件时必须保留原始会话信息。

3、在往来会话信息篇幅变得足够长时,Mallory将截获的PGP数据插入会话中,会话中其他数据处于未加密状态。

4、当Bob收到这个信息时,PGP数据就会被自动解密。

5、由于Bob很可能不会再去阅读先前的对话内容,因此他无法察觉到Mallory添加的额外信息。如果Bob按照预期那样回复这个信息,那么解密后的内容就会发给Mallory。

攻击者可以使用另一种方法来利用这个漏洞,这种方法需要社会工程学配合,同时要利用Thunderbird的转发功能。具体的攻击方法如下所示:

重现步骤:

1、Mallory拦截到Alice发往Bob的加密信息。

2、Mallory向Bob发送了一段非常长的文本信息,这段信息中包含加密过的PGP数据以及一则简短的讯息,Mallory需要通过这则讯息说服Bob将这封邮件转发给Trudy,同时不要阅读具体的数据。

3、如果Bob按照Mallory的指示转发邮件,那么Enigmail会自动解密其中包含的PGP数据,解密后的明文信息就会发送给Trudy。

需要注意的是,这个漏洞可以归结于设计理念的问题。特别是这种攻击场景还需要用户配合(比如用户毫不察觉或者比较懒惰)才能顺利完成。

当邮件中包含部分加密数据时,Thunderbird会弹出一个信息提示框。然而,用户很容易忽视掉这个信息。我们建议邮件软件在转发或者回复包含部分加密数据的邮件时,继续保持这些数据处于加密状态。另一种方法是,我们建议当用户回复或转发部分加密信息时,相关软件应该弹出引人注目的警告窗口。

<a name=”2.3 TBE-01-021 Enigmail:解析缺陷显示伪造签名(严重)” class=”reference-link”>2.3 TBE-01-021 Enigmail:解析缺陷显示伪造签名(严重

简而言之,Enigmail无法正确查找并验证邮件附件的签名。Enigmail在解析邮件过程中存在漏洞,无法将电子邮件的内容与附件内容正确区分开来。如果作为附件的邮件经过签名处理,Enigmail会验证签名是否与附件内容相匹配,不过在用户看来,好像整封邮件都被正确签名过。这样一来,攻击者可以创造一封伪造的邮件(比如来自bob@cure53.de的邮件),这封邮件将经过bob@cure53.de签名的邮件作为附件。对收件人而言,看起来好像整封邮件都经过Bob签名,然而事实上只有附件带有Bob的签名。

重现步骤:

1、保存经过受害者正确签名的一封邮件。

2、创建一封新的邮件,将之前保存的邮件作为新邮件的附件。

3、将新的邮件发给目标收件人。

4、在目标收件人看来,这封邮件带有受害者的正确签名。

举个例子,邮件正文如下所示:

Delivered-To: jonas@cure53.de
Return-Path: <mario@cure53.de>
To: Mario Heiderich <mario@cure53.de>, Jonas Magazinius <jonas@cure53.de>
From: "Dr.-Ing. Mario Heiderich" <mario@cure53.de>
Subject: This is totally signed by mario@cure53.de!
Date: Fri, 29 Sep 2017 12:35:19 +0200
Content-Type: multipart/mixed; boundary="------------AEA294334A39599F740CD34A"

This is a multi-part message in MIME format.

--------------AEA294334A39599F740CD34A
Content-Type: text/plain; charset=windows-1252
Content-Transfer-Encoding: quoted-printable

Hey!

Just writing this totally legit email and it's totally signed by me(mario@cure53.de).

/Mario

--------------AEA294334A39599F740CD34A
Content-Type: message/rfc822;
 name="poc.eml"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="poc.eml"
Subject: [REDACTED]
To: jonas@cure53.de
From: "Dr.-Ing. Mario Heiderich" <mario@cure53.de>
Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="PWpC1qlx6dsQoTPWMjFMqgqCjLq1TuoEA"

This is an OpenPGP/MIME signed message (RFC 4880 and 3156)

--PWpC1qlx6dsQoTPWMjFMqgqCjLq1TuoEA
Content-Type: multipart/mixed; boundary="MkhracRKbd653uoMlB5pR9frBfLDD2DJK"; protected-headers="v1"
From: "Dr.-Ing. Mario Heiderich" <mario@cure53.de>
To: jonas@cure53.de
Subject: [REDACTED]

--MkhracRKbd653uoMlB5pR9frBfLDD2DJK
Content-Type: text/plain; charset=utf-8
Content-Language: en-US
Content-Transfer-Encoding: quoted-printable

Hi,
[REDACTED]
Cheers,

=2Emario

--MkhracRKbd653uoMlB5pR9frBfLDD2DJK--
--PWpC1qlx6dsQoTPWMjFMqgqCjLq1TuoEA
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJZdwtDAAoJEMJshYCQ9wra/7kP/20hr3PCSO4Lm0eZ6OCpuhGj
p04h38Mx6Jxrn+i85yMA/Bk7aU48spraWNm9cVBv8sFnVLdSTs9IiNcNsEznUCM3
KMxkva+E8u3+uuOZEGlo70L/c8EFIkXT2TrW241ZMJFLzhvcAaQLKD4V+cnsJ6CS
bV9v0WYfFH3sS4ImTj1VPVGKfLgYQnxZK/OTnxVM7oHwb4ibshqGBic2L4C4afDI
K8MRc4Fek+llKPBqH/1Am72tTyyweGFyRAfJJ5BfxJTrSSJ08KPMya6NHQq4QG0A
63Sy1Ji1l5j9BoK+Y7VolwmONDnBYLnyTkN/UoPl/6C7rA8SVQzuQtG/qihXete6
6vrlwEADuS904BZv3BJuhwIw9irmqFSjMFcx4gRldZzvyII7MD7IvtSouSsbwSTZ
3swiifz5fNRUrKq4yNarLCqOKbXn+W0mSjS6Ft23wnMosadGNyT49t6f9ZPILpuB
kL2Cro1Sihsrryzg/Y5NG52Dy2BFH7VfBHjIIl++1dTU6nnfGCZ3XWdnXB5sX2BH
i+cZ2GFiu05ICgi7tdIAjL7Zwh0P1Pf4uAwZ4o5F7Ilxo1ez5LFMTPMoVa1R1E8t
bS/DwqhzTad5EXhhJknpNDt8VZJpx+XjHbD+QW4z8OTlLSVQ2UYnLZXqQsgzK8yE
hGGHg2U2a9dCF7psD2Cf
=VrRa

-----END PGP SIGNATURE-----
--PWpC1qlx6dsQoTPWMjFMqgqCjLq1TuoEA--
--------------AEA294334A39599F740CD34A--

根据前面的分析,我们建议相关软件在签名验证流程中修复邮件解析过程中的问题,需要确认整封邮件都经过签名,而不单单是邮件附件经过签名。

 

三、 其他问题

这部分内容中,我们会介绍一些值得关注的问题,虽然这些问题不会直接与漏洞利用工具关联起来,但攻击者可以借助这些漏洞进一步实现恶意目标。这些问题中,大多数为存在漏洞的代码片段,攻击者无法通过简单的方法来调用这些代码。总而言之,虽然的确存在漏洞,但攻击者不一定能够使用相应的漏洞利用技术。

<a name=”3.1 TBE-01-001 Enigmail:不安全的随机密钥生成过程()” class=”reference-link”>3.1 TBE-01-001 Enigmail:不安全的随机密钥生成过程(

Enigmail在实现pEp(pretty Easy privacy)过程中,调用了JavaScript的Math.Random()函数来生成安全令牌。然而这并不是一种非常安全的伪随机数生成方法。

受影响的文件:

/enigmail-source/package/rng.jsm

调用位置:

/enigmail-source/packagepEpAdapter.jsm:

受影响的代码:

gSecurityToken = EnigmailRNG.generateRandomString(40);
[...]
/**
* Create a string of random characters with numChars length
*/
function generateRandomString(numChars) {
 let b = "";
 let r = 0;
 for (let i = 0; i < numChars; i++) {
                 r = Math.floor(Math.random() * 58);
                b += String.fromCharCode((r < 10 ? 48 : (r < 34 ? 55 : 63)) + r);
 } return b;
}

generateRandomString()函数中使用了Math.random(),然而这种方法并不安全。相对而言,我们推荐使用更为安全的随机数生成方法,实际上rng.jsm中已经包含这个方法。通常情况下,JavaScript中最广泛使用的较为安全的伪随机数生成函数为window.crypto.getRandomValues(),如果需要生成的随机值比较重要,那么应该使用这个函数。

<a name=”3.2 TBE-01-003 Enigmail:可用于拒绝服务的正则表达式漏洞()” class=”reference-link”>3.2 TBE-01-003 Enigmail:可用于拒绝服务的正则表达式漏洞(

正则表达式经常用来解析用户输入数据或者gnupg的输出数据,使用范围太过于广泛。因此,这也让拒绝服务(Denial of Service,DoS)攻击有了可乘之机。在我们发现的这些漏洞中,软件会将任意长度的附件头部、URL协议头部以及邮件地址链接当成有效输入数据。因此,攻击者可以将非常巨大的一个字符串传递给Enigmail内部处理函数,在客户端造成拒绝服务攻击,最终导致客户端崩溃。

我们并没有发现这个问题会带来更加严重的后果,因此我们不认为这个问题会对用户安全造成任何实际层面上的影响。除了给用户正常工作流程带来干扰之外,这个漏洞无法造成更多危害。

受影响的文件:

/enigmail-source/package/decryption.jsm

受影响的代码:

if (attachmentHead.match(/-----BEGIN PGP w+ KEY BLOCK-----/)) {
// attachment appears to be a PGP key file

受影响的文件:

/enigmail-source/ui/content/enigmailMessengerOverlay.js

受影响的代码:

// Hyperlink URLs
var urls = text.match(/b(http|https|ftp):S+s/g);

受影响的文件:

/enigmail-source/ui/content/enigmailMessengerOverlay.js

受影响的代码:

// Hyperlink email addresses
var addrs = text.match(/b[A-Za-z0-9_+.-]+@[A-Za-z0-9.-]+b/g);

根据我们检测到的这些问题,Enigmail可以使用匹配固定长度输入数据的正则表达式来替换匹配任意长度输入数据的这些正则表达式。比如,不同的场景可以处理不同的输入数据长度,通常情况下预先处理1到1024个字符长度即可满足要求。对于PGP头部而言,只需要预先处理1到10个字符就足以满足要求。

(完)