
بيئة بنواة معرضة للثغرات لاستغلال سائق TEE (CVE-2021-44733)
مؤخرًا، تم اكتشاف ثغرة استخدام بعد التحرير (use-after-free) في نظام TEE الفرعي في نواة لينكس، حتى الإصدار 5.15.11 وما يشملها، وقد تم تخصيص CVE-2021-44733 [1] لها.
للوهلة الأولى، لم تكن تبدو قابلة للاستغلال لعدة أسباب، لكن بعد مزيد من التحليل لمسار الكود القابل للثغرة ومن خلال تنفيذ استغلال إثبات مفهوم مبدئي، كان من الممكن الكتابة فوق مؤشر دالة في النواة. لا يتم تقديم حمولة رفع صلاحيات في هذه المقالة، ومع ذلك، فإن البيئة الكاملة لتشغيل OPTEE والاستغلال متاحة لمزيد من الاختبار، انظر «إعداد البيئة».
بيئة التنفيذ الموثوقة (TEE) هي نظام تشغيل موثوق يعمل في بيئة آمنة معينة، على سبيل المثال TrustZone على معالجات ARM. يتولى برنامج تشغيل TEE التفاصيل اللازمة للتواصل مع TEE. من بين الواجبات الأكثر أهمية لبرنامج التشغيل توفير واجهة برمجة تطبيقات عامة نحو TEE استنادًا إلى مواصفات Globalplatform TEE Client API [3]، وكذلك إدارة الذاكرة المشتركة بين لينكس وTEE. يمكن تمكين هذا النظام الفرعي عن طريق تكوين CONFIG_OPTEE في تكوينات النواة لمعماريات ARM.
يحتوي العالم الآمن على نظام التشغيل الموثوق المسمى OP-TEE OS [4]. وفوق هذا النظام، من الممكن تشغيل ما يسمى بالتطبيقات الموثوقة (TAs) التي يمكنها تنفيذ بعض العمليات في البيئة المعزولة، انظر الشكل 1.
الشكل 1: نظرة عامة على TEE - من عرض Linaro التقديمي [5]
يمكن للعالم العادي (فضاء المستخدم/النواة في لينكس) التفاعل مع هذه التطبيقات باستخدام تطبيقات العميل (CAs) وواجهة البرمجة التي يكشفها نظام TEE الفرعي. يمكن لتطبيق CA فتح جلسة نحو TA محددة واستدعاء الدوال التي تنفّذها TA. يتم تمرير أي وسائط ذهابًا وإيابًا بين TA وCA باستخدام الذاكرة المشتركة. يتم وصف التفاعل بين CA وTA باستخدام جميع استدعاءات النظام ذات الصلة فيما يلي.
يفتح تطبيق CA /dev/tee[0-9] للتواصل مع برنامج التشغيل. لاحظ أنه بالنسبة للطريقة التقليدية لاستخدام هذه الواجهات البرمجية، يتم ذلك ضمنيًا باستخدام libteec.
يمكن لتطبيق CA تسجيل الذاكرة المشتركة باستخدام IOCTL TEE_IOC_SHM_ALLOC. يخصص هذا الذاكرة المشتركة ويعيد واصف ملف يمكن لفضاء المستخدم استخدامه كجزء من mmap.
الخطوة التالية هي إنشاء جلسة باستخدام IOCTL TEE_IOC_OPEN_SESSION وتحديد uuid لـ TA محددة. هذا 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 (المشرفين) وليس في النواة الرسمية (upstream). البيئة المقدمة في «إعداد البيئة» يمكن استخدامها للتنقيب إذا تم تكوينها بشكل صحيح وفقًا لتوثيق 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) }
أثناء fuzzing، كان الانهيار الذي لفت الانتباه مرتبطًا باستخدام بعد التحرير لكائن task_struct بينما كان mutex ممسوكًا:```
==================================================================
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 قليلاً، حدث UAF مختلف لكائن ينتمي إلى مخبأ kmalloc-64:```
================================================================== 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 ==================================================================
تم اكتشاف هذه الثغرة عن طريق اختبار التشويش (fuzzing) على برنامج تشغيل TEE دون إنشاء أي جلسة مع تطبيق TA موثوق قيد التشغيل على النظام. ويمكن توسيع ذلك بشكل أكبر باستخدام ما يُسمى بالاستدعاءات الزائفة (pseudo syscalls) في syzkaller من أجل إعداد وبدء جلسة تجاه أحد تطبيقات TA.
## تحليل السبب الجذري
الخلاصة هي وجود مشكلة تصميمية في تتبّع عمر كائن `tee_shm:dmabuf`. صُمّم برنامج التشغيل ليسمح لمساحة المستخدم (userspace) بالاحتفاظ بعدّاد المرجع الوحيد والأخير بعد استدعاء `tee_ioctl_shm_alloc()`.
من المفترض أنه إذا كان الكائن لا يزال موجودًا في كائن IDR الخاص ببرنامج التشغيل، فإن المرجع إلى dmabuf لا يزال صالحًا ويمكن زيادة عدّاد مرجعه. ولكن اتضح أن هذا صحيح جزئيًا فقط. فذاكرة dmabuf لا تزال مملوكة لبرنامج تشغيل dmabuf، ولكنها قد تكون في طور التدمير ولا يمكن إيقاف ذلك عن طريق جعل عدّاد المرجع غير صفري مرة أخرى.
السيناريو الذي يسبّب المشكلة هو تطبيق متعدد الخيوط حيث يُغلق أحد الخيوط واصف ملف dmabuf في نفس الوقت الذي يقوم فيه خيط آخر باستدعاء أمر IOCTL `TEE_IOC_OPEN_SESSION` أو `TEE_IOC_INVOKE` بالإشارة إلى تلك الذاكرة المشتركة.
عند تتبع تدمير dmabuf عندما تُغلق مساحة المستخدم الواصف fd، سيتم تنفيذ هذا الكود في النواة:
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 من مساحة المستخدم. تُغلق نافذة السباق.
10. `mutex_unlock()`
هذا يعني أن جدول IDR وقفل mutex الخاص به لا يمكن أن يضمنا أن dmabuf وكائن `tee_shm` المقابل لا يزالان على قيد الحياة. فالعملية التي تنافس `fput()` عن طريق استدعاء `tee_shm_get_from_id()` يمكنها الحصول على مرجع إلى 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() (التي يحدث عندها انهيار UAF الثاني من syzkaller) والتي تقوم بإلغاء مرجعية الكائن tee_shm:dmabuf المستخدم كوسيطة إدخال إلى dma_buf_put().```
/**
يمكن إعادة تخصيص كائن `tee_shm` قبل حدوث UAF لأنه ينتمي إلى ذاكرة التخزين المؤقت kmalloc-64. ويجب إعادة تخصيصه باستخدام:
1. كائنات مزيفة `tee_shm` و`tee_shm:dmabuf` و`dma_buf:file`
2. اضبط `file->f_count = 1`
3. صمّم كائن `file:file_operations` يحتوي على مؤشر الدالة `fasync` مضبوطًا على عنوان عشوائي
ثم يتم استدعاء هذه الدالة في `__fput()` بعد استدعاء `dma_buf_put()` عندما يصل `file->f_count` إلى الصفر.
يعمل 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()` الحاجب (blocking).
باختصار، تكمن المشكلة المتعلقة بالاستغلال في أن كلاً من التحرير وUAF يجب أن يحدثا داخل نفس استدعاء النظام. إضافة إلى ذلك، من الصعب تحفيز التحرير لأنه يتطلب سباقًا (racing) داخل استدعاء النظام. بعد التحرير، يكون الوقت بينه وبين 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()` الحاجب، يجب أن تعمل كل محاولة رشّ في خيط خاص بها ويجب أن تعمل على نفس نواة المعالج (CPU core) التي تُطلق UAF، لأن كل نواة تحتفظ بذاكرات التخزين المؤقت kmalloc الخاصة بها. هناك أيضًا عدد من خيوط التحرير التي تغلق واصف الملف من تخصيص الذاكرة المشتركة في الخطوة 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 - اضغط 'c' في وحدة تحكم QEMU للتمهيد. وحدة التحكم الثانية تعرض مخرجات العالم الآمن، والأخيرة ستمهّد إلى Linux. سجّل الدخول كجذر (بدون كلمة مرور).
شغّل كود الاستغلال حتى يتم تعيين مؤشر الدالة fasync في بنية file_operations إلى 0x22000000.```
until optee_exploit | grep "0x22000000" /var/log/messages; do sleep 0.01; done
سيتوقف هذا بسبب قيام Privileged execute-never (PXN) بحظر التنفيذ عند `PC=0x22000000`. من هنا، يمكن أن تختلف استراتيجيات الاستغلال اعتمادًا على إصدار النواة، لكن قد يكون من الممكن تنفيذ ROP على مستوى النواة والقيام بـ stack pivoting، أو جعل منطقة vDSO قابلة للكتابة ووضع الحمولة هناك. قد يكون من المثير للاهتمام أيضًا كعمل مستقبلي التحقق مما إذا كان يمكن تجاوز PAN باستخدام ret2dir وبعض تقنيات physmap spraying. يمكن تفعيل PAN في النواة عن طريق تعيين `CONFIG_CPU_SW_DOMAIN_PAN=y` في `linux/.config`. على الأجهزة الحقيقية، يكون مفعّلًا افتراضيًا على ARMv8.1 وAArch64، وبالنسبة إلى ARMv7 وAArch32 يمكن الحصول على PAN محاكى برمجيًا باستخدام هذا الإعداد [8].
**ملاحظة**: هذا الاستغلال ليس محسّنًا بشكل جيد وقد يؤدي أحيانًا إلى تعليق برنامج التشغيل إذا تمكن من تحرير كائن الذاكرة المشتركة مبكرًا جدًا، وفي هذه الحالة ستكون PC عند `tee_shm_get_from_id()`. إذا حدث ذلك، نفّذ `system_reset` في وحدة تحكم QEMU لإعادة تشغيل البيئة.
## شكر وتقدير
نشكر Lars Persson من Axis Communications لمساعدته في تحليل السبب الجذري، وJens Wiklander من Linaro ومشرف نظام TEE الفرعي على التواصل السلس والحل السريع لهذه المشكلة [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] واجهة برمجة تطبيقات TEE من Globalplatform - 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: استغلال نواة لينكس خطوة بخطوة - https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html
[8] النظام الفرعي لأمن نواة لينكس: طرق الاستغلال/استخدام بيانات مساحة المستخدم - http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage
[9] [PATCH v2] tee: معالجة البحث عن shm بعدد مراجع 0 - https://lore.kernel.org/lkml/[email protected]/T/
[10] استغلال إثبات المفهوم - https://github.com/pjlantz/optee_examples/tree/master/exploit/host