Google Zero团队:Android流行通讯APP中的BUG

原文地址:https://googleprojectzero.blogspot.com/2019/03/android-messaging-few-bugs-short-of.html

前言

大概一年半以前, 我对 Android的通讯和邮件客户端进行了一些测试。当时, 在这些客户端中发现了一些bug, 然而没能把这些bug组合成有效的攻击链, 所以没有将成果发到博客上。不过, 和当初想公布研究成果的想法不同, 我现在决定将研究成果分享出来。毕竟, 了解(客户端的)设计上的抉择会对安全造成什么影响, 也是一件有趣的事情。

实例

在研究过程中, 对Messages (Android默认的短信客户端)、 Android上的Gmail客户端、 微软 Outlook, Facebook Messenger, WhatsApp、 Signal和Telegram这些是Google Play上最流行的通讯和邮件客户端进行了测试。

通讯客户端说明(体系结构、攻击面、测试方法)

从高层次上看, 通讯客户端具有类似的体系结构, 其功能是与服务器进行通讯, 负责定位要与用户通讯的收件人, 通常还要保留通讯过程中的session。消息需要通过服务器, 再由服务器直接发给收件人, 支持端到端加密的客户端可以将消息用某种信封加密技术密封起来。

不同客户端使用的通讯协议是不一样的, 尽管如此, Facebook Messenger、WhatsApp 和 Telegram 还是有相似的地方的, 这些客户端通讯协议都是采用分层方法实现的, 使用该方法发送的消息可以在一个层次结构包含类型不同的字段。 Telegram使用的通讯协议是公开的, 用它发送出的一条消息就同时包含了发送给服务器端和接收者的信息。

一开始,我从Facebook messenger处理未加密信息的方式入手, 没想到, 这个攻击思路颇具挑战性。发送消息的协议(如mobile和 WEB API)有挺多可以用的, 再加上在目标设备接收到消息之前消息还会经过大量处理, 这两点大大提升了找到客户端中可攻击点的难度。因此, 我决定将重点放在加密后的消息上, 因为服务器不能过滤这些邮件。

我没有 Facebook Messenger和 WhatsApp 的源码, 所以我只能用apktool从这些应用的APK文件中提取出smali代码来进行分析。Signal协议的普及简化了找这些应用程序的加密部分的步骤, 因为可以直接在 libsignal 中搜索字符串。之后, 我添加了一个发送消息的函数, 将消息发送到我的服务器上, 该函数还允许在加密之前对消息进行更改的选项, 这样我就可以用格式错误的加密消息来进行测试。

比起上面两个客户端, 测试邮件和SMS客户端会显得稍微容易些。在测试SMS客户端时, 我调用了sendDataMessage API发送原始的SMS 消息。而在测试邮件客户端时, 可以直接使用 SMTP 服务器发送 MIME 邮件。

研究结果提要

有一点需要注意一下, 这些结果已超过一年, 并且通讯app的客户端的代码已有较大改动, 因此现在测试结果可能会有所不同。本文中提及漏洞均已修复。

Messages

Android默认的Messages客户端主要是用 Java写的, 使用 AOSP SMS和MMS来解析消息。因此, Messages几乎不存在内存损坏漏洞。不过Android 会以不同的方式处理一些特殊的 SMS 消息, 解析这些特殊SMS消息有可能会产生漏洞。某些特殊的消息会被被解析为符合可视化语音信箱的OMTP规范的信息, 而处理这些消息的过程以及可视化语音信箱的IMAP功能是 Java 实现的。在过去,Android曾经支持SUPL协议(用于 GPS 星历更新的 SMS 协议)在用户空间中解析, 并且进行解析工作的intent对象是存储在 SMS客户端堆栈中。但是, 现在高通的基带就可以直接处理这些消息, 因此处理过程不会在新的设备上的用户空间中进行。Messages客户端中存在的漏洞的主要是由处理媒体文件的库引发的, 过去大多漏洞都是在这些组件中发现的, 然而与之前相比,最近爆出的这样的漏洞少多了,再加上media是由低特权进程处理的。所以,目前我没有在Messages中发现任何漏洞。

Gmail

Gmail 应用程序可处理由IMAP、POP 或其他服务器发出的 MIME 消息, 这些消息的内容通常直接由非Gmail用户发出, 不过Gmail的邮件服务器还是能过滤掉一些邮件的。Gmail的客户端会调用AOSP 中的实现邮件协议底层API, 这些API和Gmail的客户端都是用Java实现的。在Gmail的客户端中, 我发现了一个由附件上传引发的目录遍历的bug

Outlook

微软 Outlook 客户端的功能和Gmail的差不太多, 同样会处理另一台服务器发送的 MIME消息, 但其所有协议的处理过程都是由客户端中的代码完成的。Outlook主要是用 Java写的, 其中的native的功能和邮件本身没多大关系。和在Gmail中一样,Outlook也存在着相似目录遍历漏洞

Facebook Messenger

与我测试的其他通讯客户端相比, Facebook messenger 的工程量挺大的了, 很多代码很难明确其功能。该应用中包含大量的native库, 几乎每天都要从 Facebook 服务器重新加载数据动态更新。不过, 这些库好像很少直接参与消息的处理。

Facebook Messenger使用一种叫 “消息队列遥测传输 (MQTT)” 的协议与服务器进行通信——MQTT 的底层是开源的Thrift协议。应用中实现该协议的有Java 和 c++ 两种的库, 但好像只用了 Java 的库。我检查了协议相关代码, 没有发现Facebook Messenger的漏洞。

WhatsApp

WhatsApp 使用的分层协议主要是用 Java 实现的,该应用处理语音和视频呼叫请求的消息代码是在native代码中实现的, 我在我写的一篇文章中讨论了相关内容。目前,我没有在 WhatsApp发现任何漏洞。

Telegram

Telegram是开源的, 其 API代码是公开的。其中有相当多的native代码, 但该部分代码主要用于设备到服务器通信, 而不是端对端通信。因此,远程攻击者可用的攻击面几乎都在 Java 中。在Telegram中, 我发现了一个bug, 同样是一个与附件相关的目录遍历漏洞。

Signal

Signal主要由 Java实现, 攻击面相当小。虽然, 它使用的通讯协议有很好的文档记录。但是, 我没有在其中的任何漏洞。

漏洞影响

总的来说, 我在这些客户端中一共发现了三个目录遍历漏洞。这些漏洞惊人地相似, 都与附件名有关,是由附件在下载时允许附件名包含特殊字符造成的。目录遍历问题在 Android 应用中非常常见,造成该漏洞的部分原因应该是 Java API 在文件名方面处理得不够严谨。

不过, 这些漏洞具有局限性。首先, Gmail 和 Outlook 邮件客户端中的bug仅在客户端与非标准电子邮件地址一起使用时才起作用 (即 Gmail 与非 Gmail 帐户一起使用, 或者 Outlook 与非 outlook 电子邮件地址一起使用)。虽然,Gmail 和 Outlook 邮件客户端都经常与非标准电子邮件地址一起使用, 但这些漏洞不会影响每个用户。并且想要利用这些漏洞, 还需要用户进行下载附件的操作。

该漏洞的利用还有一些限制条件。比如, 想要利用这些漏洞不能覆盖存在的文件, 只能往里面写入新文件。因此, 攻击者需要在某个地方放置系统可以处理但不存在的文件。但Android 上的应用程序权限有限, 通常不能在其应用程序目录之外写入。

上面失败之后,又有了一个绝妙的idea进入到我脑子里, 通过利用SQLite 中漏洞实现攻击, 因为Gmail 和 Outlook 都使用 SQLite 数据库来存储数据。SQLite 支持日志记录, 这意味着当 SQLite 数据库发生更改时, 会先写到日志文件里面, 防止在写入过程中系统崩溃或断电时数据库的数据丢失。如果发生系统崩溃或断电的情况, 在下次访问数据库时,就会存在一个保存着更改信息日志文件, 这时SQLite就知道要还原已更改信息。一旦信息还原完成, 日志文件就会被删除。这意味着, 可以通过在同一目录下写入具有特定名称的日志文件来更改SQLite数据库, 也满足此文件在一般情况下不存在的条件。

我尝试过这个方法, 并且更改了数据库, 但只能改变用户在应用中看到的邮件, 不能进行提权或执行非法操作。为了寻找更通用的方法, 我想到了可以从SQLite 解析日志文件或数据库文件的方式下手。

Telegram的漏洞有可能直接利用, 因为恰好有一个默认情况下不存在的配置文件, 如果该配置文件存在的话, Telegram可对处理该配置文件, 并且在处理代码中会引发一个内存损坏漏洞。不过, 我找不到一个可实现该攻击方法的邮件客户端。

上面失败之后,又有了一个绝妙的idea进入到我脑子里, 通过利用SQLite 中漏洞实现攻击, 因为Gmail 和 Outlook 都使用 SQLite 数据库来存储数据。SQLite 支持日志记录, 这意味着当 SQLite 数据库发生更改时, 会先写到日志文件里面, 防止在写入过程中系统崩溃或断电时数据库的数据丢失。如果发生系统崩溃或断电的情况, 在下次访问数据库时,就会存在一个保存着更改信息日志文件, 这时SQLite就知道要还原已更改信息。一旦信息还原完成, 日志文件就会被删除。这意味着, 可以通过在同一目录下写入具有特定名称的日志文件来更改SQLite数据库, 也满足此文件在一般情况下不存在的条件。

我尝试过这个方法, 并且更改了数据库, 但只能改变用户在应用中看到的邮件, 不能进行提权或执行非法操作。为了寻找更通用的方法, 我想到了可以从SQLite 解析日志文件或数据库文件的方式下手。

我用 AFL 对数据库的加载和日志处理进行了Fuzzing测试。我发现四个漏洞, 其中三个我认为是不可利用的, 剩下一个利用的可能性非常小。

我找不到更多的方法来利用目录遍历, 因为不能覆盖 Android 应用程序中的文件, 所以我不认为这些目录遍历漏洞对攻击者来说会特别有价值。

总结

我测试了几个 Android 通讯和邮件客户端,发现了三个目录遍历的漏洞, 但这些漏洞的利用存在一些限制, 现在还不清楚在一般情况下这些漏洞能否被利用。

这些漏洞非常相似, 箭头直指Android中 Java未能正确处理文件路径方面的问题。仅在客户端与非标准电子邮件地址一起使用时, 邮件客户端中的两个漏洞才能被成功利用,尽管这种应用场景非常常见。漏洞的产生可能是邮件客户端的功能测试不足或审查不足造成的。大部分通讯客户端是用 Java 实现的, 这就增加了在应用程序中找到bug的难度, 因为不能利用内存损坏漏洞进行攻击。

这项研究的着重点是未经过中间件处理的邮件和消息, 攻击者可以利用这个bug向接收者发送任意数据, 客户端不会对小心进行过滤且会对数据直接进行处理。虽然这些漏洞利用起来很容易, 但它们只是消息或邮件客户端攻击面中的一部分。在将来,我很可能会投入到通讯客户端的中介服务器的功能的研究中去。