
TEE ड्राइवर (CVE-2021-44733) के शोषण के लिए कमजोर कर्नेल वाला वातावरण
हाल ही में Linux कर्नेल TEE सबसिस्टम में, संस्करण 5.15.11 तक, एक use-after-free भेद्यता खोजी गई थी, और इसे CVE-2021-44733 [1] निर्दिष्ट किया गया था।
पहली नज़र में यह कई कारणों से शोषण योग्य नहीं लग रहा था, हालाँकि भेद्य कोड पथ के आगे के विश्लेषण और एक मोटा proof-of-concept शोषण लागू करने के बाद कर्नेल में एक फ़ंक्शन पॉइंटर को अधिलेखित करना संभव था। इस पोस्ट में कोई विशेषाधिकार वृद्धि पेलोड प्रस्तुत नहीं किया गया है, हालाँकि OPTEE और शोषण को चलाने के लिए पूरा वातावरण आगे के परीक्षण के लिए उपलब्ध है, 'वातावरण सेट करना' देखें।
TEE (Trusted Execution Environment) कुछ सुरक्षित वातावरण में चलने वाला एक विश्वसनीय OS है, उदाहरण के लिए, ARM CPU पर TrustZone। एक TEE ड्राइवर TEE के साथ संचार के लिए आवश्यक विवरणों को संभालता है। ड्राइवर के कुछ महत्वपूर्ण कर्तव्य Globalplatform TEE Client API विनिर्देश [3] के आधार पर TEE की ओर एक सामान्य API प्रदान करना है, लेकिन Linux और TEE के बीच साझा मेमोरी का प्रबंधन करना भी है। इस सबसिस्टम को ARM आर्किटेक्चर के लिए कर्नेल कॉन्फ़िगरेशन में CONFIG_OPTEE कॉन्फ़िगर करके सक्षम किया जा सकता है।
सुरक्षित दुनिया में OP-TEE OS [4] नामक विश्वसनीय OS होता है। इस OS के ऊपर तथाकथित Trusted Applications (TAs) चलाना संभव है, जो पृथक वातावरण में कुछ संचालन कर सकते हैं, चित्र 1 देखें।
चित्र 1: TEE का अवलोकन - Linaro की प्रस्तुति से [5]
सामान्य दुनिया (Linux userspace/kernel) इन अनुप्रयोगों के साथ क्लाइंट अनुप्रयोगों (CAs) और TEE सबसिस्टम द्वारा प्रदान किए गए API का उपयोग करके बातचीत कर सकती है। एक CA किसी विशिष्ट TA की ओर एक सत्र खोल सकता है और उन फ़ंक्शनों को लागू कर सकता है जिन्हें TA लागू करता है। TA और CA के बीच किसी भी तर्क को आगे-पीछे पास करना साझा मेमोरी का उपयोग करके किया जाता है। सभी प्रासंगिक syscalls का उपयोग करके CA और TA के बीच की अंतःक्रिया आगे वर्णित है।
एक CA ड्राइवर के साथ संचार करने के लिए /dev/tee[0-9] खोलता है। ध्यान दें, कि इन APIs का उपयोग करने के पारंपरिक तरीके के लिए, यह 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) }
फ़ज़िंग के दौरान, जिस क्रैश ने ध्यान खींचा, वह mutex धारण किए जाने के समय 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
==================================================================
यह TEE_IOC_SHM_ALLOC से सभी फ़ाइल डिस्क्रिप्टर बंद करने से ट्रिगर हुआ, जबकि एक अलग थ्रेड हमारे मामले में एक गैर-मौजूद TA की ओर सत्र खोल रहा था। 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 के साथ कोई सत्र (session) स्थापित किए बिना TEE ड्राइवर को फ़ज़िंग करके खोजी गई थी। इसे आगे syzkaller में तथाकथित छद्म सिस्कॉल (pseudo syscalls) का उपयोग करके बढ़ाया जा सकता है, ताकि किसी TA की ओर एक सत्र सेटअप और आरंभ किया जा सके।
## मूल कारण विश्लेषण
निष्कर्ष यह है कि `tee_shm:dmabuf` ऑब्जेक्ट के जीवनकाल पर नज़र रखने (lifetime tracking) के साथ एक डिज़ाइन समस्या है। ड्राइवर को इस तरह डिज़ाइन किया गया है कि वह `tee_ioctl_shm_alloc()` कॉल के बाद userspace को एकमात्र संदर्भ गणना (reference count) रखने देता है।
यह मान लिया जाता है कि यदि ऑब्जेक्ट अभी भी ड्राइवर के IDR ऑब्जेक्ट में मौजूद है, तो dmabuf का संदर्भ अभी भी मान्य है और उसकी संदर्भ गणना बढ़ाई जा सकती है। लेकिन यह केवल आंशिक रूप से सत्य है। dmabuf मेमोरी अभी भी dmabuf ड्राइवर के स्वामित्व में होती है, लेकिन वह नष्ट होने की प्रक्रिया में हो सकती है और संदर्भ गणना को फिर से गैर-शून्य करके उसे रोका नहीं जा सकता।
समस्या को ट्रिगर करने वाला परिदृश्य एक मल्टी-थ्रेडेड एप्लिकेशन है, जहाँ एक थ्रेड dmabuf फ़ाइल-डिस्क्रिप्टर को बंद करता है, उसी समय दूसरा थ्रेड उस साझा मेमोरी को संदर्भित करते हुए IOCTL कमांड `TEE_IOC_OPEN_SESSION` या `TEE_IOC_INVOKE` पर कॉल करता है।
जब user-space fd को बंद करता है तो dmabuf के विनाश का पता लगाने पर कर्नेल में यह कोड चलेगा:
1. `fput()`
2. `fput_many()` >> फ़ाइल संदर्भ गणना शून्य हो जाती है। रेस विंडो खुल जाती है।
3. `[task_work gets scheduled]`
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 ऑब्जेक्ट को userspace से संदर्भित नहीं किया जा सकता। रेस विंडो बंद हो जाती है।
10. `mutex_unlock()`
इसका मतलब है कि IDR तालिका और उसका mutex लॉक यह गारंटी नहीं दे सकते कि 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 ऑब्जेक्ट को डीरेफरेंस करता है।```
/**
The `tee_shm` ऑब्जेक्ट को UAF से पहले पुनः आवंटित (reallocated) किया जा सकता है क्योंकि यह kmalloc-64 कैश से संबंधित है। इसे निम्नलिखित के साथ पुनः आवंटित करना होगा:
1. नकली `tee_shm`, `tee_shm:dmabuf`, `dma_buf:file` ऑब्जेक्ट
2. `file->f_count = 1` सेट करें
3. एक `file:file_operations` ऑब्जेक्ट तैयार करें जिसमें `fasync` फ़ंक्शन पॉइंटर किसी मनमाने पते पर सेट हो
यह फ़ंक्शन तब `__fput()` में आहूत होता है, जब `file->f_count` शून्य तक पहुँचता है, `dma_buf_put()` के कॉल के बाद।
PAN (Privileged Access Never) इसका शमन करता है क्योंकि `file:f_ops` संरचना में एक मनमाना फ़ंक्शन पॉइंटर सेट करने के लिए नकली ऑब्जेक्ट्स को userspace मेमोरी में संदर्भित किया जाना चाहिए। इसलिए इसके काम करने के लिए `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()` पर आधारित है।
संक्षेप में, शोषण के संदर्भ में मुद्दा यह है कि free और UAF दोनों को एक ही सिस्टम कॉल के भीतर होना चाहिए। इसके अलावा, freeing को ट्रिगर करना कठिन है क्योंकि इसके लिए syscall के भीतर रेसिंग की आवश्यकता होती है। Freeing के बाद, उसके और वास्तविक UAF के बीच का समय एक छोटी समयावधि है जिसमें मुक्त किए गए ऑब्जेक्ट को पुनः आवंटित करने के लिए हीप स्प्रे किया जाना चाहिए। निम्नलिखित चित्र exploit कोड में शामिल थ्रेड्स और उनकी भूमिका दिखाता है।
<p align="center">
<img src="https://raw.githubusercontent.com/pjlantz/pjlantz.github.io/master/docs/assets/Threads.png?raw=true" alt="Threads involved" width="50%" height="50%"/>
<br /><em>चित्र 3: एक्सप्लॉइट कोड में शामिल थ्रेड्स</em>
</p>
तीन प्रकार के थ्रेड लगातार चल रहे हैं। सिस्टम कॉल करने वाले थ्रेड को प्रीमेप्ट करने के लिए, वह सबसे कम संभव प्राथमिकता, `SCHED_IDLE` पर चल रहा है, जबकि अन्य की प्राथमिकता `SCHED_OTHER` पर सेट है। चूँकि हम ब्लॉकिंग `sendmsg()` का उपयोग कर रहे हैं, प्रत्येक स्प्रे प्रयास को अपने स्वयं के थ्रेड में चलना चाहिए और उसे उसी CPU कोर पर चलना चाहिए जो UAF को ट्रिगर करता है, क्योंकि प्रत्येक कोर अपने स्वयं के kmalloc कैश रखता है। साथ ही, कुछ freeing थ्रेड्स हैं जो चरण 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' दबाएं। दूसरा कंसोल सुरक्षित दुनिया से आउटपुट दिखाता है और अंतिम वाला Linux में बूट होगा। root के रूप में लॉगिन करें (कोई पासवर्ड नहीं)।
एक्सप्लॉइट कोड तब तक चलाएं जब तक file_operations संरचना का fasync फ़ंक्शन पॉइंटर 0x22000000 पर सेट न हो जाए।```
until optee_exploit | grep "0x22000000" /var/log/messages; do sleep 0.01; done
यह Privileged execute-never (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 का धन्यवाद, और TEE उपप्रणाली के अनुरक्षक, Linaro में Jens Wiklander का धन्यवाद, इस मुद्दे के सुचारू संचार और त्वरित समाधान के लिए [9]।
## संदर्भ
[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: Trusted Execution और 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: handle lookup of shm with reference count 0 - https://lore.kernel.org/lkml/[email protected]/T/
[10] अवधारणा प्रमाण शोषण - https://github.com/pjlantz/optee_examples/tree/master/exploit/host