宝马Connected App 协议的逆向分析 - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com

宝马Connected App 协议的逆向分析

xiaohui 资讯 2020-02-01 13:01:55
收藏

导语:宝马Connected App 协议的逆向分析

360截图163005078089114.jpg

2016年,我买了一辆宝马车,第一次体验了IDrive,“iDrive”就是智能驾驶控制系统“intelligent-Drive system”的缩写,它是一种全新的、简单、安全和方便的未来驾驶概念。通过某种神奇的蓝牙协议,我手机上的Spotify(是一个正版流媒体音乐服务平台)将被添加到仪表板上的音乐源列表中。点击这个条目会显示一个丰富的用户界面,提供浏览选项来选择播放列表,并根据当前歌曲启动电台,这比基本的蓝牙音乐控件更具交互性。

1.jpg

Idrive使用起来非常简便,里面的功能模块分别为车内气候、通讯(车载电话等)、娱乐(CD/电视等)、导航、信息、宝马服务支持、功能设置和帮助菜单。其中经常使用的前4个主菜单可通过圆形旋钮向上下左右四个方向推拉控制器进入。以气候调节为例,3次简单操作就可以调节车内不同位置的温度和气流分布,比如可以设设定某个座椅的加热从腰部位置开始(当然,这也归功于宝马的舒适性座椅),而气流是以某种流量按设定的方向吹出。

2.jpg

但是,宝马在推出iDrive系统时,由于操作相对较为复杂,曾引起巨大争议。比如,有时附加的蓝牙应用程序协议无法连接,有时各个应用程序本身也无法响应。

因此,我决定弄清楚这个BMW应用程序协议,并在没有他们帮助的情况下将我自己喜欢的音乐应用程序添加到系统中。

蓝牙嗅探

Android有内置的蓝牙捕获日志!我只需要记录手机应用程序对汽车说了什么,然后看看我发现了什么:

3.png

这个SPP协议似乎有很多有趣的信息:我看到了一些X509证书、一些XML数据、一些看起来像歌曲元数据的字符串等。

BCL多路复用

我开始注意到大多数数据包的前几个字节的模式:第0个字节是0,第1个字节是1或6,第2个字节是0,第3个字节通常很低,而下一个字节几乎总是0x0FA4。我发现接下来的2个字节是剩余数据的长度,接下来的4个字节几乎总是0xDEADBEEF。

我开始编写一个Wireshark Lua插件来帮助我理解数据,将第一个字节解析为4个16位值,分别为Val1、Val2、Val3和Length,然后输出剩余的数据字节。

似乎该协议用于将连接多路复用到单个蓝牙串行套接字,而Val2是不同的连接ID。通过在Wireshark中解析这个字段,我可以使用显示过滤器来跟踪单个通信流。

Apache Etch

经过一番研究,我发现了一篇文章,解释了BMW使用Apache Etch作为“BMW应用程序使用的基本通信协议”的原因。快速访问Apache Etch文档可以确认0xDEADBEEF是每个Etch RPC调用开始时的神奇标识符。这意味着,我只需要将每个BCL流解码为Apache Etch数据包,Wireshark的内置Etch解析将接管一切!

Apache Etch符号名

当然,除了Apache Etch将每个函数名和任何其他符号名编译成32位哈希值之外。Wireshark可以使用Etch调试工件将哈希值替换为漂亮的名称,但是我首先需要弄清楚这些名称并自己对它们进行哈希处理。 由于Etch哈希算法是公开的,因此,我没有原始的Etch IDL,所以我编写了一个Rust实现来帮助手动生成此调试名称工件。

但是,从哪里得到这些名称呢?

事实证明,JVM字节码(也就是编写在Android应用程序中的字节码)非常容易反编译。虽然变量名有点模糊,但 Etch生成的类包含所有可用的 Etch符号的精确列表:

ValueFactoryBMWRemoting.java:

    private static void a() {
        a = (Type)hn.get("de.bmw.idrive.BMWRemoting.VersionInfo");
        b = (Type)hn.get("de.bmw.idrive.BMWRemoting.SecurityException");
        c = (Type)hn.get("de.bmw.idrive.BMWRemoting.ServiceException");
        d = (Type)hn.get("de.bmw.idrive.BMWRemoting.IllegalArgumentException");
        e = (Type)hn.get("de.bmw.idrive.BMWRemoting.IllegalStateException");
        f = (Type)hn.get("de.bmw.idrive.BMWRemoting.ver_getVersion");
        g = (Type)hn.get("de.bmw.idrive.BMWRemoting._result_ver_getVersion");
        h = (Type)hn.get("de.bmw.idrive.BMWRemoting.info_getSystemInfo");
        i = (Type)hn.get("de.bmw.idrive.BMWRemoting._result_info_getSystemInfo");
        j = (Type)hn.get("de.bmw.idrive.BMWRemoting.sas_crl");
        k = (Type)hn.get("de.bmw.idrive.BMWRemoting._result_sas_crl");
        l = (Type)hn.get("de.bmw.idrive.BMWRemoting.sas_certificate");
        m = (Type)hn.get("de.bmw.idrive.BMWRemoting._result_sas_certificate");
        n = (Type)hn.get("de.bmw.idrive.BMWRemoting.sas_login");
        o = (Type)hn.get("de.bmw.idrive.BMWRemoting._result_sas_login");
...

因此,我只需通过我的定制hasher运行这个名称列表,并将结果文件交给Wireshark,然后就得到一个漂亮的协议转储!

5.png

TCP连接

当我打开反编译代码时,我稍微研究了一下连接方法,我发现BMW Connected应用程序运行一个TCP localhost服务器,它通过这个BCL多路连接代理所有连接。这意味着任何“获得根授权的”的手机都可以运行tcpdump并记录每个应用程序与汽车的通信,而无需运行蓝牙嗅探。

另外,这意味着我不需要重写主要的BMW Connected应用程序,只需要打开一个连接到手机上的神奇TCP端口的TCP连接,以获得与汽车的直接连接,并扮演任何其他启用BMW的应用程序的角色。

如何找出代理运行在哪个端口? BMW Connected应用程序广播系统范围内的Android Intent,其中包含连接细节,以便在汽车连接时使用。此外,它已硬编码到特定端口,因此我可以尝试手动连接到该端口。

这完全改变了我的方法:我要做的就是介绍这种高级RPC协议!

重建Etch IDL

使用从应用程序生成的Etch RPC类,我至少可以使用所需的有趣位来重构Etch IDL文件。 Etch编译器愉快地接受了它,并给了我一些代理对象,即使与我的示例相比,它的包布局也正确。

伪造汽车链接

我最早的侵入式调查技术之一是实现Etch RPC的服务器端,然后将自己的连接通知发送到官方应用程序。 Android Intent是应用程序组件之间通过松散耦合彼此通信的标准方式,并且完全未经身份验证。这诱使官方应用模块连接到我自己控制的 Etch服务器,而不是汽车的BCL代理。

带有NotImplementedExceptions的Etch对象被剔除,所以我很容易看到哪个调用是官方应用程序发出的。为了让官方应用程序满意,我填了足够多的伪造汽车的标识,然后这个应用程序(在模拟器中运行)使用提供的VIN号下载一辆它从未与之物理连接的汽车的图像:

6.jpg

伪造汽车链接的第一个用途,是帮助我分析身份验证的安全性。

汽车认证

建立新的 Etch连接时,它所做的第一件事就是向汽车发送PKCS7证书。汽车以随机数作为响应,应用程序预计将以一些身份验证数据响应。

证书是由BMW CA签署的,所以没有可以绕过它的方法。然而,只要从应用程序apk中提取文件,这些证书就很容易获得,主要的连接应用程序包括大约10个证书。

接下来,我将如何找出正确的随机数响应?它看起来足够长,可以成为RSA4096签名,因此尝试破解就不会很有趣。

事实证明,JVM字节码非常容易反编译。在将整个应用程序反编译为混淆的Java文件后,你可以将整个程序加载到Android Studio中,并使用其强大的代码搜索和重构工具来帮助导航代码,你也许可以在com.bmwgroup.connected.internal.security.CarSecurityManager中找到线索。

实际上,代码清楚地显示了它如何使用Android Binder RPC连接到CarSecurityService对象,以及如何将挑战随机数(challenge nonce)作为挑战响应。此外,这项服务还可以应用于手机上的任何应用程序。

7.png

一些快速测试代码验证了此服务所返回的答案与Wireshark捕获的答案相同,服务实现是一个小型的JNI包装库,它非常可能包含一些OpenSSL符号。为了方便起见,我还围绕这个库编写了自己的最小服务实现。

首次连接

掌握了这些知识后,我很快就建立了一个测试应用程序,试图与汽车进行首次连接。官方应用程序首先要做的事情便是调用rhmi_getCapabilities来获取汽车支持的功能标志列表。这涉及到实现一个BroadcastReceiver来监听汽车的连接通知,充当SecurityService的客户端以准备立即接受挑战随机数,并使用适当的连接细节实际实例化 Etch代理对象。

终于,我成功建立了连接。

8.jpg

触摸屏(RHMI) 资源

这个项目的主要目标是向汽车中添加更多音乐应用程序,因此我的注意力自然吸引到了RHMI调用命名空间,并使用了诸如rhmi_setData和rhmi_onActionEvent之类的诱骗名称。首先调用应用程序的是rhmi_setResource,它用于发送XML小部件布局和一些zip文件。这些资源在任何BMW应用程序的APK中都很容易获得,因此很容易进行检查。

首先,也是最重要的,是XML小部件布局。它包含组织到窗口中的组件列表、组件中显示的模型列表和链接到小部件的操作列表。其中一些模型可以保存来自电话应用程序的任意数据,而另一些模型可以保存指向zip文件中的资源的数字ID。

压缩后的资源很简单,就是一个图形包,其中的每个文件都用数字ID命名,或者是一个翻译文本包,其中的字符串由ID输入。在应用程序中,有适用于BMW的资源包。

第一个应用程序

因此,当我首次尝试在汽车中创建一个应用程序时,我复制了初始化调用来发送远程UI资源,然后测试了rhmi_setData调用来尝试将图像加载到汽车中。

9.jpg

弄清楚动作事件系统非常容易,在调用rhmi_addActionEventHandler通知汽车发送输入事件后,汽车将开始调用rhmi_onActionEvent,其中包含触发具体动作的详细信息,并且该动作被链接回原始组件。

RHMI白名单

接着,我会尝试编辑发送给汽车的小部件布局。然而,失败了,汽车拒绝了上传。但是,上传的原始工件仍然工作得很好。

仔细查看之后,我发现在认证证书中有一个SHA256检查列表,原始资源与这些检查相匹配,这意味着我不能改变任何小部件布局或图形包。这主要是

10.png

组件属性

最初,我很失望,如果我被锁定在原始的小部件布局中,那么我将无法构建一个非常自定义的应用程序。但是,有时我在Wireshark捕获中看到对rhmi_setProperty的调用,并且小部件布局中的某些组件定义了

事实证明,Java字节码非常容易被反编译,我发现这个RhmiPropertyType枚举包含整个列表。

11.png

因此,即使我不能更改小部件配置,我也可以设置小部件属性,包括可见性、位置和大小,这仍然提供了极大的灵活性。

MediaBrowserService API

有了这些基本构建块,我便开始为汽车构建自己的音乐应用程序!除了我的手机上已经有几个很棒的音乐应用程序,我更愿意把这些应用程序加到车上,而不是编写自己的应用程序。有什么方法可以代替我控制现有的音乐应用程序吗?

事实证明,Android鼓励音乐应用程序实现一个名为MediaBrowserService的协议。通过实现这个单一的API,该应用程序可以自动通过Android Auto、Android Wear和所谓的蓝牙堆栈访问。我可以充当这个接口的另一个客户端,并在此API之上将汽车实现为前端。

12.jpg

汽车应用实现

这种策略使实施相对容易:我只需要为此MediaBrowserService构建一个客户端,每当发生任何元数据回调时,都使用适当的信息更新汽车的标签,来自汽车的按钮回调可以对音乐应用程序运行命令。

然后经过几个月的磨合,我有了一个具有完整浏览和搜索支持的音乐应用程序,它可以控制任何MediaBrowserService音乐应用程序:

13.jpg

安全分析

这与Connected Drive协议完全不同,后者可用于从世界的另一端入侵你的汽车。此Connected Apps协议仅在通过USB或蓝牙从手机到(行驶中的)汽车的本地连接上起作用。还有许多其他安全限制,以减少恶作剧的发生:

手机应用必须使用BMW签名的证书登录,但是,只需提取任何现有应用程序,即可轻松访问这些证书。实际上,官方配套应用程序(例如Spotify)为官方应用程序托管一个ContentProvider,以获取特定于应用程序的证书并代表它们登录汽车,该ContentProvider为手机上的任何应用提供了对证书的读取访问权限。

此身份验证证书具有许可白名单,它可以在所有品牌的汽车上进行认证、解锁各种功能、访问汽车的各种服务数据,甚至可以创建应用程序以及使用什么隐私数据。然而,也许是为了便于开发,官方的证书是非常开放的,只将隐私数据锁定在一些很好的通用布局中。

汽车将登录随机数交换为客户端应用进行签名,在2018年,宝马确实取消了这个交换服务,该服务允许任何应用程序对此挑战作出回应。尽管他们无法从Connected Classic应用中删除它,但大多数用户将可以安全地运行新的Connected应用。但是,攻击者可能只使用JNI库来进行挑战响应。

这些应用已沙箱化,只能访问(只读)汽车数据服务中的信息,而不能直接访问canbus。新的应用程序布局隐藏在菜单中某个应用程序的进入按钮后面,并且任何全局状态都只能通过特定的功能进行修改:设置音乐元数据、触发导航、启动电话呼叫等等。

该协议已被弃用并消失,宝马新的Live Cockpit系统不支持任何远程应用协议,而是最终在其现有的Apple Carplay支持中添加了Android Auto支持。将有一些应用程序嵌入到汽车中,但手机中的应用程序将无法以相同的方式进行集成。

总结

为了进一步减少被攻击的可能性,宝马应该利用他们的沙盒经验,像通用汽车那样启动一个开发者项目,让用户表达他们的创造力,并构建自己的应用程序,为应用商店贡献自己的力量。

本文翻译自:https://hufman.github.io/stories/bmwconnectedapps 如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论