2022 SDC 议题回顾 | 漫谈AOSP蓝牙漏洞挖掘技术
2022-11-17 18:6:25 Author: 看雪学苑(查看原文) 阅读量:13 收藏

车联网的话我之前做过一些T-box以及车机的一些安全研究,疫情前在国外的几个会议做过一些技术的演讲。成果的话,主要是这几年挖到一些安卓的CVE漏洞,现在在谷歌的Bug Hunters排行榜目前是全球第八,然后Hackone的高通目前是2022年的第一名。然后这是我的一个ID(ele7enxxh),大家微博,微信或者GitHub上都可以找到我。今天我的重点会在攻击面上的一个介绍,主要是因为从我的角度做漏洞挖掘的话,最重要的一点是攻击面的梳理,而后面的一些不论你是做模糊测试,还是做代码审计,可能只是让你挖到漏洞的时间,从一周变成三天或者变成一天。

能不能挖到漏洞主要的还是取决于你对攻击面的掌握。

01

背景

首先简单讲一下背景,因为在座的有可能不太了解安卓蓝牙这块东西,安卓是在最早的2.2版本就引入了蓝牙,最开始使用的不是它自己的一个代码,它是引用了 Linux系统上默认的蓝牙协议栈BlueZ,并把它移植到了安卓上。

然后是到4.2的版本,大概是两年左右的时间,谷歌就引入了一个叫Bluedroid的蓝牙默认协议栈,比较有意思一点的是,当然这个是我的一个猜测,底层的所有跟协议栈相关的一些代码的开发是由博通公司来做的。之所以说是博通和谷歌共同开发,是因为它上面的一个框架层的代码,也就是Java层的代码和一些服务的代码,这块代码应该是谷歌来写的。而且可以看到它在整个AOSP的仓库的代码位置,它是放在了external目录下面的。

这里有两个图,左边这个图是6.0最早期的一个架构版本,右边是一个9.0左右的新版本,这都是在谷歌官方网站上拿下来的。这两者之间从蓝牙模块的角度来讲的话,它的变化不是特别的大,主要是在协议栈本身和上层的结构上做了调整。最开始是一个HAL层,下面是它的一些驱动模块,后面换成了一个中间语言HIDL层来做这个事情。

这样做其实本身跟蓝牙模块关系不是特别大,他这样做是从整体上对安卓的架构进行调整,主要是为了更快的开发、迭代、开发。

OK,现在到最新的一个安卓版本,应该是前一两个月才发布出来的,安卓13的一个代码,安卓13他又换了一个名字。

跟前面几个版本最大的一个区别是他又换了一个目录,之前是 /system/bt的目录,现在将它转移到了/packages/modules/Bluetooth/system目录,我这边的理解是,最开始他应该是认为蓝牙协议栈库,它本身是一个基础的协议库,所以它放在了system下面。

到了现在他可能会认为这个蓝牙协议的库,它本身只是一个私有的库,应该只被我的蓝牙应用来使用,所以它转移了这个目录的结构。然后应该是在11,已经开始重写了一个AVRCP协议。

简单说一下AVRCP协议,它其实就是我们蓝牙耳机上用的比较多的一个蓝牙协议,是一个可以用来控制音量大小、音乐切换的一个协议,它是一个重写的协议,重写了一些比较底层的东西,比如BLE扫描,BLE广播等。

然后对安全研究者比较重要的一点是,它在部分模块开始使用了Rust的语言。从安全角度来讲,rust的语言和C/C++语言有一个最大的区别,它是一个内存安全的语言。

不过好消息是它没有对所有的代码重写,从官方博客来看,它会在新的模块,或者新的特性里面使用Rust的语言来编写。

但就目前而言,对我们整个蓝牙漏洞挖掘而言,它的影响还不是特别的大。

再讲一下几个比较知名的蓝牙历史漏洞,这里重点讲一下17年的BlueBorne漏洞,当时在安全研究者的圈子里面应该造成了一个非常大的影响,也正是因为这样的一个漏洞,很多本身不是做蓝牙安全研究的人员也开始来做蓝牙安全,因为他发现好像不去看几百页几千页的蓝牙文档,也可以做这块的安全研究。

我也是从那个时候看到这个漏洞以后,才开始对整个的安卓蓝牙它的模块做漏洞挖掘的工作。这是我自己对历年蓝牙模块的漏洞做的一些分析,我的数据来源是安卓官方的每月的致谢榜。但是有一个比较小的问题是,安卓从某个版本开始,它没有把中危的漏洞放在每个月来进行修复的,它只有在大的版本迭代的时候才会把中危漏洞给合入进去。

所以我这里的统计数据只有高危和严重,但就算我们只看这两个,我们也可以看到从17年到现在的时间节点,谷歌至少披露了148个漏洞,其中高危99,严重有49个。然后再看一下整个的一个趋势,也是和我刚刚讲的是非常相关的一个特性。在18年的时候,所有的白帽子,在18年挖到的蓝牙漏洞,目前是一半以上的占比。

这也是因为刚才我讲的,因为BlueBorne的关系大家都去看这块东西,而且由于价格的变化,由于这个代码是由博通来写的,博通写的代码质量实在是有点差,所以在很多的蓝牙数据包进来以后,在做数据包的解析的工作的时候,没有做任何的一个代码长度、数据包长度的校验,就我个人来说我一天能够挖十几二十个这样的漏洞。它有一些数据,每个协议栈进来,他的数据的读写都没有做任何的一个边界的校验,以及对数据本身的一个合法值的校验。

这个是我自己把漏洞的类型做了一个分类,我主要分为三个,一个是内存破坏,内存破坏就是我们在C/C++上面的一个内存破坏漏洞,就是一些堆上的越界读写或者一些off by one这样的一些东西。

另外一个是权限校验不当,权限校验不当这一块主要还是发生在Java层面上会多一点,因为我们知道的是安卓它会包装一些SDK的API接口暴露给我们的上层的开发者调用,如果说对这些接口本身的权限校验没有做的话,就会导致一些越权,比如说越权来访问你的蓝牙列表,或者说越权收发蓝牙文件。

还有一块是业务逻辑,业务逻辑这块怎么理解?其实就是说一方面是底层的一些,它会涉及到一些密码相关的一些安全的协议,如果这一块它的密码算法使用上逻辑上有问题也可能会导致它整个密码安全上的一些失效,以及还有可能是我们上层的一些绕过用户允许,就是说用户可能会需要点同意才能进行操作,通过一些业务逻辑的问题可以绕过去。

然后看这个图表,18年的话,大部分的内存安全漏洞都在63个,这个也是因为当时蓝牙漏洞是一片蓝海的状态,内存安全漏洞非常多,所有人都一窝蜂的去搞这个东西,所以那个时候搞了很多的内存安全的问题。然后到后面我们会发现内存破坏漏洞,好像被大家挖的差不多了,我入场比较晚,被那些前辈都给挖干净了怎么办?

我们来再往上面找找权限校验的问题,我们看一下Java层,我们走一些不同的路径,我们又发现一些Java层的漏洞,但是有一个问题是Java层的代码量是比较偏少的,所以很快的,权限校验漏洞我们又挖干净了。谷歌写代码的速度实在太慢了,跟不上我们挖的速度,怎么办呢?我们又回到内存破坏漏洞,我们再来看一下有没有我们没有关注到的点,比如说一些比较偏门的蓝牙协议,虽然说我们没怎么用,但是AOSP它自己实现了,也有潜在的一些风险点。

所以我认为我们来做漏洞挖掘的时候,你做了这么多年以后,特别是对同一个模块做这么多年,你会发现它其中有一些比较有意思的点,随着整个热度热情,它是随着代码本身的一个健壮性和白帽子的关注点而变化的。

其实简单来就是,柿子还得挑软的来捏。然后第二部分分享是关于为什么我们要做蓝牙漏洞?因为我们知道大部分蓝牙它是具有一个天然的远程的攻击面的,我们在一个近场通信的协议,比如说现在在会场,我应该能够搜索到大家所有的蓝牙的信号的,因为它有远程的一个情况,所以它的定级基本上是会比较高一点的。

简单来说如果你有一个远程的堆栈的越界写的情况,谷歌大部分情况下对于这种情况直接会定为严重漏洞,就算你只是写了一个字,基本上也是严重。如果说你是一个越界读,但是会泄露一点数据出来的话,那也是一个高危漏洞,所以大概率蓝牙漏洞都是一个高危及严重以上的情况。

然后是在模块上的分布,主要是这4个模块,这4个协议。AVRCP刚刚有讲,主要是蓝牙耳机相关的协议,这4个协议有个特点是什么?他们的代码量比较大,他们的协议本身会比较复杂,在整个的蓝牙模块里面,它所处的代码层级也会非常多,从最顶层Java到最下面的c++,它都有一个协议的相关的实现代码,后面会讲一些比较实际的案例。

02

攻击面

这一部分是我今天要讲的一个重点,我会根据我自己的理解来把这个攻击面给大家一一展开。

我们来看一下大的一个框架图,这个图是我根据整个的安全者的一个视角,来把我们整个的蓝牙模块它的架构给画一画。我们首先看一下两个蓝色的线,蓝色的线它是把它分成了几个大的模块,上面这一层它是 Host层,你可以理解为是我们能够接触到的手机上的这些软件。

然后下面这层是 Controler层,通俗地讲就是个蓝牙芯片、硬件芯片,因为我们的无线电信号过来以后,首先是被我们的底层的硬件芯片接收到的,硬件芯片经过他自己的处理,再传给我们的host的人来做处理。

然后那右边这层就是我们的远端的一个东西。黄色的框的话,这里划分了几个实例,就是我们要研究的一些对象,最上层是我们的APP,我们的一个普通应用,我们的任何的一个开发者可以在应用市场上架的一些应用。

然后这一层是一个system server,搞过安卓的应该都知道,它是一个在安卓系统上最重要的系统服务应用,很多的服务都会注册在 system server进程里面运行。

下面这个就是安卓它自己的一个默认蓝牙应用,这是一个系统应用,由它来提供所有的跟蓝牙相关的一些能力都在这里面实现,然后最右边是remote远端。

现在我们把所有小的内容都填到这个框里面,我再大家给大家讲一下,上面这一层APP里面比较简单,就是一个普通的应用,然后system service里面跟蓝牙相关的就是一个bluetooth manager service,这个bluetooth manager service,包括很多和下层打交道以及与上层打交道的一些东西都在这个里面实现,它相当于是一个框架层的服务。

下面我们来看一下整个的蓝牙系统应用里面有些什么东西,这三个是我自己从攻击者的视角做了一个分类,首先它有一个 Java的service,就是Java代码写的一些服务,这里面会包含很多各种各样的配置文件,这里的配置文件不是大家理解的那种配置文件,那个英文单词翻译过来叫配置文件,可以理解为它就是各种协议,或者是各种蓝牙的不同场景下的应用。

简单举几个,比如说我们A2DP,A2DP是跟我们耳机相关的一些东西。这个LeAudio是安卓13上新增的一块跟我们的音频相关的高保真音频那种东西。

然后还有一些的话其实跟耳机的比较多,还有AvrcpTarget、 A2dpSink这种都是。这种具体的每个的含义,就是这些每一个小模块都可能会存在漏洞的一些东西,大家有兴趣的可以自己搜一下这些协议具体是干啥的。

然后是我们的libbluetooth JNI这样的一个库,顾名思义这个就是一个JNI的库,动态库里面也包含了一些配置文件,可以看到它跟上面的 JavaScript里面可能有些重复的,但是这是因为有些服务他可能只需要Java代码,但有些服务它还需要跟底层的c++里面做一些交互,所以有些会在JNI库也有实现。

然后最重要的一个libbluetooth。libbluetooth的话,它是我们一个核心的库,这个库里面实现了绝大多数的协议,从我们的芯片上报来以后,都会走到这里面来做一些数据的处理。在18年挖到的六十几个漏洞,起码有90%都是在这里面的。

我们简单的讲一下,这里面我们划分了两个块,一个是这个数字的,它是一个层级的东西,层级的东西就是说它是一个安卓本身来源的架构上它自己人为划分的一个层级。

BTIF层它是和上层就是和我们的JNI层直接进行打交道的一个接口层,然后它的层次相当于从这个地方写的,从上往下它是越来越底层的协议,一个层级,然后右边这一块的话,就是我们在支持的各类各样的协议,这里重点讲下L2CAP, 做一个简单的类比,它相当于我们网络模型里面的TCP对应的地位。就是它是一个基座,基于L2CAP基座再向上又实现了各类的一个协议,然后其他两个框里面就比较简单, Controler层就是一个蓝牙芯片。

然后右侧端就是一个我们的无线电的信号。

接下来我会从攻击场景来把我们的整个的数据画到我们刚刚那个图上,看一下这些数据的一些走向。一个是近场的恶意蓝牙设备,这个意思就是说我的耳机可能是恶意的,我可以发些包来攻击你。我们从右下角开始看,无线电信号进来,进来到我们蓝牙芯片,拿到以后他自己会做一些解析和封装,然后上报,通过我们的内核的 Driver,传给我们的HCI层,HCI层再精心解包,又可能拿到ACL的一个数据,ACL的数据再进行一次解包,拿到一个L2CAP,L2CAP再根据具体的子协议走到不同的地方。

整体的逻辑就是从下往上,然后到BTIF层,不是所有协议都可能有往上的步骤,它有些协议可能会需要往上走交互,所以会走到btf层来调上层的一个callback函数,callback函数上来以后,它有可能会调用我们的JNI的一个调用。再往上走,就一直往上,可能就会直接绘制了我们的APP层也是不一定的。

所以从远端过来的数据,它的数据生命周期可能会是最为复杂的,它在每一个层级都可能出问题,这也是我们做漏洞挖掘需要注意一点,就关注整个蓝牙数据包它在蓝牙模块里面的一个完整的生命周期,每一个地方都是有可能会出现问题的。

来到第二个场景,第一个是一个大家可能都会猜得到的一个场景,但是蓝牙实际上不只是远程的一个攻击的情况,也就是说如果说我本地开发了一个蓝牙相关的应用,那么这个应用有没有可能会影响到我们的蓝牙模块?

我标红的框就是我们的APP,可以理解为一个恶意的蓝牙应用,他能干什么事呢?首先它和system service里面会进行交互,通过安卓系统里面的远程通信机制,我们叫Binder IPC就通过远程通信机制来调用到我们System Server里面的Bluetooth Manager Service的相关的一些能力,然后Bluetooth Manager又会通过我们安卓上的Binder Service,来调用到我们蓝牙系统应用里面的所有的这些Java Service,然后再依次往下走JNI层、Interface,这样我们可以从上往下又梳理了一个比较全面的生命的周期。

然后第三个其实是一个我最开始的几年也没有关注的一个点。为什么?因为大家可能觉得我的芯片默认它是安全的,对不对?

就我们要信任芯片上报过来的数据,但实际上我们来看刚刚的一些架构图,我们可以知道的是整个芯片和我们上面的Host层,它从架构上来看它是分开的,就是说如果说我们认为我们应该是host层,它不能够完全信任芯片报过来的一些数据,如果说我的芯片被攻击了怎么办?如果说我的芯片它被植入了后门怎么办?芯片的数据就会影响到Host层上面的一些东西。这个其实是后面几年才有报这些漏洞,还是那句话,因为柿子挑软的捏,所以大家最开始可能先搞上面的,上面都被搞干净了,大家再搞一些难一点的。

我们来看一下这一块数据的走向,这一块就会比较简单一点。它的数据走向,就芯片可能会上报一些事件或者数据过来,数据的话就可能是蓝牙的广播数据,就是我们蓝牙可能无时无刻都在发送的一些信号这些东西,还有事件的话就可能是信号的强度,或者说是我们的其他的一些东西,反正就是这些数据会上报上来,到我们的 HCI,HCI层就会进行一些处理,这个时候也有可能就发生一些安全上的问题,就比较多的,可能是一些内存破坏的情况。我们讲了三个类工具,我们来看一下把它画在一张图是怎么样的,大概就是这样的一个结构。我在每个框里面写了一点CVE漏洞,因为这样可能会有助于大家理解一下到底哪些CVE漏洞是属于这个层级的。

03

挖掘方法

接下来是一些比较工程化的东西。我们知道了整个的一个攻击面,也知道了它的一个数据生命周期了以后,生命周期流转对整个架构有一定的了解了后,我们怎么做漏洞挖掘,我会简单介绍一下我的方法。

当然比较朴实一点的就是一些源码,第一个是源码审计,为什么?我认为在iOS平台上我们要着重做一个源码审计,很简单,第一个它所有代码都是开源的,有开源的代码你干嘛不看代码,你非要去用IDA看没必要。然后为什么不做?为什么优先级fuzz不是很高?

做一些远端uzz,就是说我发个畸形数据包过来,又发个畸形数据包过来,其实我认为这效率是非常低的,而且你也很难做一些代码覆盖率的反馈,所以我不是很推荐做一些蓝牙远程的fuzz,而且这个东西可能还需要一些硬件的处理。

我用的一个审计的网站,是谷歌它自己的一个代码开源的网站,这个网站还是挺好用的,所有的交叉引用都可以很方便的找到。但是内饰可能比较费眼费脑费头发。

然后我们来看一下具体怎么做,我举一个简单的例子,跟着我们刚才的一个攻击面的数据,我们来看一下到底怎么从攻击面到漏洞的这样一个情况。

简单介绍一下HID,这个其实就是一个人体输入配置文件,他是可以用来做蓝牙键盘和蓝牙鼠标,也就是说你可以把你的安卓手机变成一个蓝牙键盘来控制其他的一些东西。

然后是 HID的话,它又分为两个Host和Device。简单来讲Device的话就是说我是控制别人的,我是蓝牙键盘;Host的话,我是被其他蓝牙键盘控制的。安卓目前只启用了这个。启用是什么意思?就是说它在代码层面上都是实现了这些能力的,只是说配置文件默认没有开启,但谷歌其实也是认为HID Host的漏洞的,因为他的逻辑代码是实现了,只是配置文件没开启,但是可能在其他一些供应商的设备,他可能会用这个功能。

简单讲一下做源码审计一个比较大的缺点,你可能会需要了解整个协议它的一些开发角度的工作流程。这个也是我在网上搜了一下HID到底要怎么做,怎么才能把自己的安卓设备变成一个蓝牙键盘。

他们提到一个很重要的是需要做一个应用的注册,就是告诉那个蓝牙模块,我现在要变成一个蓝牙键盘,怎么做的,首先这是一个非常简单的时序图,三个一个实体,我们的应用,然后刚刚讲的 service,然后以及我们的系统蓝牙系统应用建立连接,然后会通过Binder service拿到远程的HIDD Device Service的一个Binder对象,然后Bluetooth会封装一下,再还给一个代理对象。然后我们的APP拿到代理对象以后,调代理对象提供的一些接口方法来完成整个注册,而注册其实通过Binder传过来以后,在我们的蓝牙系统应用,它是这样一个4个函数的流程。

我们具体看一下代码,我们本地应用就通过刚刚那个Binder APC,就拿到代理对象以后就会调register_app接口,而且它的所有的参数都是通过我们的应用来传过来传过去的。我们优先来看的是第一个参数,第一个参数就是我们漏洞所在的地方。

我们看第一个参数的定义,第一个参数定义它有5个成员变量,其中有4个都是不定长的,有三个是Java的string类型,然后有一个是byte数组的类型,所以这三个都是一个不正常的数据。所以这个时候如果说你有比较丰富的漏洞挖掘经验,可能这时就会比较敏感起来了,如果说我这个数据过长或者过短会怎么样,我们来借大家看一下。

然后刚刚那个方法接着会调用到我们的register的APP内部方法,这个方法看名字都知道它应该是我们的JNI的方法。

就这样我们整个的数据就传递到了Native层,然后是Native层的话,主要他又会通过BTF调到另外一个方法里面,然后同样的这里他会接下来再往下调,就来到了我们的漏洞的一个地方。

我们来看我红框标的三个方面copy函数,因为我们是传过来的数据,我刚才有说的都是个不正常的数据,而且在上层是没有做任何的校验的,它拷贝的时候是一个固定大小的拷贝,所以如果我们的数据小于它的话,长度就会越界读。

刚刚还有一个,下面这个memcpy,我们刚刚那个字节数组它这个地方看着好像没什么问题,它是先麦克的一个空间,然后再拷贝过去的,但是我们接下来再往下走,它会又到了BTA层的这样的一个函数。

到了一个BTA层的函数的时候,它又会做一个拷贝,这次做拷贝的时候,它会直接把这个函数拷贝到d_data成员变量里面。而这个data的声明,我们在上面这个结构体里面也是一个固定长度的数据,所以这个地方也没有做数据比较大小的一个校验,所以也会有一个越界写的情况。

基本上整个流程下来,我们就挖到了两个漏洞。

好,我们来看一下第二part模糊测试这块,为什么还在做模糊测试,因为有时候真的眼睛会很累的时候,你可以提取出部分的代码来做一个类似于单元测试的事情,对部分函数来做一个测试。

然后第一步是寻找哪些是可以做一个测试的?刚刚讲过L2CAP它是一个基础的协议栈,通过它会注册很多子协议出来,所以我们就找到这三个函数,是用来注册上面那些纸协议的。

我们重点关注的是这个结构体里面的成员,回调函数就是来处理所有远端发过来的蓝牙数据包都会到这个地方来。寻找入口很简单,就是cs.android.com上面,它直接可以拿到这个对成员变量进行复制的所有的情况,这里可以看到各个协议栈的一些复制的情况。

我们这里直接讲SDP的话,它本身是一个SDP协议数据处理的一个入口函数,就是说所有的SDP的包过来以后,都会到我 sdp_data_ind这个函数里面做处理。然后它又分为两种,是我发起的请求还是对方发起的请求,来分为一个服务端响应数据和客户端请求数据,也就是我们这里讲的这两个函数。

这两个函数它的一个函数证明,我们可以看到第一个是一个结构,这个结构体它代表的是当前SDP连接的一个控制块。

然后第二个的结构体,它就是我们比较关注的fuzz的一个生成数据需要写进去的一个地方,它的一个结构体这个声明是这样的,其中比较重要的是认知段和对它转正就是我们那个数据长度,然后 data就是我们的可控数据,所以我们在fuzz的时候主要是来控制这两个数据。

然后也就是说这样一个fuzz的话,简单来讲的话会调用这一些过程,它整体的一个逻辑是这样的。

虽然我们是在做一些本地fuzz肯定是比远程fuzz快的,而且更方便一点,但是我们在做本地fuzz的同时,我们要假装,让代码认为我们是在做一个远程的fuzz,就是我们要把它的所有初始化状态和所有应该赋的值之类的东西都给他准备好,让他以为我们确实是收到了一个远端的数据包。然后我们对服务端响应的数据包做了一个fuzz,如果后面需要整个的fuzz代码可以问我要。

04

展望

展望的话其实就是说我们还能不能够挖到漏洞的这个问题,这个蓝牙模块已经被我搞6年了,接下来还能不能挖到?

我觉得还是可以的,主要是你要关注一些新的东西,比如说按照13上的新的 LeAudio,这可能的,然后还有一些新的架构HR服务,就是说AOSP没有问题,那高通有没有问题呢?MTK有没有问题对不对?

然后是一些架构设计的问题,我们刚刚讲的都是一些协议上的数据的问题,如果它本身蓝牙的模块的架构就有一些安全风险点,我们是不是可以做?挖掘方法的话,我以后可能会有其他的议题会讲一下怎么用CodeQL来做这些事情。CodeQL也是一个比较好用的工具,欢迎大家用一下。

谢谢大家,我的议题到此结束。


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458483960&idx=1&sn=abe163faf26c49ca4e2592eb553ff3f5&chksm=b18e4c7286f9c5649246d3a6db7ee6058fd40c3c07b121f984288e40b47dfb51f5cb779b71fd#rd
如有侵权请联系:admin#unsafe.sh