
CVE-2016-6187 向けカーネルエクスプロイト (ローカル権限昇格)
CVE-2016-6187 のカーネルエクスプロイト (ローカル特権昇格) (影響を受けるバージョン: < 4.6.5)
エクスプロイトはカスタムコンパイルされた 4.6 カーネル + buildroot に対して開発されました (kconfig は近日公開予定)。KASLR、SMEP、SMAP を回避します (KPTI は 4.6 では利用不可)
悪用される脆弱性は、AppArmor の LSM フック (setprocattr) の1つにおける、ヒープベースの単一ヌルバイトオーバーフローです。詳細: https://nvd.nist.gov/vuln/detail/CVE-2016-6187
初期の測定では、ルートシェルを起動できる確率は94.44%です。18回に1回の割合で、エクスプロイトがカーネルを oops させます。ルートアクセス獲得後にカーネルを完全に安定させるには、さらなる作業が必要です。
ヌルバイトオーバーフローを悪用して kmalloc-128 のフリーリストポインタの LSB を破壊し、既に割り当て済みのオブジェクトを指すようにします。その後、その割り当て済みオブジェクトを解放することで、ダブルフリーの状況を作り出します。
これで、kmalloc-128 キャッシュから重複する2つの構造体を割り当てることができます。最初の構造体は、ユーザースペースが読み戻せる内容を持つオブジェクトです。そのために、ip6_sf_socklist を選択しました。2番目の構造体は、ip6_sf_socklist の上に割り当てられ、カーネルポインタを含む必要があります。rfkill_data が適しています。これにはヒープポインタ (空の rfkill_data.events リスト) と、rfkill_fds へのグローバルデータポインタ (rfkill_data.list.prev) が含まれます。rfkill_data が ip6_sf_socklist の上に割り当てられると、getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen) を呼び出してカーネルポインタを取得できます。その後、カーネルテキストベースを rfkill_fds から導出できます。
次に、2回目のダブルフリー状況を引き起こしますが、今回はメッセージキューのリンクプロセスを悪用してフリーリストポインタを上書きし、実際には kmalloc-128 のチャンクを要求しているのに、アロケータが kmalloc-96 のチャンクを返すように騙します。
このサイズの不一致を利用して、kmalloc-96 チャンクのフリーリストポインタにオーバーフローさせ、任意のアドレスを割り当てるプリミティブを手に入れます。今回の場合、ptmx_fops 構造体をターゲットにし、ptmx_fops.unlocked_ioctl コールバックをスタックピボット用ガジェットへのポインタで上書きします。
/dev/ptmx に対して ioctl を呼び出すとピボットガジェットがトリガーされ、スタックを kmalloc-128 内の偽のスタックにピボットします。そこには既に第1段階の ROP チェーンを準備しています。
kmalloc-128 の偽スタックには限られたスペースしかないため (次のチャンクが同じ slab にあるかどうか不明)、第1段階の ROP チェーンの役割は、第2段階の ROP チェーンをユーザースペースからカーネルの dmesg ログバッファ (__log_buf) にコピーし、スタックを __log_buf にピボットすることです。
第2段階の ROP チェーンは、commit_creds(prepare_kernel_cred(NULL)) を呼び出して特権を昇格させる前に、カーネルを修復 (ptmx_fops の復元など) しようとします。
そして最後に iretq を介してカーネルスペースを終了し、昇格した特権で execve("/bin/sh", 0, 0) サブルーチンに戻ります。
非特権ユーザーは少なくとも /dev/rfkill を開ける必要があります。一部のディストリビューションでは許可されない可能性があります。Ubuntu と Fedora を調べましたが、両方とも非特権ユーザーが /dev/rfkill を開くことを許可しています (ただし Fedora は AppArmor ではなく SELinux を使用しています)。
以下のセクションでは、qemu-x86_64 でローカル特権昇格を再現する方法を説明します。
まず、initrd と bzImage カーネルを入手する必要があります。2つの方法があります。
最も簡単で迅速な方法です。必要なコンポーネントはすべて prebuilt/ にあります。
または、自分でコンポーネントをビルドすることもできます。4.6 カーネル を取得し、config/ に提供されている x86_64_vuln_kern_defconfig を使用して bzImage をコンパイルしてください。
initrd については、例えば buildroot を使用できます。init で /dev/rfkill の正しいパーミッションを設定することを忘れないでください。
chmod 664 /dev/rfkill
これは Ubuntu-16.04 や他の多くのディストリビューションと同じです。また、エクスプロイトをコンパイルして initrd に配置することを忘れないでください。
make
コンポーネントを入手したら、脆弱なマシンを起動します
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
そしてエクスプロイトを実行します
./exploit
すでにゼロだった LSB をヌルアウトしたかどうかに応じて、エクスプロイトを数回実行する必要があるかもしれません。また、ルートアクセス獲得後もわずかに (おそらく10%~20%?) クラッシュする可能性があることに注意してください。改善に取り組んでいます。
お楽しみください。
ptmx_fops を修復して安定性を向上kmalloc-128 フリーリストを修復して安定性を向上