
CVE-2018-4241: iOS 11 - 11.3.1 の MPTCP における境界チェックの不備による XNU カーネルヒープオーバーフロー、Ian Beer 氏が公開
@i41nbeer
mptcp_usr_connectx は AP_MULTIPATH ソケットファミリの connectx システムコールのハンドラです。
この関数のロジックは、AF_INET または AF_INET6 ではないソースおよびデスティネーション sockaddr を正しく処理できません。
// verify sa_len for AF_INET:
if (dst->sa_family == AF_INET &&
dst->sa_len != sizeof(mpte->__mpte_dst_v4)) {
mptcplog((LOG_ERR, "%s IPv4 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// verify sa_len for AF_INET6:
if (dst->sa_family == AF_INET6 &&
dst->sa_len != sizeof(mpte->__mpte_dst_v6)) {
mptcplog((LOG_ERR, "%s IPv6 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// code doesn't bail if sa_family was neither AF_INET nor AF_INET6
if (!(mpte->mpte_flags & MPTE_SVCTYPE_CHECKED)) {
if (mptcp_entitlement_check(mp_so) < 0) {
error = EPERM;
goto out;
}
mpte->mpte_flags |= MPTE_SVCTYPE_CHECKED;
}
// memcpy with sa_len up to 255:
if ((mp_so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0) {
memcpy(&mpte->mpte_dst, dst, dst->sa_len);
}
オーバーフローさせる構造体の中を見ると、ここで両方のフィールドに影響を与えられることがわかります。
if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) _FREE(mpte->mpte_itfinfo, M_TEMP);
mpte_itfinfo_size は mpte_itfinfo の直前にあります。
構造体が初期化されるとき、mpte_itfinfo ポインタは小さなインライン配列を指します。そこに収まりきらない数のサブフローが追加された場合、代わりにヒープバッファに配置され、mpte_itfinfo はそれを指すようになります。
別のバグ(例えば async_wake のカーネルヒープ情報漏洩バグ)があれば、mpte_itfinfo フィールドを任意の有効なゾーンオブジェクトで上書きでき、それが解放されます(実際には、そのオブジェクト内のオフセットで上書きすることでさらに面白いこともできます!)
しかし、それを持っていません。
代わりに別のアプローチとして、ポインタを部分的に上書きします。NULL バイトで部分的に上書きすると、256バイト、65k、16MB、または4GBアラインされた値を指すようにできます。
このエクスプロイトでは、3バイトの NULL 上書きを選択しました。これにより、mpte_itfinfo アドレスが次の16MB境界に切り捨てられたものが kfree されます。
エクスプロイトの流れは以下の通りです:
extra_recipe のスレッド例外ポートトリックを使用して、事前割り当てされた ipc_kmsg バッファにメッセージを送信します。各パイプをチェックしてメッセージが含まれているか確認します。正しい (ipc_kmsg, パイプ) ペアを見つけたら、パイプバッファ内に存在する偽のポートを自分自身に送信するようにメッセージを書き換えます。その偽ポートは @qwertyoruiopz と @marcograss による yalu 10.2 をベースにした async_wake のものと同様に構成し、初期のカーネル読み取りプリミティブを取得します。
カーネル読み取りプリミティブを使用してカーネルタスクを見つけ、mach_vm_read/mach_vm_write を介してカーネルメモリの読み書きを容易にする偽のポートを作成します。
注意:mptcp ソケットを接続するには、com.apple.developer.networking.multipath エンタイトルメントが必要です。これは Apple 開発者証明書を必要とし、誰でも Apple から購入できます。
信頼性: これはセキュリティ研究ツールであり、完璧には程遠いです。しかし、ほとんどの場合動作するはずであり、動作した場合はクリーンアップを適切に行い、後でパニックを起こさないはずです。
動作する確率を上げるには:
対応デバイス: iOS 11.0 から 11.3.1 まで動作するはずです。テスト済み:iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8
API:
#include "sploit.h" と go() を呼び出してエクスプロイトを実行します。
成功した場合は、kmem.h の関数を使ってカーネルメモリの読み書きができます。
注意: 複数の人がパッチからこのバグを公開バイナリ差分しています(または彼らの0dayがパッチされました ;) 詳細は彼らの資料を読んでください: @elvanderb が5月31日にパリの rump.beer でこのバグについてのライトニングトークを行いました:https://www.rump.beer/2018/slides/ios_48h.pdf @jaakerblom が6月1日にgithubで動作するエクスプロイトを公開しました:https://github.com/potmdehex/multipath_kfree John の手法は私のものと似ていますが、彼は3バイトではなく2バイトのオーバーフローを行い、異なるオブジェクトで置き換えます。良いものです!