在TESLA MODEL S上實現MARVELL無線協議棧漏洞的利用
在過去的兩年裡,騰訊科恩實驗室對特斯拉汽車的安全性進行了深入的研究並在Black Hat 2017與Black Hat 2018安全會議上兩次公開分享了我們的研究成果。我們的研究成果覆蓋了車載系統的多個元件。我們展示瞭如何攻入到特斯拉汽車的CID、IC、閘道器以及自動駕駛模組。這一過程利用了核心、瀏覽器、MCU韌體、UDS協議及OTA更新過程中的多個漏洞。值得注意的是,最近我們在自動駕駛模組上做了一些有趣的工作。我們分析了自動雨刷和車道識別功能的具體實現細節並且在真實的世界中對其中的缺陷進行了攻擊嘗試。
為了更深入的瞭解特斯拉車載系統的安全性,我們研究了無線功能模組(Model S上的Parrot模組)並在其中找到了兩個漏洞。一個存在於無線晶片韌體當中,另一個存在於無線晶片驅動當中。透過組合這兩個漏洞,攻擊者可以在Parrot模組的Linux系統當中執行任意命令。
介紹
本文會揭示這兩個漏洞的細節並介紹漏洞的利用過程來證明這兩個漏洞是可以被攻擊者用來透過無線協議遠端攻入到特斯拉車載系統當中的。
Parrot 模組
Tesla Model S上的Parrot模組是一個第三方模組,型號是FC6050W,它整合了無線及藍芽功能。Parrot透過USB協議與CID相連。Parrot執行著Linux系統並使用了USB Ethernet gadget,因此Parrot與CID在USB協議基礎之上實現了乙太網連線。當Tesla Model S連線到無線網路時,實際上Parrot模組連線到該無線網路中。這時,網路流量被Parrot從CID路由到外部網路。
從一份公開的資料[1]中,我們找到了Parrot模組的硬體組成。
Parrot模組的引腳定義也在這份datasheet中。Linux系統的shell可以透過Debug UART引腳得到。
其中的reset引腳連到到CID的GPIO上,因此CID有能力透過下列命令重置整個Parrot模組
Marvell 無線晶片
Marvell 88W8688是一款低成本、低功耗、高度整合的支援IEEE802.11a/g/bMAC/基帶/射頻集無線和藍芽於一體的基帶/射頻系統級晶片[2]。
Marvell官方網站[3]提供了一份該晶片的設計框圖。
Marvell 88W8688包含了一個嵌入式高效能Marvell Ferocean ARM9處理器。透過修改韌體,我們獲得了Main ID暫存器中的數值0x11101556,據此推斷88W8688使用的處理器型號可能是Feroceon 88FR101 rev 1。在Parrot模組上,Marvell 88w8688晶片透過SDIO介面與主機系統相連。
Marvell 88W8688的記憶體區域如下:
晶片韌體
韌體的下載過程包含兩個階段。首先是輔助韌體“sd8688_helper.bin”的下載,然後是主韌體“sd8688.bin”的下載。輔助韌體負責下載主韌體及驗證主韌體中每個資料塊是否正確。主韌體中包含了很多的資料塊,每個塊的結構定義如下。
88w8688韌體的執行基於ThreadX實時作業系統,該實時作業系統多用於嵌入式裝置。ThreadX的程式碼存在於ROM記憶體區域,因此韌體“sd8688.bin”實際上作為ThreadX的應用執行。
在特斯拉上,韌體“sd8688.bin”的版本ID是“sd8688-B1, RF868X, FP44, 13.44.1.p49”,本文的所有研究均基於此版本。
在逆向識別出所有的ThreadX API之後,各個任務的資訊便可以得到。
同時,記憶體池的相關資訊也可以得到。
日誌及除錯
晶片韌體沒有實現Data Abort、Prefetch Abort、Undefine和SWI等CPU異常向量的處理過程。這意味著,韌體崩潰後處理器會停止工作。我們不知道韌體在哪裡因何崩潰。
所以我們修改了韌體,並自己實現了這些異常處理過程。這些處理過程會記錄韌體崩潰時的一些暫存器資訊,包括通用暫存器,系統模式及中斷模式下的狀態暫存器和連結暫存器。透過這種方式,我們可以知道崩潰時系統模式或中斷模式下的一些暫存器資訊。
我們將這些暫存器資訊寫到末使用的記憶體區域,例如0x52100~0x5FFFF。這樣,這些資訊在晶片重置後仍然可以被讀取。
在實現了undefine異常處理過程及修改一些指令為undefine指令後,我們可以在韌體執行時獲取或設定暫存器的內容。用這種方式,我們可以除錯韌體。
將新的韌體下載到晶片中執行,可在核心驅動中傳送命令HostCmd_CMD_SOFT_RESET到晶片。隨後晶片會重置,新的韌體會下載。
韌體中的漏洞
88w8688晶片支援802.11e WMM (Wi-Fi Multimedia)協議。在這個協議中,STA會透過Action幀來傳送ADDTS request給其他裝置。請求中包含有TSPEC資訊。然後其他裝置同樣透過Action幀返回ADDTS response。下面是該Action幀的具體格式。
ADDTS的整個過程如下:當系統想要傳送ADDTS請求時,核心驅動會傳送HostCmd_CMD_WMM_ADDTS_REQ命令給晶片,然後晶片將ADDTS請求透過無線協議傳送出去。當晶片收到ADDTS response後,將該回覆資訊去掉Action幀頭部複製到HostCmd_CMD_WMM_ADDTS_REQ結構體,作為ADDTS_REQ命令的結果在HostCmd_DS_COMMAND結構體中返回給核心驅動。核心驅動來實際處理ADDTS response。
漏洞存在於將ADDTS response複製到HostCmd_CMD_WMM_ADDTS_REQ結構體的過程中。函式wlan_handle_WMM_ADDTS_response在複製時,需要複製的長度為Action幀的長度減去4位元組Action幀頭部。如果Action幀只有頭部且長度為3。那麼複製時的長度會變為0xffffffff。這樣,記憶體將會被完全破壞,導致穩定的崩潰。
驅動中的漏洞
在晶片與驅動之間,有三種資料包型別透過SDIO介面傳遞,MV_TYPE_DATA、 MV_TYPE_CMD和 MV_TYPE_EVENT。其定義可在原始碼中找到。
命令處理的過程大致如下。驅動接收到使用者態程式如ck5050、wpa_supplicant發來的指令,在函式wlan_prepare_cmd()中初始化HostCmd_DS_COMMAND結構體,該函式的最後一個引數pdata_buf指向與命令有關的結構,函式wlan_process_cmdresp()負責處理晶片返回的結果並將相關資訊複製到pdata_buf指向的結構中。
漏洞存在於函式wlan_process_cmdresp()處理HostCmd_CMD_GET_MEM的過程中。函式wlan_process_cmdresp()沒有檢查HostCmd_DS_COMMAND結構體中的成員size的大小是否合法。因此在把HostCmd_DS_COMMAND結構中的資料複製到其他位置時發生了記憶體溢位。
晶片內程式碼執行
很顯然,韌體中的漏洞是一個堆溢位。為了利用這個漏洞實現晶片內程式碼執行,我們需要知道memcpy()函式是怎樣破壞記憶體的,以及晶片是怎樣崩潰的,在哪裡崩潰的。
為了觸發這個漏洞,action幀頭部的長度應該小於4。同時我們需要在Action幀中提供正確的dialog token,這意味著memcpy()接收的長度只能是0xffffffff。源地址是固定的,因為該記憶體塊是從記憶體池pool_start_id_rmlmebuf分配的,並且這個記憶體池只有一個記憶體塊。目的地址是從記憶體池pool_start_id_tx分配的,所以目的地址可能是四個地址中的某一個。
源地址及目的地址均位於RAM記憶體區域0xC0000000~0xC003FFFF,但是記憶體地址0xC0000000到0xCFFFFFFF都是合法的。結果就是,讀或寫下面這些記憶體區域會得到完全一樣的效果。
因為記憶體區域0xC0000000到0xCFFFFFFF都是可讀可寫的,所以複製過程幾乎不會碰到記憶體的邊界。在複製了0x40000個位元組後,整個記憶體可被看作是整體移位了,其中有些資料被覆蓋並且丟失了。
88w8688中的CPU是單核的,所以複製過程中晶片不會崩潰直到有中斷產生。因為這時記憶體已被破壞,在大多數情況下,晶片崩潰在中斷過程中。
中斷控制器給中斷系統提供了一個介面。當一箇中斷產生時,韌體可從暫存器中獲取中斷事件型別並呼叫相應的中斷處理過程。
中斷源有很多,所以漏洞觸發後,晶片可能崩潰在多個位置。
一個可能性是中斷0x15的處理過程中,函式0x26580被呼叫。0xC000CC08是一個連結串列指標,這個指標在漏洞觸發後可能會被篡改。然而,對這個連結串列的操作很難給出獲得程式碼執行的機會。
另一個崩潰位置在時鐘中斷的處理過程中。處理過程有時會進行執行緒的切換,這時其他任務會被喚醒,那麼複製過程就會被暫停。然後晶片可能崩潰在其他任務恢復執行之後。在這種情況下,韌體通常崩潰在函式0x4D75C中。
這個函式會讀取一個指標0xC000D7DC,它指向結構TX_SEMAPHORE。觸發漏洞後,我們可以覆蓋這個指標,使其指向一個偽造的TX_SEMAPHORE結構。
如果偽造的TX_SEMAPHORE結構中的tx_semaphore_suspension_lis指標剛好指向偽造的TX_THREAD_STRUCT結構。那麼當函式_tx_semaphore_put()更新TX_THREAD_STRUCT結構中的連結串列的時候,我們可以得到一次任意地址寫的機會。
我們可以直接將“BL os_semaphore_put”指令的下一條指令改成跳轉指令來實現任意程式碼執行,因為ITCM記憶體區域是RWX的。困難在於我們需要同時在記憶體中堆噴兩種結構TX_SEMAPHORE和TX_THREAD_STRUCT,並且還要確保指標 tx_semaphore_suspension_list 指向TX_THREAD_STRUCT結構。這些條件可以被滿足,但是利用成功率會非常低。
我們主要關注第三個崩潰位置,在MCU中斷的處理過程中。指向struct_interface 結構的指標g_interface_sdio會被覆蓋。
結構中函式指標funB會被使用。如果g_interface_sdio被篡改,那麼就會直接實現程式碼執行。
這是當函式interface_call_funB()中的指令“BX R3” 在地址0x3CD4E執行時的一份暫存器日誌資訊。此時,g_interface_sdio被覆蓋成了0xabcd1211。
函式interface_call_funB()在地址0x4E3D0處被MCU中斷的處理過程使用。
當複製的源地址到達0xC0040000時,整個記憶體可被看作是做了一次偏移。當複製的源地址到達0xC0080000時,整個記憶體偏移了兩次。每次偏移的距離如下。
在多數情況下,漏洞觸發後再產生中斷時,這樣的記憶體偏移會發生3至5次。所以指標g_interface_sdio會被來自下列地址的資料所覆蓋。
地址0xC0024FAF、 0xC00237AF和0xC0021FAF剛好位於一個巨大的DMA buffer 0xC0021F90~0xC0025790之中。這個DMA buffer用於儲存無線晶片接收到的802.11資料幀。所以這個DMA buffer可以用來堆噴偽造的指標。
為了堆噴偽造的指標,我們可以傳送許多正常的802.11資料幀給晶片,其中填滿了偽造的指標。DMA buffer非常大,因此shellcode也可以直接放在資料幀中。為了提高利用的成功率,我們用了Egg Hunter在記憶體中查詢真正的shellcode。
如果g_interface_sdio被成功的覆蓋。Shellcode或egg hunter會非常的接近0xC000B818。我們所使用的偽造指標是0x41954,因為在地址0x41954+0x24處有一個指標0xC000B991。這樣,我們可以劫持$PC到0xC000B991。同時,指標0x41954可被作為正常的指令執行。
用這種方法有25%的成功率獲得程式碼執行。
攻擊主機系統
核心驅動中的漏洞可透過由晶片傳送命令資料包給主機系統來觸發。命令HostCmd_CMD_GET_MEM通常由函式wlan_get_firmware_mem()發起。
這種情況下,pdata_buf指向的buffer由kmalloc()分配,所以這是一個核心堆溢位。在真實環境中函式wlan_get_firmware_mem()不會被用到,並且堆溢位的利用較複雜。
然而,一個被攻陷的晶片在返回某個命令的結果時可以更改命令ID。因此漏洞可以在許多命令的處理過程中被觸發。這時,根據pdata_buf指向的位置,漏洞即可以是堆溢位也可以是棧溢位。我們找到了函式wlan_enable_11d(),它把區域性變數enable的地址作為pdata_buf。因此我們可以觸發一個棧溢位。
函式wlan_enable_11d()被wlan_11h_process_join()呼叫。顯然HostCmd_CMD_802_11_SNMP_MIB會在與AP的連線過程中被使用。韌體中的漏洞只能在Parrot已經加入AP後使用。為了觸發wlan_enable_11d()中的棧溢位,晶片需要欺騙核心驅動晶片已經斷開與AP的連線。接著,驅動會發起重連,在這個過程中HostCmd_CMD_802_11_SNMP_MIB會傳送給晶片。於是,為了觸發重連過程,晶片需要傳送EVENT_DISASSOCIATED事件給驅動。
當在晶片中觸發漏洞並獲得程式碼執行之後晶片不能再正常工作。所以我們的shellcode需要自己處理重連過程中的一系列命令並返回相應的結果。在命令HostCmd_CMD_802_11_SNMP_MIB來到之前,唯一一個我們要構造返回結果的命令是HostCmd_CMD_802_11_SCAN。下面是斷開連線到觸發核心漏洞的整個過程。
SDIO介面上事件和命令資料包的傳送可直接透過操作暫存器SDIO_CardStatus和SDIO_SQReadBaseAddress0來完成。SDIO介面上獲得核心發來的資料可藉助SDIO_SQWriteBaseAddress0暫存器。
Linux系統中命令執行
Parrot的Linux核心2.6.36不支援NX,所以可以直接在棧上執行shellcode。同時結構HostCmd_DS_COMMAND中的size是u16型別,所以shellcode可以足夠大來做許多事情。
在觸發棧溢位並控制$PC之後 ,$R7剛好指向核心棧,所以可以很方便的執行shellcode。
在shellcode中的函式run_linux_cmd呼叫了Usermode Helper API來執行Linux命令。
遠端獲取shell
在漏洞觸發後,晶片中的記憶體被完全破壞無法繼續正常工作。同時核心棧已損壞,無法正常工作。
為了讓Parrot的無線功能可以重新正常工作,我們做了如下事情:
1. 在向核心傳送完payload之後,我們透過如下命令重置了晶片。在這之後,核心驅動會重新發現晶片然後重新下載韌體。
2. 在shellcode的函式fun_ret()中呼叫核心函式rtnl_unlock()來解開rtnl_mutex鎖。否則Linux的無線功能會無法正常功能,導致Parrot被CID重啟。
3. 在shellcode的函式fun_ret()中呼叫do_exit()來終止使用者態程式wpa_supplicant並重新執行,這樣就不需要修復核心棧。
4. 殺掉程式ck5050並重新執行,否則稍後ck5050會因晶片重置而崩潰,導致Parrot被CID重啟。
為了遠端獲取shell,我們強制讓Parrot連入我們自己的AP並修改iptables規則。之後,便可透過23埠訪問到Parrot的shell。
最終拿到shell的成功率在10%左右。
完整的利用過程
1. 攻擊者傳送DEAUTH幀給附近的所有AP。
2. 當Tesla重連至AP時,攻擊者可以嗅探到特斯拉的MAC地址。
3. 堆噴偽造的指標,然後傳送Action幀來觸發韌體中的漏洞。
4. 函式memcpy()會一直工作直到有中斷產生。
5. 在晶片內成功執行任意程式碼。
6. 第一階段shellcode傳送EVENT_DISASSOCIATED事件給驅動。
7. 第一階段shellcode處理一系列命令並等待命令HostCmd_CMD_802_11_SNMP_MIB。
8. 第一階段shellcode透過SDIO介面傳送payload來觸發核心棧溢位。
9. 第二階段shellcode執行並呼叫call_usermodehelper()函式。
10. 成功執行Linux系統命令並嘗試修復Parrot的無線功能。
11. 攻擊者搭建自己的AP熱點及DHCP伺服器。
12. 透過Linux命令強制Parrot加入攻擊者建立的AP熱點並修改iptables規則。
13. 攻擊者可透過Parrot的23埠獲得Parrot的shell。
演示影片
總結
在這篇文章中,我們展示了Marvell無線晶片韌體及驅動中漏洞的具體細節,並演示瞭如何利用這兩個漏洞僅透過傳送無線資料包的形式遠端在Parrot系統內部實現命令執行。
負責任的漏洞披露
本文所提到的兩個漏洞已於2019年3月報告給Tesla,Tesla已經在2019.36.2版本中對該漏洞進行了修復。同時,Marvell也修復了該漏洞並針對該漏洞釋出了安全公告[4]。漏洞研究報告的披露已事先與特斯拉溝透過,特斯拉對我們的釋出知情。
你可以透過下列連結來跟蹤本文提到的漏洞。
1. https://www.cnvd.org.cn/flaw/show/CNVD-2019-44105
2. http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1040
3. http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1038
4. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13581
5. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13582
參考資料
[1] https://fccid.io/RKXFC6050W/Users-Manual/user-manual-1707044
[2] https://www.marvell.com/wireless/88w8688/
[3] https://www.marvell.com/wireless/assets/Marvell-88W8688-SoC.pdf
[4] https://www.marvell.com/documents/ioaj5dntk2ubykssa78s/
相關文章
- 在無線J2ME裝置上實現超文字傳輸協議2005-03-26協議
- 在無線J2ME裝置上實現超文字傳輸協議 (轉)2007-11-15協議
- 利用飛鴿協議實現通訊功能2016-04-11協議
- zstack協議棧2019-04-16協議
- 從CVE復現看棧溢位漏洞利用2024-04-12
- 如何利用 Netty 實現自定義協議通訊?2020-11-11Netty協議
- 【無線通訊篇 | Zstack協議棧】CC2530 Zigbee Zstack協議棧組網專案及詳細講解篇2021-06-22協議
- 1、zstack協議棧2019-06-12協議
- 實現 Raft 協議2023-12-19Raft協議
- 無線協作會議系統方案2021-08-03
- 物聯網實驗2 協議棧剖析2020-11-18協議
- 無線模組透過TCP/IP協議實現與PC端的資料傳輸解析2024-06-15TCP協議
- 深入理解 FastCGI 協議以及在 PHP 中的實現2017-06-22AST協議PHP
- TI的TCP/IP協議棧--NDK .2011-10-27TCP協議
- 特斯拉Model S可以無線充電:馬斯克說不是我乾的2016-02-24馬斯克
- 利用棧實現佇列(C語言實現)2014-06-19佇列C語言
- 無線通訊模組透過TCP/IP協議實現與PC端的資料傳輸2024-05-11TCP協議
- PPM協議的輸出實現2024-03-31協議
- 無線通訊協議設計的幾點要素2020-04-06協議
- Zstack協議棧結構的初步解析2017-03-01協議
- 在Linux中,TCP/IP協議棧的工作原理是什麼?2024-03-15LinuxTCP協議
- 如何在 Apinto 實現 HTTP 與gRPC 的協議轉換 (上)2023-03-16APIHTTPRPC協議
- i2s 協議2014-11-27協議
- HLS直播協議在B站的實踐2022-08-24協議
- TCP/IP協議棧在Linux核心中的執行時序分析2021-01-26TCP協議Linux
- 頁面連結跳轉--指定協議,半協議,無協議2018-08-10協議
- [計算機網路]協議棧2024-09-04計算機網路協議
- 實現無鎖的棧與佇列(4)2013-08-08佇列
- 實現無鎖的棧與佇列(3)2013-07-07佇列
- 實現無鎖的棧與佇列(1)2013-06-30佇列
- 實現無鎖的棧與佇列(2)2013-07-01佇列
- 2021看雪SDC議題回顧 | 基於模擬模擬的藍芽協議棧漏洞挖掘2021-11-01藍芽協議
- 實現網站由http協議轉為https協議2017-11-12網站HTTP協議
- 無狀態協議2018-03-30協議
- FFmpeg實現監控攝像頭的RTSP協議轉RTMP協議直播2017-07-05協議
- 利用orm 在業務程式碼無感知下,實現實現分庫分表2021-06-09ORM
- 遠端桌面協議 CredSSP 出現漏洞,影響所有版本的 Windows2018-03-15協議Windows
- 在 Firefox 上使用 Org 協議捕獲 URL2022-11-28Firefox協議