【51CTO.com快译】导言:Signal应用可谓是最可信的消息传递应用程序,但它并不完美的。
Signal应用,一款由美国国家安全局(NSA)泄密者--爱德华·斯诺登和大量的安全专家推荐的移动消息传送应用,近期修复了一个允许攻击者将随机数添加到由Android用户发送的加密消息附件里的错误(bug)。该更新已在Github(面向开源及私有软件项目的托管平台)的项目子集里可获取到,但在谷歌的Android应用市场—Google Play里尚无。
旁路消息验证(message authentication-bypass)的脆弱性是由研究人员Jean-Philippe Aumasson和Markus Vervier在一次非正式的检查Android版本的Signal所使用的Java代码时发现出的多个弱点中的一个。该bug将使得那些破坏或假冒Signal服务器的攻击者有可能通过添加随机数据来修改“合法”的附件。第二个bug则可能是允许攻击者远程执行恶意代码;而Vervier先生告诉本刊记者,第三个bug是一个简单的远程导致系统崩溃手段的有限利用。
“虽然结果并非不是灾难性,但这表明,和其他的软件一样,Signal应用也并非完美。”Aumasson先生在一封邮件中这么写道,“Signal应用吸引了许多安全研究人员的注意。在此之前,其‘无脆弱性’曾给大家留下了深刻印象。但该发现是有益于Signal应用的,我们仍将继续信任它。”
附件破坏的脆弱性来自整数类型数据溢出的bug,即:当有非常大的文件集(至少4 GB大小)被附加到消息里时就会被触发。Signal应用将会检查一小部分,而并非验证整体文件真实性这一特点,使得攻击者可以添加伪随机数据而不会被MAC(消息验证码)所检测到,尽管MAC已是大多数加密方案里的一个标准部分。为了使得此攻击更具操作性,攻击者可以使用Signal应用所支持的文件压缩来将恶意附件的大小减少到可控的4MB以用于运输。
在邮件中,Aumasson声称数据溢出的bug可在如下代码行中被发现:
- int remainingData = (int) file.length() - mac.getMacLength();
他的解释是:此处“file.length()”的值是一个64位编码的数值(“长整型”),而接收变量--“remainingData”却是一个32位 (“int,整型”)。因此,当“file.length()”比适合32位的数值还要长的时候,“remainingData”(剩下用于处理的字节数)的值将不正确,因为它将比实际文件的大小要小得多。因此,当Signal应用验证加密的真实性的时候,文件的很大部分将被忽略掉了。Signal应用只会检查文件的开始一小部分,而用户却实际上将接收的是更大的文件。
Signal应用吸引人的原因之一就是它部署的是端到端加密,也就意味着它在发送方的设备上加密一条消息,直到安全地存储到了接收设备上才进行解密。当然,加密的消息要经过一个服务器。那么黑客就可以冒充该服务器,绕过消息身份验证,进而篡改信息附件。为了绕过传输层的安全保护,攻击者可能需要黑掉Android操作系统所信任的数以百计的权威证书签发机构中的某一个或诱骗其目标在设备上安装一个假的CA证书。下面是更细节化的漏洞分析:
为了防止被第三方(也包括Signal的维护人员)所阅读或改变,Signal应用的附件是被加密验证的。相对于“消息验证再加密”(如TLS)和“加密并消息验证”(如SSH)的做法,Signal则使用的是“加密再消息验证”,这一最为安全的方法。
如果消息和附件被发送,其附件被单独下载到AWS服务器上,如https://whispersystems-textsecure-attachments.s3.amazonaws.com/。附件被发送方用PKCS7的AES-128-CBC进行加密,以及HMAC-SHA-256进行认证,而且使用的是128位密钥。
通过HTTPS方式下载的附件被保存到Android存储空间。Signal的服务对文件的MAC使用如下代码进行检查。其文件路径是:
- :libsignal-service-java/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java:
- private void verifyMac(File file, Mac mac) throws FileNotFoundException, InvalidMacException {
- try {
- FileInputStream fin = new FileInputStream(file);
- int remainingData = (int) file.length() - mac.getMacLength();
- byte[] buffer = new byte[4096];
- while (remainingData > 0) {
- int read = fin.read(buffer, 0, Math.min(buffer.length, remainingData));
- mac.update(buffer, 0, read);
- remainingData -= read;
- }
- byte[] ourMac = mac.doFinal();
- byte[] theirMac = new byte[mac.getMacLength()];
- Util.readFully(fin, theirMac);
- if (!Arrays.equals(ourMac, theirMac)) {
- throw new InvalidMacException("MAC doesn't match!");
- }
- } catch (IOException e1) {
- throw new InvalidMacException(e1);
- }
- }
如上所述remainingData的类型是int(整形),由文件的长度减去MAC的长度得出。因为file.length()将返回一个长整型值而文件可能大于Integer.MAX_VALUE,所以remainingData将被略过了。
不像C(+ +)语言,Java是“记忆安全”的,也就不会导致任何经典的内存崩溃状态。然而,我们却可以使用此溢出来破坏程序的逻辑。现在,如果文件大小是4BG + 1byte+ X,其价值将被略过,remainingData也将被设置为X。
不巧的是:Signal应用将所有附件存储在AWS S3上,以HTTPS的方式获取它们,并使用系统证书集来检查服务器的证书(注意:在S3服务器上的Signal用的是通配符:*.s3.amazonaws.com)。因此具有访问Amazon S3权限或具有其他Android系统所信任的CA证书的实体,可以用下列步骤修改附件:
1.等待取附件的请求。
2.取出原始附件的大小X。
3.用4GB + 1byte的数据来填充附件,以得到X + 4GB + 1的总共大小。
如上所述,这将导致X字节通过verifyMAC()的检查,而原始的MAC已然合法。因此,我们可以将任意数据添加到文件,而MAC的检查是不会报错的!
值得注意的是:攻击者并不需要在任何网络连接中真实发送超过4GB的数据,如果我们使用gzip进行HTTP的流压缩,我们就能创建一个4GB的文件而实际压缩下来却只有4.5MB。如下所示:
- [s@polo tools-markus]$ python2 sap.py --encoding gzip
- Serving HTTP on 0.0.0.0 port 8000 ...
- opening: https://whispersystems-textsecure-attachments.s3.amazonaws.com/attachments/id1/id2...
- * Compressing content...
- ** Padding content...
- ** Finished
- Compressed Content Length (Raw 49284): 4458483
- * Set Content-Length to: 4458483
- * Sent header, writing content
- * Request finished
通过查看Android的调试日志,我们现在看到如下异常代码:
- W/AttachmentDownloadJob(10484): ws.com.google.android.mms.MmsException: java.io.IOException: javax.crypto.BadPaddingException: EVP_CipherFinal_ex
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.database.AttachmentDatabase.setAttachmentData(AttachmentDatabase.java:427)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.database.AttachmentDatabase.setAttachmentData(AttachmentDatabase.java:412)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.database.AttachmentDatabase.insertAttachmentsForPlaceholder(AttachmentDatabase.java:255)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.jobs.AttachmentDownloadJob.retrieveAttachment(AttachmentDownloadJob.java:120)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.jobs.AttachmentDownloadJob.onRun(AttachmentDownloadJob.java:84)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.jobs.MasterSecretJob.onRun(MasterSecretJob.java:18)
- W/AttachmentDownloadJob(10484): at org.whispersystems.jobqueue.JobConsumer.runJob(JobConsumer.java:76)
- W/AttachmentDownloadJob(10484): at org.whispersystems.jobqueue.JobConsumer.run(JobConsumer.java:46)
- W/AttachmentDownloadJob(10484): Caused by: java.io.IOException: javax.crypto.BadPaddingException: EVP_CipherFinal_ex
- W/AttachmentDownloadJob(10484): at org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream.readFinal(AttachmentCipherInputStream.java:129)
- W/AttachmentDownloadJob(10484): at org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream.read(AttachmentCipherInputStream.java:100)
- W/AttachmentDownloadJob(10484): at org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream.read(AttachmentCipherInputStream.java:94)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.util.Util.copy(Util.java:220)
- W/AttachmentDownloadJob(10484): at org.thoughtcrime.securesms.database.AttachmentDatabase.setAttachmentData(AttachmentDatabase.java:425)
- W/AttachmentDownloadJob(10484): ... 7 more
- W/AttachmentDownloadJob(10484): Caused by: javax.crypto.BadPaddingException: EVP_CipherFinal_ex
- W/AttachmentDownloadJob(10484): at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method)
- W/AttachmentDownloadJob(10484): at com.android.org.conscrypt.OpenSSLCipher.doFinalInternal(OpenSSLCipher.java:430)
- W/AttachmentDownloadJob(10484): at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:490)
- W/AttachmentDownloadJob(10484): at javax.crypto.Cipher.doFinal(Cipher.java:1314)
- W/AttachmentDownloadJob(10484): at org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream.readFinal(AttachmentCipherInputStream.java:124)
- W/AttachmentDownloadJob(10484): ... 11 more
检查MAC后,类构造函数--AttachmentCipherInputStream将创建一个javax.crypto.Cipher类的实例:
- public AttachmentCipherInputStream(File file, byte[] combinedKeyMaterial)
- throws IOException, InvalidMessageException
- {
- ...
- verifyMac(file, mac);
- byte[] iv = new byte[BLOCK_SIZE];
- readFully(iv);
- this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(parts[0], "AES"), new IvParameterSpec(iv));
- this.done = false;
- this.totalRead = 0;
- this.totalDataSize = file.length() - cipher.getBlockSize() - mac.getMacLength();
- } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
- throw new AssertionError(e);
- } catch (InvalidMacException e) {
- throw new InvalidMessageException(e);
- }
达到这种状态时,我们便可以对自己所选取的密码进行解密了。
研究人员已将此漏洞在9月13日“私信”了Signal应用的开发商Open Whisper Systems公司,该司也已发布了相应的更新。Kudelski安全公司的首席研究员Aumasson先生和X41公司的首席执行官兼安全研究主任Vervier先生分别声称他们仍在研究这次bug是否也影响到了依赖于Signal代码的WhatsApp和Facebook消息传递应用。
在邮件中,Open Whisper Systems公司的创办人Moxie Marlinspike写到:这是一个重大的错误报告,但我们认为当前其影响程度仍较低。它并不允许攻破了服务器的黑客去读取或修改附件,而只能添加一个最低为4GB的不可预测的随机数据到附件尾部用于传输。在有效地以不可预知的方式破坏文件并使之太大,从而无法在Android设备上打开的同时,入侵了服务器的黑客就很容易通过拒绝你的附件要求的方式使服务器不再提供服务。
参考原文:
http://arstechnica.com/security/2016/09/signal-fixes-bug-that-let-attackers-tamper-with-encrypted-messages/
https://pwnaccelerator.github.io/2016/signal-part1.html
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】