聯發科芯片Rootkit漏洞分析(CVE-2020-0069)

發布時間 2020-06-24

一、漏洞背景


2020年3月,谷歌修補了一個存在于聯發科芯片中的安全漏洞(CVE-2020-0069),漏洞影響20余款聯發科芯片和數百萬Android設備。該漏洞存在于MediaTek Command Queue驅動(CMDQ命令隊列驅動),允許本地攻擊者實現對物理內存地址的任意讀寫,從而導致權限提升。


二、受影響國產手機型號


Huawei GR3 TAG-L21

Huawei Y5II

Huawei Y6II MT6735 series

Lenovo A5

Lenovo C2 series

Lenovo Tab E7

Lenovo Tab E8

Lenovo Tab2 A10-70F

Meizu M5c

Meizu M6

Meizu Pro 7 Plus

Oppo A59 series

Oppo A5s

Oppo A7x -- up to Android 8.x

Oppo F5 series/A73 -- up to A.39

Oppo F7 series -- Android 8.x only

Oppo F9 series -- Android 8.x only

Oppo R9xm series

Xiaomi Redmi 6/6A series

ZTE Blade A530

ZTE Blade D6/V6

ZTE Quest 5 Z3351S


三、CMDQ驅動簡析


DMA(直接內存訪問)是允許專用硬件直接從主存儲器(RAM)發送或接收數據的一種特性。其目的是通過允許大內存訪問而不過多占用CPU來加速系統。MediaTek Command Queue驅動(CMDQ命令隊列驅動)允許從用戶層與DMA控制器通信,以實現媒體或顯示相關的任務。


基于Redmi 6/6A 源代碼分析,在cmdq_driver.h頭文件中,聲明cmdq驅動的IOCTL調用如下:



CMDQ_IOCTL_ALLOC_WRITE_ADDRESS指令為分配一個DMA緩沖區。

CMDQ_IOCTL_FREE_WRITE_ADDRESS指令為釋放一個DMA緩沖區。

CMDQ_IOCTL_READ_WRITE_ADDRESS指令為讀取一個DMA緩沖區中的數據。

CMDQ_IOCTL_EXEC_COMMAND指令運行發送其他命令。


1、分配過程


通過CMDQ_IOCTL_ALLOC_WRITE_ADDRESS調用cmdqCoreAllocWriteAddress ()函數,分配一個DMA緩沖區,該函數關鍵代碼實現如下:



然后,調用cmdq_core_alloc_hw_buffer()函數分配DMA緩沖區,pWriteAddr->va是虛擬地址,pWriteAddr->pa為物理地址,兩者一一對應。并清理緩沖區。



最后,將物理地址賦值到*paStart,并將pWriteAddr結構體添加到gCmdqContext.writeAddrList鏈表中。


2、執行命令過程


在CMDQ_IOCTL_EXEC_COMMAND調用中,采用cmdqCommandStruct結構體作為參數,結構體定義如下:



pVABase指向用戶層存放命令的緩沖區,緩沖區大小放在blockSize中。其中cmdqReadAddressStruct結構體定義如下:



DmaAddresses是要讀取的物理地址,讀取的值存放在values中。在CMDQ_IOCTL_EXEC_COMMAND命令的執行過程,實現代碼如下:



函數調用路徑如下:



Cmdq_core_acquire_task()函數會將command綁定到task中執行。具體實現如下:



調用cmdq_core_find_free_task()函數獲取一個空閑task。拿到空閑task并進行一些初始化設置,然后開始調用cmdq_core_insert_read_reg_command()函數執行命令。



該函數實現分析,先拷貝用戶層傳入的命令到DMA緩沖區中。



pCommandDesc->pVABase是存放命令的內存起始地址??截愅昝詈?,后面分幾種方式結尾。


這里不做深究,最后拷貝EOC和JUMP指令結尾。這里也是將用戶層傳入的命令拷貝過來。



從cmdq_core_acquire_task()函數中返回后,如下:



調用cmdq_core_consume_waiting_list()函數執行task。先從等待隊列中獲取task。



然后,獲取空閑內核線程。



最后,將task綁定到thread中去執行。



四、讀寫命令分析


以cmdq_test.c測試代碼為例,分析理解一個完整的讀寫命令構造。cmdq驅動中定義了兩類寄存器,一類是地址寄存器用于存放地址,一類是數值寄存器用于存放讀取或寫入的數值。



regResults是虛擬地址,調用cmdq_core_alloc_hw_buffer()函數分配一個dma地址,regResultsMVA與之對應,然后設置regResults中的數據。開始拼接讀取和寫入命令:



將regResults[0]的地址寫入CMDQ_DATA_REG_DEBUG_DST類型的地址寄存器中。



然后,從CMDQ_DATA_REG_DEBUG_DST地址寄存器中讀取數據并寫入到CMDQ_DATA_REG_DEBUG數值寄存器中。這時候,CMDQ_DATA_REG_DEBUG數值寄存器中的值應該為0xdeaddead。



接著,將regResults[1]的地址轉存到CMDQ_DATA_REG_DEBUG_DST地址寄存器中。



最后,將CMDQ_DATA_REG_DEBUG數值寄存器中的0xdeaddead寫入到CMDQ_DATA_REG_DEBUG_DST地址寄存器中保存的regResults[1]的地址中,即regResults[1]=0xdeaddead。判斷regResults[0]和regResults[1]是否相等。



如果相等,說明讀寫成功。


五、PoC分析與測試


(1)PoC代碼中,執行寫操作的關鍵代碼如下:



寫入過程中,先將value[count]移動到CMDQ_DATA_REG_DEBUG數值寄存器中,然后將pa_address+offset地址移動到CMDQ_DATA_REG_DEBUG_DST地址寄存器中,最后將CMDQ_DATA_REG_DEBUG數值寄存器中的value寫入到CMDQ_DATA_REG_DEBUG_DST地址寄存器中保存的pa_address+offset地址中,即*(pa_address+offset)= value[count]。


(2)PoC代碼中,執行讀操作的關鍵代碼如下:



讀取過程中,第一步先將pa_address+offset地址移動到CMDQ_DATA_REG_DEBUG_DST地址寄存器中,然后從CMDQ_DATA__REG_DEBUG_DST地址寄存器中存儲的地址pa_address+offset中讀取數據放到CMDQ_DATA_REG_DEBUG數據寄存器中,再將dma_address+offset地址移動到CMDQ_DATA_REG_DEBUG_DST地址寄存器中,最后將CMDQ_DATA_REG_DEBUG數值寄存器中保存的數據寫入到CMDQ_DATA_REG_DEBUG_DST地址寄存器中存儲的dma_address+offset地址中,即*(dma_address+ offset) = *(pa_address + offset)。


(3)在Reami6測試機中,執行PoC測試,成功將Linux修改成minix。



參考鏈接:

[1]https://github.com/MiCode/Xiaomi_Kernel_OpenSource/tree/cactus-p-oss/drivers/misc/mediatek/cmdq

[2]https://github.com/quarkslab/CVE-2020-0069_poc/blob/master/jni/kernel_rw.c

[3]https://blog.quarkslab.com/cve-2020-0069-autopsy-of-the-most-stable-mediatek-rootkit.html

[4]https://forum.xda-developers.com/android/development/amazing-temp-root-mediatek-armv8-t3922213

[5]https://source.android.com/security/bulletin/2020-03-01



啟明星辰積極防御實驗室(ADLab)


ADLab成立于1999年,是中國安全行業最早成立的攻防技術研究實驗室之一,微軟MAPP計劃核心成員,“黑雀攻擊”概念首推者。截止目前,ADLab已通過CVE累計發布安全漏洞1000余個,通過 CNVD/CNNVD累計發布安全漏洞800余個,持續保持國際網絡安全領域一流水準。實驗室研究方向涵蓋操作系統與應用系統安全研究、移動智能終端安全研究、物聯網智能設備安全研究、Web安全研究、工控系統安全研究、云安全研究。研究成果應用于產品核心技術研究、國家重點科技項目攻關、專業安全服務等。