37年前のOpenBSDカーネルバグでroot権限取得
原題: Big Fish: Exploiting a 37-Year-Old Kernel Bug in OpenBSD
なぜ重要か
37年間見逃されたカーネル脆弱性の実証は、レガシーコードのセキュリティ監査の限界と整数型定義の危険性を改めて業界に示す。
セキュリティ企業Califは、約37年前のBSD原典コードに遡る整数オーバーフロー脆弱性をOpenBSDカーネルで発見・実証した。共有メモリの接続カウンターが符号付き16ビット短整数として定義されていたため、259プロセスをforkするだけでカウンターがラップアラウンドし、KARL・SMAP・SMEP有効環境でも情報漏洩なしにローカルroot権限の昇格に成功した。問題はerrata 026および018で修正済み。
Califが公開した調査報告によると、問題の根源は約37年前のオリジナルBSDソースコードにある。共有メモリの接続カウンター「shm_nattch」が符号付き16ビット短整数(最大値32,767)として定義されており、259プロセスをforkし続けると値が-32,643にラップアラウンドする整数オーバーフローが生じる。
攻撃の流れは明快だ。まず127個のSysV共有メモリセグメントをマップしてmunmap()で解除、その後259プロセスをforkしてカウンターを負値に転落させる。次にsysctl(KERN_MALLOCSTATS)のkb_totalやkb_totalfreeでヒープのレイアウトを把握しながらヒープグルーミングを実施。一方のプロセスがIPC_RMIDを実行するタイミングで、別のプロセスがサイズ混乱させた共有メモリをアタッチするレースコンディションを勝ち取る。
その後、IPソケットのアロケーションを整列させ、IP_MULTICAST_TTLをデフォルト外の値2に設定して16バイトの「struct ip_moptions」を永続確保。条件付き-1の境界外書き込みでimo_num_membershipsとimo_max_membershipsを上書きし、close()でグループメンバーのトラバースを誘発。これによりPTEの自己参照マッピングを二度書き換え、ユーザーモードマッピングをスーパーバイザーモードでアクセス可能な状態に変化させ、予測可能なカーネルポインタを上書きしてカーネルコード実行を達成した。
OpenBSDのKARLはカーネルバイナリを再リンクして関数・データアドレスをランダム化するが、メモリアロケーター自体のベースアドレスはランダム化されていない。このため起動時のアロケーションが同一ハードウェアで常に予測可能な位置に配置され、lo0およびenc0のioctl handlerが安定したターゲットになった。今回のexploitはSMAP/SMEPバイパスに情報漏洩を必要としなかった点が特に注目される。脆弱性はOpenBSD errata 026と018でそれぞれ修正されている。