
これはCVE-2017-7047のエクスプロイトで、10.3.2以下で動作します。
triple_fetch - ianbeer [https://bugs.chromium.org/p/project-zero/issues/detail?id=1247]
これは CVE-2017-7047 のエクスプロイトです。libxpc の論理エラーにより、 悪意のあるメッセージ送信者が共有メモリに支えられた xpc_data オブジェクトを送信できました。 xpc メッセージの消費者は、xpc_data オブジェクトのバッキングバッファが 受信側で処理されている間に送信者によって変更され得ることを想定していないようでした。
このプロジェクトは CVE-2017-7047 を利用して、iOS 上のすべてのユーザースペースプロセスに アタッチしてリモートデバッグを可能にする、概念実証用のリモート lldb debugserver スタブを構築します。
これはエクスプロイトの概要です。詳細な解説は後日公開されるかもしれません。 今のところは、詳細についてコードを参照してください :)
第一部
このエクスプロイトは、多くの iOS サービスで使用されている Objective-C リモートプロシージャコール実装である NSXPC を標的とします (1)。 NSXPC メッセージは、bplist16 シリアライズされたオブジェクトから構成され、それは mach メッセージ内の xpc シリアライズされた xpc_data オブジェクトの中に入っています。
bplist16 オブジェクトは、とりわけ Objective-C の型エンコーディング文字列 (2) を含み、 これは CoreFoundation の関数 ___NSMS1 によってパースされます。 この関数は、パース中の文字列の内容が変更されることを想定しておらず、この エクスプロイトは、文字列の特定の部分がメモリから3回フェッチされるという事実を利用して ヒープオーバーフロープリミティブを構築します。慎重に選んだ3つの異なる値の間で 切り替えることにより、選択したヒープ割り当てサイズから 任意のバイトでオーバーフローさせることができます。
minibplist16.c には、bplist シリアライズの最小実装と、 それがどのように機能するかの解説が含まれています。
外側の xpc メッセージにはヒープグルーミングが含まれます。これは、XPC シリアライズプロトコルの カスタム実装を使用し、キーが衝突する xpc ディクショナリを作成してヒープをグルーミングし、 割り当ておよび解放のプリミティブを構築します。 また、外側の xpc メッセージにはヒープスプレー(メモリ使用量を抑えるため共有メモリオブジェクトの 複数のコピーを使用)と、mach ポートの送信権名前スプレーも含まれます。
オーバーフローにより、Objective-C オブジェクトの isa クラスポインタをヒープスプレーされた 偽オブジェクトに向け、その偽オブジェクトに対してメソッドが呼び出されるとスタックが 小さな ROP スタックにピボットされます。ROP はスプレーされた mach ポート送信権名を総当たりで探索し、 候補となるスプレー済みの各送信権名に対して、ターゲットの送信権を自身のタスクポートに送信しようとします。 エクスプロイトはスプレーされたすべてのポートをリッスンし、成功すると ターゲットのタスクポートへの送信権を受け取ります。その時点でターゲットタスクを 完全に制御できるようになります。
余談
このエクスプロイトは、root で動作する coreauthd デーモンがホストする com.apple.CoreAuthentication.daemon サービスを標的とします。 このサービスにはアプリサンドボックスから到達できます。 最初にエクスプロイトを動作させた後、少し実験してみたところ、 coreauthd のコンテキストから processor_set_tasks API が、デバイス上で動作するすべてのユーザースペースプロセスのタスクポートへの 送信権を取得できることが明らかになりました。これは少なくとも2012年から公知の事実であり、 その歴史は著名な iOS 内部構造研究者である Jonathan Levin が彼のサイト (3) で詳しく解説しています。 Levin が2015年にアップロードしたコードは現在も動作します - 脱獄デバイスは不要で、 通常デバイス上の root だけで十分です。
第二部
このエクスプロイトで構築したかったデバッガの主な目標は、任意のプロセスにアタッチし、 ブレークポイントを設定して、ヒットしたときにレジスタとメモリの状態を検査および変更できるようにすることでした。 gdb や lldb のリモートプロトコルをゼロから実装する代わりに、 lldb debugserver プロジェクトに必要な変更を加え、それからエクスプロイトを使ってそれを実行することにしました。
コード署名の無効化または回避を必要とするソフトウェアブレークポイントを使用する代わりに、 debugserver はハードウェアブレークポイントのみを使用するようにパッチされています。ARM64 には16個のハードウェアブレークポイント レジスタがあり、つまりアクティブなブレークポイントは最大16個までです。
ARM ハードウェアブレークポイントのプロトタイプサポートは lldb debugserver コードに存在していましたが、 動作させるには多少のハックが必要でした。例えば、デバッグ対象の pthread_introspection_hook 関数ポインタを 常にクラッシュするようにパッチするコードを追加し、新しいスレッドの生成を検出して、 ハードウェアブレークポイントの状態を新しいスレッドに伝播し、 クラッシュしなかったかのように続行できるようにしました。
また、アタッチと continue のコードをパッチして、ptrace とシグナルを使用する代わりに タスクポート経由でタスクを直接停止および再開するようにしました。
ビルドのヒント
すべて 動作するはず です(iOS 10.0 から 10.3.2 まで)。以下でテストしています:
iPhone 7 + 10.3.2 iPod Touch + 10.1.1 iPad Mini 2 + 10.2
プリビルドの debugserver バイナリを同梱しています。これを使うことをお勧めしますが、 lldb の debugserver 用パッチも debugserver.diff に含まれています。
debugserver のビルドはそれほど難しくありません。以下の git リビジョンを使用していました:
lldb: 0db640c4cd1ec4e4c2580336fa5f53be029c5bc7 llvm: ec48fd127774a4b67c72ea7c3057b5c964375e77 clang: b6e778e0bfa2fc32f8821c6b33762f5cb6724659
同梱の debugserver.diff を適用してください。
ビルドには(最近の)cmake と ninja が必要です。これらはソースから入手するか、 お好みの Mac パッケージマネージャからバイナリを入手できます。
(4) には、MacOS で通常の llvm ビルドをセットアップするためのガイドがあり、参考になるかもしれません。
iOS SDK に多数のヘッダファイルをシンボリックリンクする必要があります。少なくとも以下です:
xpc/ launchd.h libproc.h sys/proc_info.h sys/kern_control.h net/route.h mach/mach_vm.h mach/shared_region.h sys/ptrace.h crt_externs.h
以下の cmake の呪文で、必要なヒントがすべて得られるはずです:
cmake -G "Ninja" -DCMAKE_OSX_ARCHITECTURES="armv7;armv7s;arm64" -DCMAKE_TOOLCHAIN_FILE=../cmake/platforms/iOS.cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_RUNTIME=Off -DLLVM_INCLUDE_TESTS=Off -DLLVM_INCLUDE_EXAMPLES=Off -DLLVM_ENABLE_BACKTRACES=Off ../ ninja debugserver
その後、debugserver バイナリに署名またはフェイク署名し、同梱の xcode プロジェクト内のものを置き換える必要があります。
コード署名
エクスプロイトプロジェクトは、デフォルトで改良版の mach_portal amfid フックをインストールします (今回は fat ファイルのサポートが機能し、ハードコードされたオフセットもありません :) )
単にデバッグしたいだけなら、debugserver バイナリに自分の 証明書で署名し、amfid フックを無効にできるはずです。
amfid フックを使用する場合、それが実行されているアプリが依然としてバックグラウンドコード実行制限の対象であることに注意してください。 アプリは beginBackgroundTaskWithName を介して追加の時間を要求します。
使用方法:
ホストとターゲットの iDevice を同じワイヤレスネットワークに接続し、iDevice の IP アドレスを確認してください。
エクスプロイトアプリをビルドして実行します。xcode 内で行うことをお勧めしますが、単体でも動作します。
少し待ちます。数分経っても動作しない場合は、デバイスを強制再起動し、少し待ってから再試行してください。
動作すれば、“patched debugserver listening on port 1234” と表示されるはずです。
“get process listing” ボタンをクリックすると、ps の出力が表示されるはずです。
(xcode を使うと出力が見やすくなりますが、エクスプロイトも出力を表示します)
デバッグしたいターゲットプロセスを探し、その pid を確認してください。
ホスト上でコマンドラインから lldb を起動します: $ lldb (lldb)
プラットフォームを ios remote に設定します: (lldb) platform select remote-ios
debugserver スタブに接続します: (lldb) process connect connect://192.168.0.172:1234
(192.168.0.172 は iDevice の IP アドレスです)
対象のプロセスにアタッチします: (lldb) attach 55
...少し待ちます。debugserver は verbose モードで実行されています...
Process 55 stopped
Executable module set to "/usr/libexec/backboardd".
アタッチできました :)
ブレークポイントを設定します: (lldb) break set --name malloc Breakpoint 1: 4 locations.
continue します:
(lldb) c Process 55 resuming Process 55 stopped
malloc libsystem_malloc.dylibmalloc:
-> 0x1936161e0 <+0>: stp x20, x19, [sp, #-0x20]!
0x1936161e4 <+4>: stp x29, x30, [sp, #0x10]
0x1936161e8 <+8>: add x29, sp, #0x10 ; =0x10バックトレースを取得します:
(lldb) bt
malloc frame #1: 0x0000000100099648 backboardd_mh_execute_header + 71240
frame #2: 0x00000001945bf218 CoreFoundation__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 56 frame #3: 0x00000001945be9cc CoreFoundation__CFRunLoopDoSource1 + 436
frame #4: 0x00000001945bc4b0 CoreFoundation__CFRunLoopRun + 1840 frame #5: 0x00000001944ea2b8 CoreFoundationCFRunLoopRunSpecific + 444
frame #6: 0x0000000194537b44 CoreFoundationCFRunLoopRun + 112 frame #7: 0x00000001000a5ba8 backboardd_mh_execute_header + 121768
frame #8: 0x00000001000a5bec backboardd_mh_execute_header + 121836 frame #9: 0x00000001936a5850 libsystem_pthread.dylib_pthread_body + 240
frame #10: 0x00000001936a5760 libsystem_pthread.dylib_pthread_start + 284 frame #11: 0x00000001936a2d94 libsystem_pthread.dylibthread_start + 4トラブルシューティング:
最新バージョンの XCode をインストールしてください(8.3.3 で開発およびテスト済み) lldb connect が失敗する場合は、対象の iOS バージョン用の SDK がインストールされていることを確認してください
注意事項:
これらを修正したいと思っていますが、現在とても忙しいので申し訳ありません!
他の PoC を実行する:
このプロジェクトの pocs フォルダにバイナリを置くと、UI で選択することでエクスプロイトにそれを exec させることができます。 デバイス上で "exec bundle binary" をクリックします。
それでもアプリサンドボックス内で実行されますが、アプリに privileged_task_port というシンボルがある場合、 launchd のタスクポートへの送信権が与えられます。triple_fetch_sdk フォルダには、 これを使用して PoC を構築する方法を示すサンプルプロジェクトが含まれています。例えば、興味深いプロセスで動作をトリガーし、 その後 debugserver を使ってデバッグできます。
また、他のプロセスの関数を簡単に呼び出したり、 ファイルディスクリプタ、mach ポート、メモリを移動したりできる、リモート呼び出し/ポート/ファイル/メモリ API も含まれています。
(1) [https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html] (2) [https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html] (3) [http://newosxbook.com/articles/PST2.html] (4) [https://gist.github.com/thlorenz/a068c202f2487ec13809]
このプロジェクトには、lldb に基づくコード (debugserver.diff) とバイナリ (debugserver) が含まれており、 以下のライセンスが適用されます:
University of Illinois/NCSA Open Source License
Copyright (c) 2010 Apple Inc. All rights reserved.
Developed by:
LLDB Team