Linux內核藍牙協議棧漏洞(BleedingTooth)利用分析與復現
發布時間 2021-04-16漏洞概述
2020年10月,谷歌安全研究人員披露了三個Linux內核藍牙協議棧漏洞,可導致遠程代碼執行,被稱為BleedingTooth。這三個漏洞中,一個是堆溢出,編號為CVE-2020-24490;另一個是類型混淆,編號為CVE-2020-12351,最后一個是信息泄露,編號為CVE-2020-12352。近日,谷歌安全研究人員又披露了BleedingTooth中CVE-2020-12351和CVE-2020-12352組合的漏洞利用及細節,并在藍牙4.0下,實現了零點擊遠程代碼執行。
漏洞分析
CVE-2020-12351
該漏洞出現在net/bluetooth/l2cap_core.c中。l2cap_recv_frame()是解析和處理l2cap協議數據包的函數。代碼實現如下所示:
獲取通道cid和l2cap數據包長度len。代碼實現如下所示:
根據不同的通道cid,進入不同的子過程進行處理,進入l2cap_data_channel()函數。代碼實現如下所示:
首先,通過cid找到通道chan;如果沒有找到,判斷cid是否為L2CAP_CID_A2MP;如果是,調用a2mp_channel_create()創建一個新的通道chan。a2mp_channel_create()函數實現如下所示:
調用amp_mgr_create()創建mgr,在amp_mgr_create()函數中,代碼實現如下所示:
調用a2mp_chan_open()創建通道chan,該函數將初始化一部分數據,代碼實現如下所示:
如將chan->mode初始化為L2CAP_MODE_ERTM。chan->data賦值為mgr,類型為struct amp_mgr。成功創建a2mp通道返回到l2cap_data_channel()中,代碼實現如下所示:
根據chan->mode的不同,進入不同的data處理子過程,當mode為L2CAP_MODE_ERTM和L2CAP_MODE_STREAMING時,進入l2cap_data_rcv()函數中,代碼實現如下所示:
該if條件中,會調用sk_filter()函數,此時chan->data為參數。而sk_filter()函數定義如下所示:
第一個參數類型為struct sock,而chan->data類型為struct amp_mgr,發生類型混淆。
CVE-2020-12352
該漏洞是出現在a2mp協議中,漏洞代碼位于net/bluetooth/a2mp.c,多個函數使用未初始化的結構體,將數據返回到用戶層,導致信息泄露,可泄露內核棧上的內存數據。漏洞原理較為簡單,以a2mp_getinfo_req()函數為例,該函數是響應getinfo請求時調用的,代碼實現如下所示:
行304,通過req->id獲取hdev,如果不存在hdev或hdev->type不是HCI_AMP,進入if語句中,定義struct a2mp_info_rsp類型的 rsp,該結構體定義如下所示:
其只使用了rsp.id和rsp.status,其他的數據域未使用也未初始化,可以泄露16字節數據,然后調用a2mp_send()函數將響應包發送到用戶層,泄露內存數據。
CVE-2020-24490
該漏洞只能在bluetooth 5.0下觸發,在bluetooth 5.0之前,HCI進行廣播的最大數據長度為0x1F,0x20-0xFF保留。如下所示:
在bluetooth 5.0中,該length最大擴展到229字節。如下所示:
該漏洞代碼位于net/bluetooth/hci_event.c中,在處理HCI_LE_Extended_Advertising_Report事件中,未判斷廣播數據長度最大值,后續拷貝廣播Data導致溢出。調用過程如下所示:
process_adv_report()函數處理廣播數據,將廣播數據拷貝到發現的設備中,代碼實現如下所示:
調用store_pending_adv_report()函數,該函數實現廣播數據拷貝,代碼實現如下所示:
其中,discovery_state結構體定義如下所示:
last_adv_data數據大小為HCI_MAX_AD_LENGTH,共31字節,當執行memcpy時發生溢出。
利用分析與復現
控制代碼執行流程
前文分析到CVE-2020-12351類型混淆是在sk_filter()函數中發生的,sk_filter()函數調用sk_filter_trim_cap()函數,該函數代碼實現如下:
該函數第一個參數為sk,參數類型為sock結構體,這部分代碼中對sk和skb的檢查容易繞過。接下來關鍵代碼如下所示:
行113,對sk->sk_filter進行解引用,如果成功獲取filter指針,進入行115。行119,調用bpf_prog_run_save_cb()函數,參數分別為filter->prog和skb,該函數代碼實現如下所示:
然后,行676,調用__bpf_prog_run_save_cb()函數,該函數實現代碼如下:
接著,行662,調用BPF_PROG_RUN(prog,skb),該函數定義為一個宏,實現代碼如下所示:
一路調用下來,最終會調用到紅框中的代碼,簡化一下調用過程為:
sk->sk_filter->prog->bpf_func(skb, sk->sk_filter->prog->insnsi)。因此,只要控制sk->sk_filter就可以控制執行流程。
堆噴占位
函數sk_filter()的第一個參數類型為struct sock,而實際傳入的參數類型為struct amp_mgr,可以采用堆噴128大小的內存塊進行占位,偽造amp_mgr 對象。這里有個問題,sk->sk_filter在sock中的偏移為0x110,而amp_mgr結構體大小為0x70,偏移已經超出了范圍。要解決這個問題,這里可以采用如下巧妙的堆噴布局:
結構體amp_mgr在kmalloc-128類型的slub中被分配,從第三個塊開始,amp_mgr結構體偏移0x10處,可以被偽造成sk_filter,便可以滿足sk對sk_filter域的解引用,并且可控。
布局載荷
通過堆噴占位控制代碼執行流程后,接下來就是布局攻擊載荷??梢圆捎枚褔?024大小的內存塊去偽造l2cap_chan對象,因為結構體大小為792,正好落在kmalloc-1024 slub塊中,而且a2mp通道也屬于l2cap通道中,釋放a2mp通道時,l2cap通道也將被釋放,操控起來較為靈活,最終布局如下所示:
泄露l2cap_chan對象地址
通過堆噴布局和創建釋放l2cap_chan通道等一系列操作后,可能存在一個指向kmalloc-1024內存塊地址的l2cap_chan對象,可以通過CVE-2020-12352漏洞泄露一個內核棧上面的內核地址,如下圖中紅框所示:
通過該內地地址減去一個0x110偏移便可以找到一個l2cap_chan對象地址,可以通過amp_mgr結構體內存地址檢查一下是否正確,因為amp_mgr結構體偏移0x18處為l2cap_chan指針,如下圖中紅框所示:
成功泄露l2cap_chan對象地址后,然后去填充amp_mgr結構體偏移0x10處的數據域。
復現測試
我們在ubuntu 5.4.0-26-generic系統下復現測試漏洞利用,執行過程如下:
成功反彈root級shell,如下所示:
參考鏈接:
[1]https://google.github.io/security-research/pocs/linux/bleedingtooth/writeup
[2]https://github.com/google/security-research/security/advisories/GHSA-ccx2-w2r4-x649
[3]https://github.com/google/security-research/security/advisories/GHSA-7mh3-gq28-gfrq
[4]https://github.com/google/security-research/security/advisories/GHSA-h637-c88j-47wq
啟明星辰積極防御實驗室(ADLab)
ADLab成立于1999年,是中國安全行業最早成立的攻防技術研究實驗室之一,微軟MAPP計劃核心成員,“黑雀攻擊”概念首推者。截止目前,ADLab已通過CVE累計發布安全漏洞近1100個,通過 CNVD/CNNVD累計發布安全漏洞1000余個,持續保持國際網絡安全領域一流水準。實驗室研究方向涵蓋操作系統與應用系統安全研究、智能終端安全研究、物聯網智能設備安全研究、Web安全研究、工控系統安全研究、云安全研究。研究成果應用于產品核心技術研究、國家重點科技項目攻關、專業安全服務等。