
Android Binder の UAF (Use-After-Free) に対する概念実証 (PoC) ローカル権限昇格 (LPE) エクスプロイト。iovec スプレーと addr_limit 上書きを使用して、任意のカーネル読み書きを実現します。
書き直された Proof-of-Concept / ローカル権限昇格 (LPE) エクスプロイトで、Android Binder ドライバーにおける Use-After-Free の脆弱性 CVE-2019-2215 を標的としています。
脆弱なカーネルビルドを vulnerable_kernel_builds フォルダに用意しました。Android 10 エミュレータ AOSP を作成し、それを使ってカーネルを実行してください。
emulator -show-kernel -no-window -no-snapshot -wipe-data -avd research -kernel bzImage
このバグの技術的詳細については、この素晴らしいブログから学ぶことができます: https://projectzero.google/2019/11/bad-binder-android-in-wild-exploit.html
task_struct 構造体には、mm_segment_t 型の重要なメンバー addr_limit があります。addr_limit は、有効なユーザー空間アドレスの最大値を格納します。addr_limit は、ターゲットアーキテクチャに応じて struct thread_info または struct thread_struct の一部です。今回は x86_64 ビットシステムを扱っているため、addr_limit は struct thread_struct 内に定義されています。

この addr_limit を 0xFFFFFFFFFFFFFFFF で破壊できれば、カーネル空間メモリの任意の部分に対して読み書きができるようになります。x86_64 と arm64 でのエクスプロイトの互換性を高めるには、addr_limit を 0xFFFFFFFFFFFFFFFE に設定する方が良いでしょう。
struct iovec は、Scatter/Gather I/O としても知られる Vectored I/O に使用されます。struct iovec の主な問題の1つは、寿命が短いことです。システムコールがバッファを処理する際に割り当てられ、ユーザーモードに戻るとすぐに解放されます。
unlink 操作をトリガーして iov_base ポインタを binder_thread->wait.head のアドレスで上書きし、スコープ付きの読み書きを獲得する際には、iovec 構造体をカーネル内に留めておきたいところです。1つの方法は、pipe ファイルディスクリプタに対して readv や writev などのシステムコールを使用することです。pipe が満杯または空の場合、これらのシステムコールはブロックされる可能性があります。pipe はプロセス間通信に使用できる単方向データチャネルです。pipe のブロッキング機能により、カーネル空間で iovec 構造体を破壊するための十分な時間的ウィンドウが得られます。
同様に、recvmsg システムコールを使用し、フラグパラメータとして MSG_WAITALL を渡すことでブロックさせることができます。
binder_thread 構造体のサイズは 408 バイトなので、kmalloc-512 キャッシュに配置されます。

ダングリングチャンクを再割り当てするには、25 個の iovec 構造体を積み重ねる必要があります。408 / 16 = 25.5

上の画像からわかるように、iovecStack[10].iov_len と iovecStack[11].iov_base が破壊されます。

そこで、iovecStack[10] を処理し、writev システムコールをブロックしてから unlink 操作をトリガーします。これにより、iovecStack[11].iov_base が破壊された時点で writev システムコールを再開できます。そして最後に、binder_thread チャンクの内容をユーザー空間にリークし、そこから task_struct ポインタを読み取ります。

スコープ付きの write を達成するために、フラグパラメータとして MSG_WAITALL を渡すことでブロックする recvmsg システムコールを使用します。recvmsg システムコールは writev システムコールと同様にブロックできます。

mm_segment_t のサイズは 0x8 バイトなので、0xFFFFFFFFFFFFFFFE で破壊します。これは有効なカーネル空間アドレスの最大値であり、arm64 システムでページフォルトが発生してもプロセスがクラッシュしません。

