最近在Linux 內核TEE 子系統中發現了一個釋放後重引用(UAF)漏洞,影響版本小於等於5.15.11,分配了CVE編號CVE-2021-44733。
簡單的UAF無法進一步利用,在對漏洞代碼路徑做了進一步分析,簡單寫了個PoC測試後發現是可以覆蓋Linux內核中的函數指針的,本文沒有提供權限提升的漏洞利用代碼,但是在環境設置部分提供了運行OPTTE和漏洞利用的測試環境。
0x01 背景信息TEE是Trusted Execution Environment,也就是可信執行環境,通常用於數字版權保護(Digital Rights Management)、移動支付保護、敏感數據保護。 TEE的實現是基於ARM TrustZone。
需要介紹的另一個概念是REE,也就是Rich Execution Environment,被稱為通用執行環境,這是所有移動設備的通用運行環境,運行Android、iOS 系統。 OS的代碼量龐雜很容易出現漏洞,OS可以讀寫APP軟件中的所有數據,存在大量針對REE的高級攻擊技術和漏洞利用代碼。
TEE受硬件機制的保護,TEE隔離於REE,只能通過特定入口和TEE進行通信;TEE可以訪問REE的內存,可以抵禦某些硬件攻擊。
TEE實質上是一個可信子操作系統,比如最常見的ARM CPU上的TrustZone,運行在CPU芯片中的子OS,TEE驅動程序會處理TEE OS和上層操作之間的通信。
TEE 子系統會對TEE驅動程序進行註冊初始化、會管理Linux 和TEE的共享內存、會為TEE提供通用API。
驅動程序註冊初始化步驟:
1.根據設備類型,構造所需描述驅動的結構體。該結構體需要繼承structdevice_driver結構,並給幾個重要的成員初始化。
2.通過module_init宏調用驅動程序的初始化函數xx_init_module,在初始化函數中註冊驅動程序。
3.驅動程序會遍歷總線上的structdevice和structdevice_driver兩條鍊錶,調用總線的match函數,對設備與驅動程序進行匹配。
4.如果設備與驅動程序匹配成功,則調用驅動程序的probe函數。
什麼是註冊驅動程序:
初始化函數中調用的xx_register_driver函數就是註冊驅動程序,初始化函數執行其實非常簡單,執行一下xx_register_driver函數就會返回,這也是Linux驅動程序的標準註冊流程:module_init--xx_init_module--xx_register_driver。 TEE接口include/uapi/linux/tee.h中的結構體和宏定義提供了TEE的通用使用接口,用戶空間和客戶端可通過打開/dev/tee[0-9] 或/dev/teepriv[0-9] 連接TEE驅動程序。
下面是在include/uapi/linux/tee.h中定義的結構體接口:
TEE_IOC_SHM_ALLOC 分配共享內存並返回用戶空間可以映射的文件描述符。當用戶空間不再需要文件描述符時,它應該被關閉。當不再需要共享內存時,應該使用munmap() 取消映射以允許重用內存。
TEE_IOC_VERSION 讓用戶空間知道此驅動程序處理哪個TEE 及其功能。
TEE_IOC_OPEN_SESSION 打開一個到可信應用程序的新會話。
TEE_IOC_INVOKE 調用可信應用程序中的函數。
TEE_IOC_CANCEL 可以取消正在進行的TEE_IOC_OPEN_SESSION 或TEE_IOC_INVOKE。
TEE_IOC_CLOSE_SESSION 關閉與可信應用程序的會話。
mmap會將一個文件和其他對象映射到內存空間中。文件被映射到多個頁上,如果文件的大小不是所有頁的大小之和,最後一個頁不被使用的空間將會清零。 munmap執行相反的操作,刪除特定地址區域的對象映射。 TEE有兩種客戶端:普通客戶端和請求者客戶端,請求者客戶端是TEE在Linux OS中的輔助進程,用於訪問資源,比如文件系統訪問等。普通客戶端會打開/dev/tee[0-9]*,請求者kehu客戶端會打開/dev/teepriv[0-9]。
/dev/目錄下是Linux的外部設備文件,注意不是驅動文件,Linux會將所有設備認為是文件。客戶端和TEE 之間的大部分通信對驅動程序來說是不透明的。驅動程序的主要工作是接收來自客戶端的請求,將它們轉發到TEE 並將結果發回。在請求方的情況下,通信在另一個方向進行,TEE 向請求方發送請求,然後請求方將結果發回。
TEE是在安全環境中運行的可信操作系統,例如ARM CPU 上的TrustZone。 TEE 驅動程序會處理與TEE 通信所需的細節,驅動程序更重要的職責是為基於Globalplatform TEE 客戶端API 規範的TEE 提供通用API,而且還管理Linux 和TEE 之間的共享內存。該子系統可以通過CONFIG_OPTEE在ARM 架構的內核配置中進行配置來啟用。
The secure world包含表示為OP-TEE OS 的可信操作系統。在此操作系統之上,可以運行受信任應用程序(TA),這些應用程序可以在隔離環境中執行某些操作,參見圖1。
圖1:TEE 概述
The normal world 包括Linux 用戶空間和內核空間,可以使用客戶端應用程序(CA) 和TEE 子系統公開的API 與這些應用程序交互。 CA 可以向特定TA 打開會話並調用TA 實現的功能。在TA 和CA 之間來回傳遞任何參數都是使用共享內存完成的。接下來描述使用所有相關係統調用的CA 和TA 之間的交互。
1、CA 打開/dev/tee[0-9]以與驅動程序通信。對於使用這些API 的傳統方式,這是使用libteec 隱式完成的。
2、CA 可以使用IOCTL TEE_IOC_SHM_ALLOC。這將分配共享內存並返回一個文件描述符,用戶空間可以將其用作mmap 的一部分。
3、下一步是使用IOCTL TEE_IOC_OPEN_SESSION和指定特定TA 的uuid建立會話。這個uuid 在TA 的編譯過程中是硬編碼的。
4、為了調用TA 中的特定函數,CA 通過指定函數的標識符以及輸入參數來調用該函數,這裡使用的是TEE_IOC_INVOKE。
5、當CA 完成所有請求後,可以使用TEE_IOC_CLOSE_SESSION關閉會話。
圖2:CA 和TA 之間的會話
客戶端和TEE 之間的大部分通信對驅動程序來說是不透明的。驅動程序的主要工作是管理上下文、接收來自客戶端的請求、將它們轉發到TEE 並將結果發回。
0x02 對TEE 驅動器的模糊測試CVE-2021-44733 是使用syzkaller 模糊測試發現的,下面提供了相關的描述文件。 ioctl$TEE_SHM_REGISTER_FD只是Linaro內核樹的一部分,根據syzkaller 文檔正確配置後,“Setting up the environment”中提供的環境就可以用於模糊測試了。
#include
resourcefd_tee0[fd]
resourcesession_resource[int32]
openat$tee0(fdconst[AT_FDCWD],devptr[in,string['/dev/tee0']],flagsflags[open_flags],modeflags[open_mode])fd_tee0
ioctl$TEE_OPEN_SESSION(fdfd_tee0,cmdconst[0x8010a402],argptr[inout,tee_ioctl_buf_data_session])
ioctl$TEE_INVOKE(fdfd_tee0,cmdconst[0x8010a403],argptr[inout,tee_ioctl_buf_data_invoke])
ioctl$TEE_CANCEL(fdfd_tee0,cmdconst[0x8008a404],argptr[in,tee_ioctl_buf_data_cancel])
ioctl$TEE_CLOSE_SESSION(fdfd_tee0,cmdconst[0x8004a405],argptr[in,tee_ioctl_buf_data_close])
ioctl$TEE_VERSION(fdfd_tee0,cmdconst[0x800ca400],argptr[out,tee_ioctl_buf_data_version])
ioctl$TEE_SHM_ALLOC(fdfd_tee0,cmdconst[0xc010a401],argptr[inout,tee_ioctl_buf_data_shm_alloc])
ioctl$TEE_SHM_REGISTER(fdfd_tee0,cmdconst[0xc018a409],argptr[inout,tee_ioctl_buf_data_shm_register])
ioctl$TEE_SHM_REGISTER_FD(fdfd_tee0,cmdconst[0xc018a408],argptr[inout,tee_ioctl_buf_data_shm_register_fd])
ioctl$TEE_SUPPL_RECV(fdfd_tee0,cmdconst[0x8010a406],argptr[inout,tee_ioctl_buf_suppl_recv])
ioctl$TEE_SUPPL_SEND(fdfd_tee0,cmdconst[0x8010a407],argptr[inout,tee_ioctl_buf_suppl_send])
#COMMON
#=======================================================
defineTEE_IOCTL_UUID_LEN16
tee_ioctl_param_struct{
attrflags[TEE_IOCTL_PARAM_ATTR_TYPE,int64]
aint64
bint64
cint64
}
TEE_IOCTL_PARAM_ATTR_TYPE=0,1,2,3,5,6,7
TEE_LOGIN=0,1,2,4,5,6
#OPENSESSION
#=======================================================
tee_ioctl_buf_data_session{
buf_ptrptr64[inout,tee_ioctl_open_session_struct]
buf_lenlen[buf_ptr,int64]
}
tee_ioctl_open_session_struct{
uuidarray[int8,TEE_IOCTL_UUID_LEN](in)
clnt_uuidarray[int8,TEE_IOCTL_UUID_LEN](in)
clnt_loginflags[TEE_LOGIN,int32](in)
cancel_idint32(in)
sessionsession_resource(out)
retint32(out)
ret_originint32(out)
num_paramslen[params,int32](in)
paramsarray[tee_ioctl_param_struct](in)
}
#INVOKE
#=======================================================
tee_ioctl_buf_data_invoke{
buf_ptrptr64[inout,tee_ioctl_invoke_struct]
buf_lenlen[buf_ptr,int64]
}
tee_ioctl_invoke_struct{
funcint32(in)
sessionsession_resource(in)
cancel_idint32(in)
retint32(out)
ret_originint32(out)
num_paramslen[params,int32](in)
paramsarray[tee_ioctl_param_struct](in)
}
#CANCELSESSION
#=======================================================
tee_ioctl_buf_data_cancel{
cancel_idint32(in)
sessionsession_resource(in)
}
#CLOSESESSION
#=======================================================
tee_ioctl_buf_data_close{
sessionsession_resource(in)
}
#VERSION
#=======================================================
tee_ioctl_buf_data_version{
impl_idint32(out)
impl_capsint32(out)
gen_capsint32(out)
}
#SHMALLOC
#=======================================================
tee_ioctl_buf_data_shm_alloc{
sizeint64(inout)
flagsconst[0,int32](inout)
idint32(out)
}
#SHMREGISTER
#=======================================================
tee_ioctl_buf_data_shm_register{
addrint64(in)
lengthint64(inout)
flagsconst[0,int32](inout)
idint32(out)
}
#SHMREGISTERFD
#=======================================================
tee_ioctl_buf_data_shm_register_fd{
fdint64(in)
sizeint64(out)
flagsconst[0,int32](in)
idint32(out)
}[align[8]]
#SUPPLICANTRECV
#=======================================================
tee_ioctl_buf_suppl_recv{
funcint32(in)
num_paramslen[params,int32](inout)
paramsarray[tee_ioctl_param_struct](inout)
}
#SUPPLICANTSEND
#=======================================================
tee_ioctl_buf_suppl_send{
retint32(out)
num_paramslen[params,int32](in)
paramsarray[tee_ioctl_param_struct](in)
}模糊測試中的崩潰是由於持有互斥對象時task _ struct 的use-after-free 漏洞:
==================================================================
BUG:KASAN:use-after-freein__mutex_lock.constprop.0+0x118c/0x11c4
Readofsize4ataddr863b0714bytaskoptee_example_r/244
CPU:0PID:244Comm:optee_example_rTainted:GD5.14.0#151
Hardwarename:GenericDTbasedsystem
[](unwind_backtrace)from[](show_stack+0x20/0x24)
[](show_stack)from[](dump_stack_lvl+0x5c/0x68)
[](dump_stack_lvl)from[](print_address_description.constprop.0+0x38/0x304)
[](print_address_description.constprop.0)from[](kasan_report+0x1c0/0x1dc)
[](kasan_report)from[](__mutex_lock.constprop.0+0x118c/0x11c4)
[](__mutex_lock.constprop.0)from[](mutex_lock+0x128/0x13c)
[](mutex_lock)from[](tee_shm_release+0x4b0/0x6cc)
[](tee_shm_release)from[](dma_buf_release+0x1b8/0x2f0)
[](dma_buf_release)from[](__dentry_kill+0x4c4/0x678)
[](__dentry_kill)from[](dput+0x630/0xba4)
[](dput)from[](__fput+0x3b4/0x900)
[](__fput)from[](task_work_run+0x15c/0x230)
[](task_work_run)from[](do_exit+0x103c/0x3770)
[](do_exit)from[](do_group_exit+0x134/0x3ac)
[](do_group_exit)from[](get_signal+0x7d8/0x2f28)
[](get_signal)from[](do_work_pending+0x984/0x154c)
[](do_work_pending)from[](slow_work_pending+0xc/0x20)
Exceptionstack(0x85743fb0to0x85743ff8)
3fa0:00023108000000800000000000000000
3fc0:66bca2d066bca2d066bca2d0000000f066bca2d066bca340000000006ec00b0c
3fe0:66bc9cc866bc9cb80001165566c80c20000e013000023108
Allocatedbytask242:
set_alloc_info+0x48/0x50
__kasan_slab_alloc+0x48/0x58
kmem_cache_alloc+0x14c/0x314
copy_process+0x2014/0x7b18
kernel_clone+0x244/0xfc8
sys_clone+0xc8/0xec
ret_fast_syscall+0x0/0x58
0x6ec00a10
Freedbytask67:
kasan_set_track+0x28/0x30
kasan_set_free_info+0x20
Recommended Comments