Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)

ADLab發表於2020-02-14
1、漏洞背景
2020年2月,Android安全公告中披露並修復了一個嚴重漏洞,漏洞編號為CVE-2020-0022,又稱BlueFrag,可影響Android藍芽子系統。該漏洞是一個遠端程式碼執行漏洞,出現在Bluedroid藍芽協議棧的HCI層,當無線模組處於活動狀態時,攻擊者可以利用藍芽守護程式提升許可權進而在裝置上執行程式碼。該漏洞影響Android Oreo(8.0和8.1)、Pie(9),但無法在Android 10上進行利用,僅能觸發DoS攻擊。

2、協議簡介
2.1 HCI
HCI 層位於藍芽協議棧高層協議和低層協議之間,提供了對基帶控制器和鏈路管理器的命令以及訪問藍芽硬體的統一介面方法,其介面適用於BR/EDR控制器、BR/EDR/LE控制器、LE控制器、AMP控制器,與底層的結構關係如下圖:
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
主機系統上的HCI驅動程式和控制器中的HCI層之間會存在中間層, 這些中間層即是主機控制器傳輸層,這些傳輸層是透明的,只需完成傳輸資料的任務,不必清楚資料的具體格式。兩個藍芽裝置點對點HCI層的互動過程如下圖所示:
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022) 
2.1.1 HCI包格式
HCI通過包的方式來傳送資料、命令和事件的,所有在主機和主機控制器之間的通訊都以包的形式進行。包括每個命令的返回引數都通過特定的事件包來傳輸。HCI有資料、命令和事件三種型別的包。命令包COMMAND(0x01)只能從主機發往主機控制器,其中資料包是雙向的,分為兩類:ACL(0x02)、SCO(0x03),而事件包EVENT(0x04)始終是主機控制器發向主機的。主機發出的大多數命令包都會觸發主機控制器產生相應的事件包作為響應,在傳輸過程中會有一個控制程式碼,用於識別主機之間的邏輯通道和控制器,共有三種型別的控制程式碼:連線控制程式碼、邏輯鏈路控制程式碼和物理鏈路控制程式碼。
根據需要,這裡只介紹ACL資料包格式,ACL 資料用於主機和控制器之間的非同步資料交換,如播放音樂資料的資料包,格式如下圖:
  Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)

每個欄位的說明如下所示:

Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)

   其中,PB Flag的描述如下:
 Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
    設定為 00'b 的時候,代表 Host -> Contoller 的 L2CAP 的首包。設定為 01’b 的時候,代表 Host -> Contoller 或者 Contoller -> Host 的 L2CAP 的續包(中間的)。設定為 10'b 的時候,代表 Contoller -> Host 的 L2CAP 的首包。
2.1.2 分段(Fragmentation)和重組(Reassembly )
分段是將PDU分解成較小的部分,以便從L2CAP傳遞到較低層。重組是根據從下層傳遞來的片段重組PDU的過程。分段和重組可以應用於任何L2CAP PDU。
 Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
2.2 L2CAP資料包格式
L2CAP是基於分組的,但也遵循通道傳輸的通訊模型。L2CAP支援的通道有兩種:面向連線的通道和麵向無連線的通道。在面向連線的通道中,L2CAP資料包的格式如下圖所示。
 Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
資料包中每個欄位的說明如下所示:
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)

3、漏洞原理分析
CVE-2020-0022漏洞位於HCI層,漏洞補丁程式碼位於hci/src/packet_fragmenter.cc(以8.1.0_r33為例)中的reassemble_and_dispatch()函式中,該函式是用於資料包分片的重組。對於過長的ACL資料包需要進行包的重組,主要是根據ACL包中的PB Flag標誌位進行重組,如果當前是起始部分並且是不完整的,則生成一個部分包(partial_packet)放到map裡,等下次收到它的後續部分進行拼裝,拼裝完畢後就分發出去。詳細分析reassemble_and_dispatch()函式如下:
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
    首先,處理第一個packet,程式碼127行到129行,分別讀取handle、acl_length和l2cap_length。handle為本次鏈路的Connection_Handle。根據前文資料包格式的介紹,acl_length為Data Total Length,該data資料域中存放著L2CAP資料包分片(也可能是一個完整的L2CAP資料包)。然後,直接讀取data中L2CAP Length,該l2cap_length是一個完整的L2CAP資料包中payload的長度。行131,校驗packet包長度是否正常。行133,通過handle獲取boundary_flag,即是PB Flag。
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
行136,判斷boundary_flag是否為2,二進位制表示為10’b,即判斷當前packet是否為 Contoller -> Host 的 L2CAP 的首包,如果是,進入if語句。行137到行147,判斷當前packet是否已經被處理,保證本次處理的packet都是最新的。行149到行154,判斷L2CAP資料包長度是否正常,不正常直接報錯返回。
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
接下來,行156到行157,計算full_length,其中包括一個完整的L2CAP資料包中的payload的長度,一個L2CAP頭部長度和一個HCI頭部長度。行161到行168,判斷full_length是否超過BT_DEFAULT_BUFFER_SIZE,如果超過直接報錯返回。行170到行178,判斷當前頭包packet是否還有續包,如果沒有續包直接呼叫callbacks->reassembled處理當前packet並返回。
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
如果當前頭包packet後面還有續包,那就開始重新分配一塊新的記憶體用於packet中資料包重組。行180到184,分配並設定partial_packet,將partial_packet->len設定為full_length,將partial_packet->offset設定為packet->len即當前頭包packet->data的長度。行186,呼叫memcpy,將頭包packet中HCI資料包整體拷貝到partial_packet中。行189到行191,先找到HCI資料包頭部,並跳過handle,更新acl_length為一個完整的L2CAP資料包長度。行193,將partial_packet存放到容器中。行196,釋放當前頭包packet,表示已經處理完第一個packet,不再需要它了。行197,else語句開始處理後續packet,即boundary_flag不等於2的packet。
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
行198到行205,首先通過handle判斷當前後續packet是否屬於本次鏈路的,如果不屬於,直接返回。行206,獲取前一輪生成的partial_packet。行208,將當前後續packet->offset賦值為HCI_ACL_PREAMBLE_SIZE即4位元組,此時packet->offset指向HCI包中的data域,裡面存放著L2CAP資料包分片。行209和行210,計算projected_offset,projected_offset為partial_packet->offset與本次L2CAP資料包分片的長度之和。
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
行211和行219,判斷projected_offset是否大於partial_packet->len,即判斷projected_offset是否大於full_length。如果大於,則修改packet->len為partial_packet->len減去partial_packet->offset,即packet->len為partial_packet剩餘空間的長度。然後,將projected_offset設定為partial_packet->len。具體資料包重組如下圖所示:
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
修正好實際要拷貝的長度後,行221,呼叫memcpy進行拷貝,漏洞點到了,第一個引數為partial_packet->data + partial_packet->offset,目的地址是正確的,第二個引數為packet->data + packet->offset,源地址也是正確的,第三個引數是要拷貝的長度len為packet->len - packet->offset,這個值是有問題的,分兩種情況。第一種情況是projected_offset小於partial_packet->len,packet->len - packet->offset為L2CAP資料包片段總長度,並且是個正數。第二種是行211的情況,packet->len已經被修正過,不需要再一次packet->len - packet->offset的操作,如果partial_packet剩餘空間長度小於4位元組,那packet->len - packet->offset 是小於零的,是一個負數。由於memcpy()函式第三個引數型別是一個無符號整型型別,因此整數溢位導致堆溢位。漏洞補丁如下:
Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)
可以看到,補丁程式碼中將packet->len加上了一個packet->offset,用於後面抵消減packet->offset的操作。

4、影響版本
Android Oreo(8.0和8.1)
Android Pie(9)
Android 10

5、安全建議
儘快更新最新的Android安全補丁。
僅在絕對必要時啟用藍芽。
保持藍芽裝置不可發現。

6、參考資訊
(1)https://insinuator.net/2020/02/critical-bluetooth-vulnerability-in-android-cve-2020-0022/
(2)https://akhozo.blogspot.com/2020/02/critical-android-bluetooth-flaw-cve.html?spref=tw
(3)https://android.googlesource.com/platform/system/bt/+/3cb7149d8fed2d7d77ceaa95bf845224c4db3baf%5E%21/#F0
(4)https://source.android.com/security/bulletin/2020-02-01.html
(5)http://androidxref.com/8.1.0_r33/xref/system/bt/hci/src/packet_fragmenter.cc
(6)Bluetooth_Core_v4.2藍芽官方文件


Android藍芽子系統"BlueFrag"漏洞分析(CVE-2020-0022)

相關文章