Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863103344

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

最近在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。

image-20220117152205863.png

圖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關閉會話。

image.png

圖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