BlueBorne 之 CVE-2017-0785 原理分析

CVE-2017-0785 属于 BlueBorne 漏洞集。导致该漏洞的原因是 Android BlueDroid 和 Fluoride 蓝牙协议栈实现的 SDP server 没有正确处理 continuation state。攻击者可以利用它泄露 Android 栈上的内存信息,然后绕过 ASLR。

本文将基于标签为 android-8.0.0_r1 的 AOSP Fluoride 蓝牙协议栈分析该漏洞。

背景知识

SDP 简介

SDP (Service Discovery Protocol) 是经典蓝牙中的高层协议,使用 C/S 架构。它定义了 client 如何发现 server 提供的服务:

上图中的关键点是,client 必须等待 server 响应当前的 request PDU 后,才能发送下一个 request PDU。Request PDU 会携带限制 response PDU 返回数据大小的字段,比如 MaximumAttributeByteCount

Bluetooth SDP Protocol
    PDU: Service Search Attribute Request (0x06)
    Transaction Id: 0x0000
    Parameter Length: 15
    Service Search Pattern: Public Browse Group
    Maximum Attribute Byte Count: 65535
    Attribute ID List
    Continuation State: no (00)

Response PDU 也会携带相应的字段表示自己返回数据的长度,比如 AttributeListsByteCount

Bluetooth SDP Protocol
    PDU: Service Search Attribute Response (0x07)
    Transaction Id: 0x0000
    Parameter Length: 667
    Attribute List Byte Count: 662
    Data Fragment
    Continuation State: yes (02 96)
        Continuation State Length: 2
        Continuation State Value

Continuation State 简介

上一节提到的 AttributeListsByteCount 不能比 MaximumAttributeByteCount 大。一旦 server 发现 client 请求的数据在一个 response PDU 中放不下,就会把数据分段,并使用 SDP 定义的 continuation state 完成所有分段的传输。每个数据段的大小不一定把 MaximumAttributeByteCount 用完,server 可自行决定分段大小。比如在上面的 response PDU 中分段大小为 662 字节,远小于 MaximumAttributeByteCount 定义的最大值 65535。

传输分段数据的 response PDU 必须携带 ContinuationState 字段。该字段由两部分组成。其中 InfoLength 表示 continuation information 的长度,且最大值为 0x10:

+------------+--------------------------+
| InfoLength | Continuation Information |
+------------+--------------------------+

Continuation information 则是一个很神奇的字段,因为蓝牙核心规范并没有为它定义具体的含义,只要求该字段能解决分段传输的问题即可。具体的情形是,client 收到携带分段数据的 response PDU 后,会把其中的 ContinuationState 拿出来,放到下一个 request PDU 中发回给 server。然后 server 根据收到的 ContinuationState 确定下一个应传输的数据分段(这与 cookie 有异曲同工之妙),如此往复直到所有分段都传输完毕(传输最后一个分段的 response PDU 不使能 continuation state)。

L2CAP 导致的数据分段

在大多数实际场景中,并不是上一节阐述的原因导致了数据分段。比如当 SDP server 返回的数据总和 (1343 bytes) 远小于 MaximumAttributeByteCount 设置的 65535 时,也会有数据分段:

这是因为 L2CAP (Logical Link Control and Adaptation Protocol) 的限制。L2CAP 承载了 SDP PDU 的传输,在 L2CAP 连接建立后,两端的设备会交换各自接收数据时支持的 MTU (Maximum Transmission Unit):

Device A -> Device B
Bluetooth L2CAP Protocol
    Length: 12
    CID: L2CAP Signaling Channel (0x0001)
    Command: Configure Request
        Command Code: Configure Request (0x04)
        Command Identifier: 0x04
        Command Length: 8
        Destination CID: Dynamically Allocated Channel (0x0040)
        0000 0000 0000 000. = Reserved: 0x0000
        .... .... .... ...0 = Continuation Flag: False
        Option: MTU
            Type: Maximum Transmission Unit (0x01)
            Length: 2
            MTU: 1024

Device B -> Device A
Bluetooth L2CAP Protocol
    Length: 14
    CID: L2CAP Signaling Channel (0x0001)
    Command: Configure Response
        Command Code: Configure Response (0x05)
        Command Identifier: 0x04
        Command Length: 10
        Source CID: Dynamically Allocated Channel (0x0040)
        0000 0000 0000 000. = Reserved: 0x0000
        .... .... .... ...0 = Continuation Flag: False
        Result: Success (0x0000)
        Option: MTU
            Type: Maximum Transmission Unit (0x01)
            Length: 2
            MTU: 1024

当 SDP server 发现填入 SDP response PDU 的数据长度超过远端设备 L2CAP 设置的 MTU 时,就会使能 continuation state 把数据分段传输。

Android 定义的 Continuation Information

前面说明了蓝牙核心规范并没有定义 continuation information 的具体含义是什么,于是 Android 对该字段的定义如下:

platform/system/bt/stack/sdp/sdpint.h#200

cont_offset 的具体含义有两种:

  • 当使用 SDP_SERVICE_SEARCH_REQ/RSP PDU 时,cont_offset 表示下一个分段中起始数据项相对不分段完整数据项的偏移。

    这里的数据项具体指的是 service record handle。比如 server 总共要返回 100 个 service record handle,第一次返回了 10 个,那么 cont_offset 就为 10。也可以把这个偏移理解为当前已经传输的数据项总和或是后续传输的第一个数据项在完整数据项中的索引。

  • 当使用 SDP_SERVICE_ATTR_REQ/RSPSDP_SERVICE_SEARCH_ATTR_REQ/RSP 时,cont_offset 表示下一个分段的数据相对不分段完整数据的偏移。

    这和上面一种情况类似,只不过 offset 的单位由一条数据项变为了字节。当然也可以理解为已经传输的分段数据大小总和。比如下面传输第一个分段数据的 response PDU,其中 cont_offset02 96,即 662,正好等于 AttributeListsByteCount 中的 662。

Client 只要在后续的 request PDU 中回传 cont_offset,server 就能找到对应 response PDU 应携带分段数据的起始位置,从而继续传输分段数据。这满足了蓝牙核心规范解决分段传输问题的要求。

漏洞分析

Android 实现的 SDP server 在处理 request PDU 时会进入 sdp_server_handle_client_req()

platform/system/bt/stack/sdp/sdp_server.cc#116

该函数会根据 PDU ID 判断当前 request PDU 的类型。当 request PDU 为 SDP_SERVICE_SEARCH_REQ PDU 时,进入 process_service_search() 做进一步处理:

platform/system/bt/stack/sdp/sdp_server.cc#141

处理函数会依次提取 SDP_SERVICE_SEARCH_REQ PDU 携带的三个参数:

  1. 提取 ServiceSearchPatternuid_seq

    platform/system/bt/stack/sdp/sdp_server.cc#183

  2. 提取 MaximumServiceRecordCountmax_replies

    platform/system/bt/stack/sdp/sdp_server.cc#192

  3. 提取 ContinuationStatecont_offset

    platform/system/bt/stack/sdp/sdp_server.cc#219

    若当前处理的 request PDU 使能了 continuation state,SDP server 就会提取其中的 continuation information 字段,并存入 cont_offset。紧接着 Android 还会对 cont_offset 做安全检查,比较它与先前传给 SDP client 的值是否相等,防止攻击者恶意设置 offset 导致数组越界:

    platform/system/bt/stack/sdp/sdp_server.cc#221

在得到 uid_seqmax_replies 后,SDP server 会找到所有与 uid_seq 匹配的 service record handle,并把它们存储在 rsp_handles 数组中。这些 handle 就是 client 请求的数据。max_replies 则用于限制这些 handle 的总数 num_rsp_handles漏洞点),避免返回的 handle 数量超过 MaximumServiceRecordCount 的限制:

platform/system/bt/stack/sdp/sdp_server.cc#207

对于每一个请求,num_rsp_handles 都会被重新计算一次。这种做法对不使用 continuation state 的请求是合理的,因为若 uid_seqmax_replies 不同,num_rsp_handles 也可能不同。但是当请求使用 continuation state 时,所有上下文相同的请求都应使用同样的 uid_seqmax_replies,那么 num_rsp_handles 不会改变,因此没必要再重新计算。如果重新计算,就应检查每次计算得到的 num_rsp_handles 是否相同。若不相同,则说明出现了异常流量,需要报错处理。但 AOSP 并没有做这种检查。

于是攻击者可以利用 max_repliesnum_rsp_handles 的限制,使它的值在一个 continuation state 上下文中发生变化。比如先发送普通的 SDP_SERVICE_SEARCH_REQ PDU,正常触发 continuation state 机制(为了增加触发 continuation state 的概率,可以配置一个很小的 L2CAP MTU),然后发送 MaximumServiceRecordCount 为 1 的 continuation state PDU,就会导致 num_rsp_handles 的值为 1。

num_rsp_handles 值为 1 的情况下继续跟踪代码。当 continuation state 使能时,SDP server 会使用 num_rsp_handles 减去 cont_offset(已经传输的数量),从而得到剩余需要传输的 handle 数量 rem_handles。由于 num_rsp_handles 被攻击者篡改为了 1,且 rem_handles 的类型为 uint16_t,所以 rem_handles 发生 ​underflow:

platform/system/bt/stack/sdp/sdp_server.cc#227

接下来 SDP server 会根据远端设备 L2CAP 配置的 MTU 计算当前 SDP_SERVICE_SEARCH_RSP PDU 中最多能携带的 handle 数量 cur_handles。显然下溢的 rem_handles 总是大于 cur_handles,导致 continuation state 恒使能:

platform/system/bt/stack/sdp/sdp_server.cc#236

之后 SDP server 会把位于 cont_offsetcont_offset + cur_handles 之间的 handle 写入 SDP_SERVICE_SEARCH_RSP PDU。由于持续的 continuation state,会导致 cont_offset 不断增大,所以如下循环中的 rsp_handles[xx] 必然会发生越界读,最终导致内存泄露:

platform/system/bt/stack/sdp/sdp_server.cc#269

References

  1. Service Discovery Protocol (SDP) Specification, BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 3, Part B page 1206
  2. BlueBorne Technical White Paper
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容