Android藍牙協議棧漏洞剖析
1.背景介紹
藍牙協議相對于其他通信協議如WIFI(802.11)、傳統TCP/IP議協等來說,更為復雜,目前藍牙核心規范(5.3)高達3085頁。藍牙的這種復雜性使得對藍牙的各個協議的實現進行安全測試與審計變得相對困難,從而導致協議的實現和使用容易出現較多的安全漏洞。但是從另一方面來說,它的復雜性也會要求研究員或攻擊者進行漏洞挖掘的技術門檻變得相對更高,需要突破的難點也會更多。
圖1 藍牙核心規范說明書封面
圖2 藍牙協議棧架構
如圖2所示,藍牙協議棧主要分為兩個部分:Host和Controller。Host主要包含鏈路層之上的整個藍牙協議棧,包含傳輸層L2CAP以及在它之上的多種應用層協議,是藍牙協議棧的核心部分。Controller主要與硬件驅動打交道,偏底層。本文主要關注的是Host層各協議的實現安全。
從Android系統發展歷史上來看,其藍牙協議棧也一直是安全研究的重點目標。2017年Armis安全團隊公布BlueBorne組合漏洞攻擊鏈可以通過藍牙對智能手機進行遠程攻擊,危害性極大。2020年Android爆出BlueFrag漏洞,攻擊者可以通過該漏洞實現遠程零交互的遠程代碼執行。
OPPO安珀實驗室一直致力于參與維護整個Android系統生態安全,因此也對Android藍牙協議棧展開了深入的研究。至今我們已向Google Android安全團隊提報了多個藍牙協議棧實現安全漏洞,這次將依次介紹我們團隊發現的三個不同Android藍牙協議模塊漏洞:CVE-2020-27024(SMP協議)、CVE-2021-0918(GATT協議)、CVE-2021-39805(L2CAP協議)。
2.CVE-2020-27024
2.1 SMP協議簡介
SMP(Security Manage Protocol)安全管理協議定義了配對和密鑰分發的過程實現,然后使用密鑰對鏈路通信數據進行加密。SMP被用在LE設備或藍牙雙模設備中。如圖3所示,SMP協議工作流程大致分為幾個階段:
Step1:特征交換
配對特征交換,即交換各自都支持哪些配對特性,比如支不支持Secure Connection(安全連接),支不支持MITM,支不支持OOB(Out of Band:帶外),以及它的輸入輸出能力(IO capabilities)等。
Step2:密鑰產生
該階段雙方將協商生成產生STK/LTK:
LE Legacy pairing:產生Short Term Key(短期密鑰)。
LE Secure Connection:產生Long Term Key(長期密鑰)。
Step3:密鑰分發
該階段分發一些其他用途的Key,像Identity Resolving Key(IRK),這個Key是用于Random Address解析。當分發這些Key時,此階段鏈路是必須要加密的,若不加密,將導致密鑰直接泄漏。加密使用的密鑰是用第二階段產生的STK或LTK,或者雙模下直接共享使用BR/EDR配對產生的Key。
Step4:鏈路加密
通過使用STK/LTK密鑰對整個藍牙通信鏈路進行加密。
SMP協議整個配對流程如下圖所示,其中配對發起方角色為主設備類型(MASTER),接收方為角色為從設備類型(SLAVE)。
圖3 SMP協議工作流程
2.2 漏洞剖析
CVE-2020-27024是SMP協議中一個數組越界漏洞。SMP協議使用預定義的L2CAP_SMP_CID(0x06)、L2CAP_SMP_BR_CID(0x07)通道,位于L2CAP協議層之上。如圖4所示,SMP協議棧smp_l2cap_if_init()函數負責注冊SMP數據報文接收回調處理函數,分別為處理BLE藍牙SMP報文的smp_data_received()函數,以及處理BR藍牙SMP報文的smp_br_data_received()函數。
圖4 Android藍牙協議棧SMP協議的初始化代碼段
由BR藍牙通信鏈接傳入的SMP數據包將交由smp_br_data_received()函數[ 代碼位于/system/bt/stack/smp/smp_l2c.cc]處理。當SMP協議棧接收到對端發起的配對請求SMP數據包SMP_OPCODE_PAIRING_REQ(0x01)時,報文處理流程則將到達以下圖5的代碼段。
圖5 smp_br_data_received()函數代碼段
當smp_br_data_received()函數處理完成后,它調用smp_br_state_machine_event()函數 [代碼位于/system/bt/stack/smp/smp_br_main.cc ],該函數代碼實現如圖6所示。
圖6 smp_br_state_machine_event()函數代碼段
首先我們可從圖7中看到全局變量smp_br_entry_table數組的SIZE其實為2。
圖7 smp_br_entry_table數組定義
從圖6中的代碼段我們可以看到由于smp_br_state_machine_event()函數在一開始執行tSMP_BR_ENTRY_TBL entry_table = smp_br_entry_table[p_cb->role]語句時并未對p_cb->role的最大值進行合法性判斷,反而是取值后再判斷p_cb->role的合法性。當攻擊者惡意造成p_cb->role出現取值大于2的情況時,將導致smp_br_state_machine_event()函數entry_table賦值語句出現內存OOB越界讀的異常。
3.CVE-2021-0918
3.1 GATT協議簡介
GATT是 Generic Attributes的縮寫,中文是通用屬性,它是低功耗藍牙BLE設備之間進行通信的協議。GATT定義了一種多層的數據結構,已連接的低功耗藍牙設備用它來進行通信,其定義的多層數據結構簡要概括起來就是服務(service)可以包含多個特征(characteristic),每個特征包含屬性(properties)和值(value),還可以包含多個描述(descriptor)。
GATT基于ATT協議(屬性協議)來承載的,屬性協議主要用來發現、讀寫、通知和指示屬性。
圖8 GATT協議交互圖
在ATT層協議框架內,擁有一組屬性的設備稱為服務端(Server),讀寫該屬性值的設備稱為客戶端(Client),Server和Client通過ATT PDU進行交互;
其中Attribute Protocol PDU報文格式如圖9所示。其中Opcode:操作碼(消息類型,1字節大小); Attribute Parameters:ATT參數(變長); Authentication Signature:身份驗證簽名。
圖9 Attribute Protocol PDU報文格式
ATT消息各種類型多達30多種,其中常見的消息類型包含表1中的6種類型,如下表所示。后續介紹的Android藍牙協議棧GATT漏洞也就是存于Notification消息類型處理函數代碼當中的。
表1 Attribute Protocol PDU的類型表
PDU類型 | 發送方 | 描述信息 |
請求/Request | Client | 客戶端向服務器請求數據 |
回復/Response | Server | 服務器對上面請求的回復 |
命令/Command | Client | 客戶端向服務器發送命令-無回復 |
通知/Notification | Server | 服務器對特征數值向客戶端發起通知-無回復 |
指示/Indication | Server | 服務器對特征數值指示發送給客戶端 |
確認/Confirmation | Client | 客戶端對指示的應答 |
3.2 漏洞剖析
CVE-2021-0918是Android系統GATT藍牙協議棧處理Notification類型報文時存在的一個內存OOB越界讀寫安全漏洞。
藍牙協議GATT報文數據核心處理函數位于gatt_data_process()函數[system\bt\stack\gatt\gatt_main.cc]。如圖10所示,該函數通過op_code值類型判斷,決定該報文是來自client端還是server端,然后分別調用不同的函數處理接口gatt_server_handle_client_req及gatt_client_handle_server_rsp函數。
圖10 gatt_data_process()代碼段
通過分析該代碼我們發現可以通過精心構造opcode值(如GATT_HANDLE_VALUE_IND:0x1D、GATT_HANDLE_VALUE_NOTIF:0x1B、GATT_HANDLE_MULTI_VALUE_NOTIF:0x23)來控制函數執行調用到gatt_client_handle_server_rsp()函數。其中GATT_HANDLE_MULTI_VALUE_NOTIF類型是后續觸發漏洞的報文類型。
圖11 gatt_client_handle_server_rsp()代碼段
最后gatt_client_handle_server_rsp()將調用到gatt_process_notification()函數。該漏洞問題代碼是就位于system/bt/stack/gatt/gatt_cl.cc文件內的gatt_process_notification()函數,該函數是負責對接收到的gatt協議notification類型通知報文進行解析處理。
圖12 gatt_process_notification()代碼段
首先介紹下Android藍牙協議棧里常用的解析報文內容的宏定義:STREAM_TO_INT8(u8, p)從p指向的報文中讀取1個字節,保存到u8類型變量當中,p指針加1;STREAM_TO_UINT16(u16, p)每次從p指向報文中讀取2個字節,保存到u16類型變量當中,p指針加2。STREAM_TO_ARRAY(a, p, len)負責從p指向的報文內容讀取len字節長度的數據,保存到數組變量a空間中,p指針加len長度。另外tGATT_VALUE value變量類型的結構體構造如圖13所示。
圖13 tGATT_VALUE結構體
我們可以看到gatt_process_notification()函數內當op_code值為GATT_HANDLE_MULTI_VALUE_NOTIF類型時,value.len變量值是可以通過報文內長度字段來控制的,此時如果value.len長度大于實際數據長度len時且小于GATT_MAX_ATTR_LEN(固定值為600),后續STREAM_TO_ARRAY(value.value, p, value.len)這行代碼將導致一個OOB越界讀的異常內存拷貝操作。如圖14所示,后續gatt_process_notification函數在循環解析報文內的多個notification消息時,由于未對解析出的value.len的長度做最大長度(不能超過GATT_MAX_ATTR_LEN)合法性校驗,將導致STREAM_TO_ARRAY(value.value, p, value.len)出現內存OOB越界寫的風險。
圖14 gatt_process_notification()后續代碼段
4 CVE-2021-39805
4.1 L2CAP協議簡介
L2CAP(Logical Link Control and Adaptation Protocol)稱為邏輯鏈路和適配協議,是藍牙系統中的核心協議。L2CAP通過協議多分復用、分段和重組,向高層提供面向連接和無連接的數據服務。
圖15 L2CAP協議交互圖
L2CAP基于信道的概念,信道的每一個端點被稱為信道標識符(CID),同設備間CID可復用,但本地設備CID不可復用。 L2CAP是基于分組的,但也遵循信道傳輸的通信模型。對端設備上兩個L2CAP實體間傳遞的信號命令(Signaling Commands)這些信號命令通過Signaling Channel來傳輸,對于ACL-U邏輯鏈路應該使用CID 0x0001, 而對于LE-U則應該使用CID 0x0005。如L2CAP LE Signalling Channel信令通道,用于控制LE面向連接的數據信道,并可對這些LE信道的特性變化進行協商(LE-U),后續介紹的漏洞就位于L2CAP LE Signalling Channel通道信令的處理代碼中。藍牙規范定義的CID如圖16所示。
圖16 BLE信道CID
L2CAP數據包的格式如下圖所示。
圖17 L2CAP數據包的格式
L2CAP信令報文格式如下所示。
圖18 L2CAP信令報文格式
4.2 漏洞剖析
CVE-2021-39805是L2CAP協議中一個數組越界讀漏洞。問題代碼位于l2cap藍牙協議棧system\bt\stack\l2cap\l2c_ble.cc文件中的l2cble_process_sig_cmd函數。該函數主要負責解析處理LE的信令通道控制報文。
圖19 l2cble_process_sig_cmd()代碼段
當L2CAP層處理基于信用的增強型流控模式的重新配置的回復(L2CAP_CMD_CREDIT_BASED_RECONFIG_RES,0x1A)報文時,其未進行包長度判斷,導致內存信息泄露。顧名思義,這個回復就是響應重新配置的請求(request)報文,這個(request)請求是為了重新配置該通道的MTU和MPS的,其結構如圖18。圖19就是響應的回復報文。
圖20基于信用的增強型流控模式的重新配置請求
圖21基于信用的增強型流控模式的重新配置回復
Android在2020年8月左右添加了對基于信用的增強型流控模式的支持,此漏洞代碼就從那時起一直存在,如圖22所示。這段代碼段中,STREAM_TO_UINT16從回復報文去取result字段的值,但是取之前未判斷此時回復的報文是否已經沒有多余的數據了。因此,若精心構造一個該回復數據包,那么就會觸發到這個地方導致OOB Read。且這個被賦值的result字段后續會返回給發送方,導致內存信息泄露。
圖22 l2cble_process_sig_cmd()后續代碼段
5.總結與展望
Android系統中藍牙協議棧演進分為幾個階段,最早android使用是Linux藍牙協議棧BlueZ。從Android 4.2開始,Google便在Android源碼中推出了它和博通公司一起開發的BlueDroid以替代BlueZ。當前Android使用的藍牙協議棧版本名為Fluoride,而Google正在開發的下一代藍牙協議棧叫做Gabeldorsh,并使用Rust編程語言進行開發。 目前Fluoride藍牙協議棧基本上還是使用C/C++語言來實現的,由于藍牙報文的協議解析處理涉及大量的內存操作,加上藍牙協議的多樣性和復雜性,以及歷史上多家公司代碼融合等各類原因,導致安卓藍牙協議棧爆出過很多嚴重RCE漏洞。而Gabeldorsh可以利用Rust語言本身的特性減少內存型漏洞的產生。這就導致今后對藍牙協議棧的漏洞挖掘更會集中于邏輯性漏洞,如協議本身的邏輯問題、條件競爭、設計缺陷等方面,這就更加要求該領域的漏洞挖掘研究員具有更加深厚的藍牙技術知識棧,漏洞挖掘的技術門檻也會更高、需要突破的難點也會更多。
6.參考鏈接
《Bluetooth核心規范》:https://www.bluetooth.com/specifications/specs/core-specification-5-3/
《Pixel 更新公告 - 2020 年 12 月》:https://source.android.com/security/bulletin/pixel/2020-12-01?hl=zh-cn
《Android 安全公告 - 2021 年 11 月》:https://source.android.com/security/bulletin/2021-11-01
《Android 安全公告 - 2022 年 4 月》:https://source.android.com/security/bulletin/2022-04-01