
TEE 드라이버(CVE-2021-44733) 악용을 위한 취약한 커널 환경
최근 리눅스 커널 TEE 서브시스템(버전 5.15.11까지 포함)에서 use-after-free 취약점이 발견되어 CVE-2021-44733 [1]이 할당되었습니다.
처음에는 여러 이유로 익스플로잇이 불가능해 보였지만, 취약한 코드 경로를 추가로 분석하고 개념 증명 익스플로잇을 구현한 결과 커널의 함수 포인터를 덮어쓸 수 있었습니다. 이 글에서는 권한 상승 페이로드를 제시하지 않지만, OPTEE와 익스플로잇을 실행하기 위한 전체 환경을 추가 테스트용으로 제공합니다('환경 설정' 참조).
TEE(Trusted Execution Environment)는 ARM CPU의 TrustZone과 같은 보안 환경에서 실행되는 신뢰할 수 있는 OS입니다. TEE 드라이버는 TEE와 통신하는 데 필요한 세부 사항을 처리합니다. 드라이버의 주요 임무 중 일부는 Globalplatform TEE 클라이언트 API 사양 [3]에 기반한 TEE용 일반 API를 제공하는 것뿐만 아니라 Linux와 TEE 간의 공유 메모리를 관리하는 것입니다. 이 서브시스템은 ARM 아키텍처의 커널 구성에서 CONFIG_OPTEE를 설정하여 활성화할 수 있습니다.
보안 월드에는 OP-TEE OS [4]라는 신뢰할 수 있는 OS가 포함되어 있습니다. 이 OS 위에서 소위 Trusted Application(TA)을 실행할 수 있으며, 이는 격리된 환경에서 일부 작업을 수행할 수 있습니다(그림 1 참조).
그림 1: TEE 개요 - Linaro의 프레젠테이션 [5]에서 발췌
노멀 월드(Linux 사용자 공간/커널)는 클라이언트 애플리케이션(CA)과 TEE 서브시스템이 노출하는 API를 사용하여 이러한 애플리케이션과 상호 작용할 수 있습니다. CA는 특정 TA에 대한 세션을 열고 TA가 구현한 함수를 호출할 수 있습니다. TA와 CA 간의 인수 전달은 공유 메모리를 사용하여 수행됩니다. CA와 TA 간의 상호 작용은 관련 모든 시스템 호출을 사용하여 다음에 설명됩니다.
CA는 드라이버와 통신하기 위해 /dev/tee[0-9]를 엽니다. 참고로, 이러한 API를 사용하는 일반적인 방법에서는 libteec를 사용하여 암시적으로 수행됩니다.
공유 메모리는 CA가 IOCTL TEE_IOC_SHM_ALLOC을 사용하여 등록할 수 있습니다. 이는 공유 메모리를 할당하고 사용자 공간이 mmap의 일부로 사용할 수 있는 파일 디스크립터를 반환합니다.
다음 단계는 IOCTL TEE_IOC_OPEN_SESSION을 사용하고 특정 TA의 uuid를 지정하여 세션을 설정하는 것입니다. 이 uuid는 TA 컴파일 중에 하드코딩됩니다.
TA의 특정 함수를 호출하려면 CA가 함수 식별자와 입력 인수를 지정하여 호출하며, 이는 TEE_IOC_INVOKE를 사용하여 수행됩니다.
CA가 모든 요청을 완료하면 TEE_IOC_CLOSE_SESSION을 사용하여 세션을 닫을 수 있습니다.
그림 2: CA와 TA 간의 세션 - Linaro의 프레젠테이션 [5]에서 발췌
클라이언트와 TEE 간의 통신의 대부분은 드라이버에 대해 불투명합니다. 드라이버의 주요 작업은 컨텍스트를 관리하고, 클라이언트로부터 요청을 수신하고, 이를 TEE로 전달하고, 결과를 다시 보내는 것입니다 [2].
CVE-2021-44733은 syzkaller를 사용한 퍼징을 통해 발견되었습니다. 이에 사용된 설명 파일은 아래에 제공됩니다. ioctl$TEE_SHM_REGISTER_FD는 Linaro(메인테이너) 커널 트리에만 있으며 업스트림에는 없습니다. '환경 설정'에서 제공된 환경은 syzkaller 문서 [6]에 따라 적절히 구성하면 퍼징에 사용할 수 있습니다.```
#include <uapi/linux/tee.h>
resource fd_tee0[fd] resource session_resource[int32]
openat$tee0(fd const[AT_FDCWD], dev ptr[in, string["/dev/tee0"]], flags flags[open_flags], mode flags[open_mode]) fd_tee0 ioctl$TEE_OPEN_SESSION(fd fd_tee0, cmd const[0x8010a402], arg ptr[inout, tee_ioctl_buf_data_session]) ioctl$TEE_INVOKE(fd fd_tee0, cmd const[0x8010a403], arg ptr[inout, tee_ioctl_buf_data_invoke]) ioctl$TEE_CANCEL(fd fd_tee0, cmd const[0x8008a404], arg ptr[in, tee_ioctl_buf_data_cancel]) ioctl$TEE_CLOSE_SESSION(fd fd_tee0, cmd const[0x8004a405], arg ptr[in, tee_ioctl_buf_data_close]) ioctl$TEE_VERSION(fd fd_tee0, cmd const[0x800ca400], arg ptr[out, tee_ioctl_buf_data_version]) ioctl$TEE_SHM_ALLOC(fd fd_tee0, cmd const[0xc010a401], arg ptr[inout, tee_ioctl_buf_data_shm_alloc]) ioctl$TEE_SHM_REGISTER(fd fd_tee0, cmd const[0xc018a409], arg ptr[inout, tee_ioctl_buf_data_shm_register]) ioctl$TEE_SHM_REGISTER_FD(fd fd_tee0, cmd const[0xc018a408], arg ptr[inout, tee_ioctl_buf_data_shm_register_fd]) ioctl$TEE_SUPPL_RECV(fd fd_tee0, cmd const[0x8010a406], arg ptr[inout, tee_ioctl_buf_suppl_recv]) ioctl$TEE_SUPPL_SEND(fd fd_tee0, cmd const[0x8010a407], arg ptr[inout, tee_ioctl_buf_suppl_send])
#=======================================================
define TEE_IOCTL_UUID_LEN 16
tee_ioctl_param_struct { attr flags[TEE_IOCTL_PARAM_ATTR_TYPE, int64] a int64 b int64 c int64 }
TEE_IOCTL_PARAM_ATTR_TYPE = 0, 1, 2, 3, 5, 6, 7 TEE_LOGIN = 0, 1, 2, 4, 5, 6
#=======================================================
tee_ioctl_buf_data_session { buf_ptr ptr64[inout, tee_ioctl_open_session_struct] buf_len len[buf_ptr, int64] }
tee_ioctl_open_session_struct { uuid array[int8, TEE_IOCTL_UUID_LEN] (in) clnt_uuid array[int8, TEE_IOCTL_UUID_LEN] (in) clnt_login flags[TEE_LOGIN, int32] (in) cancel_id int32 (in) session session_resource (out) ret int32 (out) ret_origin int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
#=======================================================
tee_ioctl_buf_data_invoke { buf_ptr ptr64[inout, tee_ioctl_invoke_struct] buf_len len[buf_ptr, int64] }
tee_ioctl_invoke_struct { func int32 (in) session session_resource (in) cancel_id int32 (in) ret int32 (out) ret_origin int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
#=======================================================
tee_ioctl_buf_data_cancel { cancel_id int32 (in) session session_resource (in) }
#=======================================================
tee_ioctl_buf_data_close { session session_resource (in) }
#=======================================================
tee_ioctl_buf_data_version { impl_id int32 (out) impl_caps int32 (out) gen_caps int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_alloc { size int64 (inout) flags const[0, int32] (inout) id int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_register { addr int64 (in) length int64 (inout) flags const[0, int32] (inout) id int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_register_fd { fd int64 (in) size int64 (out) flags const[0, int32] (in) id int32 (out) } [align[8]]
#=======================================================
tee_ioctl_buf_suppl_recv { func int32 (in) num_params len[params, int32] (inout) params array[tee_ioctl_param_struct] (inout) }
#=======================================================
tee_ioctl_buf_suppl_send { ret int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
퍼징 중에 주목을 끈 충돌은 뮤텍스가 보유된 상태에서 task_struct 객체의 use-after-free와 관련되었습니다:```
==================================================================
BUG: KASAN: use-after-free in __mutex_lock.constprop.0+0x118c/0x11c4
Read of size 4 at addr 863b0714 by task optee_example_r/244
CPU: 0 PID: 244 Comm: optee_example_r Tainted: G D 5.14.0 #151
Hardware name: Generic DT based system
[<8012b204>] (unwind_backtrace) from [<8011f460>] (show_stack+0x20/0x24)
[<8011f460>] (show_stack) from [<81cf0108>] (dump_stack_lvl+0x5c/0x68)
[<81cf0108>] (dump_stack_lvl) from [<80650f04>] (print_address_description.constprop.0+0x38/0x304)
[<80650f04>] (print_address_description.constprop.0) from [<80651548>] (kasan_report+0x1c0/0x1dc)
[<80651548>] (kasan_report) from [<81d0a9b4>] (__mutex_lock.constprop.0+0x118c/0x11c4)
[<81d0a9b4>] (__mutex_lock.constprop.0) from [<81d0ada4>] (mutex_lock+0x128/0x13c)
[<81d0ada4>] (mutex_lock) from [<817424b0>] (tee_shm_release+0x4b0/0x6cc)
[<817424b0>] (tee_shm_release) from [<81303674>] (dma_buf_release+0x1b8/0x2f0)
[<81303674>] (dma_buf_release) from [<806d5ac0>] (__dentry_kill+0x4c4/0x678)
[<806d5ac0>] (__dentry_kill) from [<806d8a68>] (dput+0x630/0xba4)
[<806d8a68>] (dput) from [<8067d890>] (__fput+0x3b4/0x900)
[<8067d890>] (__fput) from [<801dd1d8>] (task_work_run+0x15c/0x230)
[<801dd1d8>] (task_work_run) from [<80172b70>] (do_exit+0x103c/0x3770)
[<80172b70>] (do_exit) from [<80179aec>] (do_group_exit+0x134/0x3ac)
[<80179aec>] (do_group_exit) from [<801a7658>] (get_signal+0x7d8/0x2f28)
[<801a7658>] (get_signal) from [<8011dea4>] (do_work_pending+0x984/0x154c)
[<8011dea4>] (do_work_pending) from [<801000d0>] (slow_work_pending+0xc/0x20)
Exception stack(0x85743fb0 to 0x85743ff8)
3fa0: 00023108 00000080 00000000 00000000
3fc0: 66bca2d0 66bca2d0 66bca2d0 000000f0 66bca2d0 66bca340 00000000 6ec00b0c
3fe0: 66bc9cc8 66bc9cb8 00011655 66c80c20 000e0130 00023108
Allocated by task 242:
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
Freed by task 67:
kasan_set_track+0x28/0x30
kasan_set_free_info+0x20/0x34
__kasan_slab_free+0xdc/0x108
kmem_cache_free+0x80/0x394
__put_task_struct+0x2b4/0x35c
delayed_put_task_struct+0x104/0x384
rcu_core+0x91c/0x2a68
__do_softirq+0x2fc/0xfb8
Last potentially related work creation:
kasan_record_aux_stack+0xb8/0xc0
call_rcu+0x9c/0xfd0
put_task_struct_rcu_user+0x9c/0xbc
finish_task_switch+0x534/0xa10
__schedule+0x934/0x1adc
schedule_idle+0x9c/0x120
do_idle+0x2ec/0x434
cpu_startup_entry+0x18/0x1c
start_kernel+0x3ec/0x430
The buggy address belongs to the object at 863b0700
which belongs to the cache task_struct of size 1664
The buggy address is located 20 bytes inside of
1664-byte region [863b0700, 863b0d80)
The buggy address belongs to the page:
page:f09c9565 refcount:1 mapcount:0 mapping:00000000 index:0x0 pfn:0x463b0
head:f09c9565 order:3 compound_mapcount:0 compound_pincount:0
flags: 0x10200(slab|head|zone=0)
raw: 00010200 00000000 00000122 82802e00 00000000 80120012 ffffffff 00000001
page dumped because: kasan: bad access detected
Memory state around the buggy address:
863b0600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
863b0680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>863b0700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
863b0780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
863b0800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
이는 한 스레드가 (우리의 경우 존재하지 않는 TA를 대상으로) 세션을 여는 동안 다른 스레드가 TEE_IOC_SHM_ALLOC에서 모든 파일 디스크립터를 닫음으로써 트리거되었습니다. Syzkaller가 이를 재현하는 데 성공했으며, 재현 코드를 실험하고 TEE_IOC_OPEN_SESSION 호출을 약간 지연시킴으로써 kmalloc-64 캐시에 속하는 객체에 대해 다른 UAF가 발생했습니다:```
================================================================== BUG: KASAN: use-after-free in tee_shm_put+0x8c/0x98 Read of size 4 at addr 86467020 by task optee_example_h/216
CPU: 0 PID: 216 Comm: optee_example_h Not tainted 5.14.0 #21 Hardware name: Generic DT based system [<80122584>] (unwind_backtrace) from [<80117fd4>] (show_stack+0x10/0x14) [<80117fd4>] (show_stack) from [<819d57a0>] (dump_stack_lvl+0x40/0x4c) [<819d57a0>] (dump_stack_lvl) from [<819ced74>] (print_address_description.constprop.0+0x5c/0x2d8) [<819ced74>] (print_address_description.constprop.0) from [<805a12c4>] (kasan_report+0x1b4/0x1d0) [<805a12c4>] (kasan_report) from [<814cc6b0>] (tee_shm_put+0x8c/0x98) [<814cc6b0>] (tee_shm_put) from [<814c9b2c>] (tee_ioctl+0x1578/0x2e44) [<814c9b2c>] (tee_ioctl) from [<806038ec>] (sys_ioctl+0x918/0x1e70) [<806038ec>] (sys_ioctl) from [<80100060>] (ret_fast_syscall+0x0/0x58) Exception stack(0x86417fa8 to 0x86417ff0) 7fa0: 00000080 00000000 00000003 8010a402 200001c0 00000003 7fc0: 00000080 00000000 00423018 00000036 66c562d0 66c55e10 66c562d0 6ebebafc 7fe0: 66c55cb0 66c55ca0 004114bd 66cebd72
Allocated by task 216: tee_shm_alloc+0x15c/0x7e8 tee_ioctl+0x8d0/0x2e44 sys_ioctl+0x918/0x1e70 ret_fast_syscall+0x0/0x58 0x66c55ca0
Freed by task 215: kasan_set_free_info+0x20/0x34 __kasan_slab_free+0xdc/0x108 kfree+0x98/0x294 tee_shm_release+0x1dc/0x610 dma_buf_release+0x180/0x2a0 __dentry_kill+0x488/0x6ac __fput+0x2f0/0x7b4 task_work_run+0x178/0x230 do_work_pending+0xaf8/0x10a8 slow_work_pending+0xc/0x20 0x66d5bd16
The buggy address belongs to the object at 86467000 which belongs to the cache kmalloc-64 of size 64 The buggy address is located 32 bytes inside of 64-byte region [86467000, 86467040) The buggy address belongs to the page: page:(ptrval) refcount:1 mapcount:0 mapping:00000000 index:0x0 pfn:0x46467 flags: 0x200(slab|zone=0) raw: 00000200 00000000 00000122 82401200 00000000 00200020 ffffffff 00000001 page dumped because: kasan: bad access detected
Memory state around the buggy address: 86466f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 86466f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
86467000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ^ 86467080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc 86467100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ==================================================================
이 취약점은 기존 TA와 세션이 설정되지 않은 상태에서 TEE 드라이버를 퍼징하여 발견되었습니다. 이는 syzkaller에서 소위 유사 시스템 콜(pseudo syscalls)을 사용하여 일부 TA에 대한 세션을 설정하고 시작하는 방식으로 더 확장될 수 있습니다.
## 근본 원인 분석
결론은 `tee_shm:dmabuf` 객체의 수명 추적에 대한 설계 문제입니다. 드라이버는 `tee_ioctl_shm_alloc()` 호출 이후 사용자 공간이 유일한 참조 카운트를 유지하도록 설계되었습니다.
객체가 여전히 드라이버의 IDR 객체에 존재한다면 dmabuf에 대한 참조가 여전히 유효하고 참조 카운트를 증가시킬 수 있다고 가정합니다. 이는 부분적으로만 사실임이 밝혀졌습니다. dmabuf 메모리는 여전히 dmabuf 드라이버가 소유하고 있지만, 소멸 과정에 있을 수 있으며 참조 카운트를 다시 0이 아닌 값으로 만들어도 이를 중단할 수 없습니다.
문제를 촉발하는 시나리오는 멀티스레드 애플리케이션에서 한 스레드가 dmabuf 파일 디스크립터를 닫는 동시에 다른 스레드가 해당 공유 메모리를 참조하는 IOCTL 명령 `TEE_IOC_OPEN_SESSION` 또는 `TEE_IOC_INVOKE`를 호출하는 경우입니다.
사용자 공간이 fd를 닫을 때 dmabuf의 소멸 과정을 추적하면 커널에서 다음 코드가 실행됩니다:
1. `fput()`
2. `fput_many()` >> 파일 참조 카운트가 0에 도달합니다. 경쟁 조건 창이 열립니다.
3. `[task_work가 예약됨]`
4. `__fput`
5. `dput`
6. `dma_buf_release`
7. `tee_shm_release`
8. `mutex_lock(teedev->mutex)`
9. `idr_remove(teedev->idr, shm->id)` >> 이제 shm 객체는 더 이상 사용자 공간에서 참조할 수 없습니다. 경쟁 조건 창이 닫힙니다.
10. `mutex_unlock()`
즉, IDR 테이블과 그 뮤텍스 잠금은 dmabuf와 해당 `tee_shm`이 여전히 살아 있음을 보장할 수 없습니다. `tee_shm_get_from_id()`를 호출하여 `fput()`과 경쟁하는 프로세스는 곧 종료될 shm에 대한 참조를 얻을 수 있습니다.```
/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
* count
* @ctx: Context owning the shared memory
* @id: Id of shared memory object
* @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
{
struct tee_device *teedev;
struct tee_shm *shm;
if (!ctx)
return ERR_PTR(-EINVAL);
teedev = ctx->teedev;
mutex_lock(&teedev->mutex);
shm = idr_find(&teedev->idr, id);
if (!shm || shm->ctx != ctx)
shm = ERR_PTR(-EINVAL);
else if (shm->flags & TEE_SHM_DMA_BUF)
get_dma_buf(shm->dmabuf);
mutex_unlock(&teedev->mutex);
return shm;
}
이를 악용하려면 객체가 해제된 후 UAF가 트리거되기 전에 재할당이 이루어져야 합니다. tee_shm_get_from_id() 호출 후, tee_shm_put() 함수가 호출됩니다 (이 함수에서 syzkaller의 두 번째 UAF 크래시가 발생합니다). 이 함수는 dma_buf_put()에 입력 인수로 사용된 tee_shm:dmabuf 객체를 역참조합니다.```
/**
`tee_shm` 객체는 kmalloc-64 캐시에 속하므로 UAF 이전에 재할당될 수 있습니다. 다음과 같이 재할당해야 합니다:
1. 가짜 `tee_shm`, `tee_shm:dmabuf`, `dma_buf:file` 객체
2. `file->f_count = 1` 설정
3. `fasync` 함수 포인터가 임의의 주소로 설정된 `file:file_operations` 객체 생성
이 함수는 `file->f_count`가 0에 도달한 후 `dma_buf_put()` 호출 이후 `__fput()`에서 호출됩니다.
PAN(Privileged Access Never)은 `file:f_ops` 구조체에 임의의 함수 포인터를 설정하려면 가짜 객체가 사용자 공간 메모리에 참조되어야 하므로 이를 완화합니다. 따라서 이 기능이 작동하려면 `CONFIG_CPU_SW_DOMAIN_PAN`이 비활성화되어야 하며, 제공된 환경에서는 비활성화되어 있습니다. 이 취약점에서 PAN을 우회할 수 있는지(예: ret2dir 사용)에 대한 몇 가지 미해결 질문이 남아 있습니다.
또한, 해제된 shm 객체를 성공적으로 재할당하려면 IOCTL 호출 `TEE_IOC_OPEN_SESSION` 또는 `TEE_IOC_INVOKE`가 파일 디스크립터를 닫는 스레드와 kmalloc-64 캐시를 채우는 힙 스프레이 스레드에 의해 선점되어야 합니다. 이를 위해 커널은 `CONFIG_PREEMPT`로 구성되어야 합니다. 이 PoC에서는 Nicolas Fabretti의 블로그 게시물 [7]의 힙 스프레이를 사용했으며, 이는 차단 `sendmsg()`를 기반으로 합니다.
요약하자면, 악용과 관련된 문제는 해제와 UAF가 동일한 시스템 호출 내에서 발생해야 한다는 것입니다. 또한 해제를 트리거하기 어려운데, 이는 시스템 호출 내에서 경쟁 조건이 필요하기 때문입니다. 해제 후 실제 UAF 사이의 시간은 작은 시간 창이며, 이 기간 동안 해제된 객체를 재할당하기 위해 힙 스프레이를 수행해야 합니다. 다음 그림은 악용 코드에 관련된 스레드와 그 역할을 보여줍니다.
<p align="center">
<img src="https://raw.githubusercontent.com/pjlantz/pjlantz.github.io/master/docs/assets/Threads.png?raw=true" alt="관련 스레드" width="50%" height="50%"/>
<br /><em>그림 3: 악용 코드에 관련된 스레드</em>
</p>
세 가지 유형의 스레드가 연속적으로 실행됩니다. 시스템 호출 스레드를 선점하기 위해 이 스레드는 가능한 가장 낮은 우선순위인 `SCHED_IDLE`로 실행되는 반면, 다른 스레드는 `SCHED_OTHER` 우선순위로 설정됩니다. 차단 `sendmsg()`를 사용하기 때문에 각 스프레이 시도는 자체 스레드에서 실행되어야 하며, 각 코어가 자체 kmalloc 캐시를 유지하므로 UAF를 트리거하는 동일한 CPU 코어에서 실행되어야 합니다. 또한 1b) 단계에서 공유 메모리 할당에서 파일 디스크립터를 닫는 여러 해제 스레드가 있습니다. 이 UAF 트리거 및 함수 포인터 덮어쓰기의 전체 소스 코드는 [10]에서 찾을 수 있습니다.
## 새 환경 설정
취약한 커널과 OPTEE가 포함된 환경을 재현하려면 다음 저장소에서 복제하고 다음을 사용하여 빌드할 수 있습니다:```
$ mkdir optee-qemu && cd optee-qemu
$ repo init -u https://github.com/pjlantz/optee-qemu.git
$ repo sync
$ cd build
$ make toolchains -j2
$ make run
빌드 성공 후, 세 개의 콘솔이 생성됩니다. 하나는 QEMU용이며, 부팅하려면 QEMU 콘솔에서 'c'를 누르십시오. 두 번째 콘솔은 보안 세계(secure world)의 출력을 보여주고, 마지막 콘솔은 Linux로 부팅됩니다. root로 로그인합니다(비밀번호 없음).
file_operations 구조체의 fasync 함수 포인터가 0x22000000으로 설정될 때까지 익스플로잇 코드를 실행합니다.```
until optee_exploit | grep "0x22000000" /var/log/messages; do sleep 0.01; done
이는 특권 실행-중단(PXN)이 `PC=0x22000000`에서 실행을 차단하면서 중단됩니다. 이후 커널 버전에 따라 익스플로잇 전략은 다양할 수 있지만, 커널 ROP를 실행하여 스택 피벗을 수행하거나 vDSO 영역을 쓰기 가능하게 만들어 페이로드를 그곳에 배치할 수 있습니다. 또한 ret2dir 및 일부 physmap 스프레이를 사용하여 PAN을 우회할 수 있는지 조사하는 것은 향후 연구에 흥미로울 수 있습니다. PAN은 `linux/.config`에서 `CONFIG_CPU_SW_DOMAIN_PAN=y`를 설정하여 커널에서 활성화할 수 있습니다. 실제 하드웨어에서는 ARMv8.1 및 AArch64에서 기본적으로 활성화되어 있으며, ARMv7 및 AArch32의 경우 이 설정[8]을 사용하여 소프트웨어 에뮬레이션 PAN이 가능합니다.
**참고**: 이 익스플로잇은 매우 잘 최적화되어 있지 않으며, 공유 메모리 객체를 너무 일찍 해제할 경우 드라이버가 가끔 정지될 수 있습니다. 이 경우 PC는 `tee_shm_get_from_id()`에 위치하게 됩니다. 이런 경우 QEMU 콘솔에서 `system_reset`을 실행하여 환경을 재부팅하세요.
## 감사의 말
근본 원인 분석에 도움을 주신 Axis Communications의 Lars Persson, 원활한 커뮤니케이션과 이 문제[9]의 신속한 해결을 위해 노력해 주신 Linaro의 TEE 서브시스템 관리자 Jens Wiklander에게 감사드립니다.
## 참고 자료
[1] CVE-2021-44733 - https://nvd.nist.gov/vuln/detail/CVE-2021-44733
[2] TEE 서브시스템 - https://www.kernel.org/doc/html/latest/staging/tee.html
[3] Globalplatform TEE API - https://globalplatform.org/specs-library/?filter-committee=tee
[4] OP-TEE OS - https://github.com/OP-TEE/optee_os
[5] BKK16-110: 신뢰할 수 있는 실행과 OP-TEE에 대한 쉬운 소개 - https://connect.linaro.org/resources/bkk16/bkk16-110/
[6] Syzkaller - https://github.com/google/syzkaller
[7] Lexfo의 보안 블로그, 작성자: Nicolas Fabretti: CVE-2017-11176: 단계별 Linux 커널 익스플로잇 - https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html
[8] Linux 커널 보안 서브시스템: 익스플로잇 방법/사용자 공간 데이터 사용 - http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage
[9] [PATCH v2] tee: 참조 카운트가 0인 shm 조회 처리 - https://lore.kernel.org/lkml/[email protected]/T/
[10] 개념 증명 익스플로잇 - https://github.com/pjlantz/optee_examples/tree/master/exploit/host