
desc_race は、iOS 15.0〜15.1.1向けのエクスプロイト(安定したカーネル読み書きプリミティブを備える)(CVE-2021-30955)
「desc_race」(CVE-2021-30955) は、iOS 15.0〜15.1.1 向けのエクスプロイトです(安定したカーネル r/w プリミティブを備えています)。
IOSurfaceClient 配列の容量を 0x2000 に増やします。目的は、内容が完全に制御されたポインタを書き込み、その後 IOSurfaceRootUserClient インターフェースを使用してカーネル r/w を実現することです。配列のサイズは 0x2000 * 8 バイトであり、KHEAP_DEFAULT と同じ KHEAP_KEXT の large map に配置されます。
次に、0x4000 バイトの ool ディスクリプタを持つアシスタント kmsg を使用して 0x4000 バイトのカーネルバッファを割り当てます。これは後方から上書きされます。そして、割り当てられたメッセージ(double copyin kmsg と呼ばれる)でバグをトリガーし、アシスタントカーネルバッファの直後に配置します。このメッセージには、カウントが 0x2000 の ool ports ディスクリプタが含まれており、IOSurfaceClient 配列にかなり近い位置に配置されます。
次に、アシスタントメッセージを受信します。レースが成功すると、ool ports ディスクリプタが開示され、IOSurfaceClient 配列のアドレスを特定できます。
次に、再度 0x4000 バイトのカーネルバッファを割り当てます。これにより、前述のカーネルバッファが占有されます。今回は、適切なボディを持つ偽の kmsg ヘッダを構築します。その後、double copyin kmsg を破棄すると、カーネルは偽のヘッダから処理を開始します。偽の mach_msg_ool_descriotpor_t を使用して、完全に制御されたコピーで vm_copy_discard() をトリガーします。コピーの破棄中、最も価値のある行は _vm_map_entry_unlink_ll にあります:
1
2
3
4
5
6
#define _vm_map_entry_unlink_ll(hdr, entry) \
MACRO_BEGIN \
(hdr)->nentries--; \
(entry)->vme_next->vme_prev = (entry)->vme_prev; \
(entry)->vme_prev->vme_next = (entry)->vme_next; \
MACRO_END
entry は私たちの制御下にあり、これにより完璧な r/w プリミティブが得られます。これを使用して IOSurfaceClient 配列に制御されたポインタを書き込み、IOSurfaceRootUserClient インターフェースと組み合わせてカーネル r/w を実現します。
このエクスプロイトの開発中、bazad の投稿「One byte to rule them all」を何度も読み返し、彼のエクスプロイトと同じ手法(vm_copy_t の偽装)を使用しました。ただし、いくつかの点が異なります。
XNU はメッセージに署名するため、破損した kmsg を受信できなくなりました。
破棄手順では、vm_object の mapping_in_progress を設定してカーネルをスピンさせ、ゾーンチェックによるパニックを防ぎます。
素晴らしい投稿をしてくれた bazad、IOSurfaceClient インターフェースが r/w プリミティブに使用できなくなったことを教えてくれた WangTielei、そして親切にしてくれた pedantcoder に多大な感謝を捧げます。