
CVE-2021-39749のPoC。Android 12L Betaで任意のActivityを起動できるようにするもの。
これはCVE-2021-39749のPoCであり、Android 12L Betaで、他のアプリのpermissionやexported設定に関係なく、そのアクティビティを起動できるようにするものです。
Android 12Lでは、TaskFragmentOrganizerへのアクセスは(意図的に)MANAGE_ACTIVITY_TASKS権限を必要としなくなりました
ここで提供されるアプリを使用するには、Hidden API Checksを無効にする必要があります。adb shell settings put global hidden_api_policy 1で無効にできます。これらはセキュリティ境界ではなく、既知のアプリベースのバイパスが存在します
このバグ(および元のレポートで言及された関連するいくつかのバグ)を修正するコミットは次のとおりです。
startActivityInTaskFragmentがBinder.getCallingUid()に依存しなくなったResolverActivityでrelinquishTaskIdentityが有効になったTaskFragmentのSurfaceControlが提供されなくなったActivityRecord#appTokenをTaskFragmentOrganizerに送信するかどうかの決定が、pidベースからuidベースに変更されたandroid-12.1.0_r4をチェックアウトし、最初の3つのコミット(または最初の2つ。アプリは(ShutdownActivityを起動することで)デバイスをシャットダウンできますが、「ズームしてアルファを設定」チェックボックスは機能しません)をリバートできます。
(そのリストの最初のコミットは、リバートしようとするとテストでマージ競合が発生しますが、これらは無視できます)
Binder.getCallingUid()Binder.getCallingUid()メソッドは、現在処理中のBinderトランザクションを送信したプロセスのuidを返します。そのuidはスレッドローカル変数に格納されます。トランザクションを処理するコードは、Binder.clearCallingIdentity()を呼び出して、その変数を自身のプロセスのuidに設定し、トランザクション処理中に後で呼び出されるメソッドに対して、権限チェックがBinderトランザクションの呼び出し元ではなく自身に対して行われるべきであることを示すことができます。
Binder.clearCallingIdentity()の後に常に呼び出されるため、常に自身のプロセスのuidを返すBinder.getCallingUid()が存在することがあります。これは意図的に行われる場合もあります。例えば、ActivityTaskManagerService#startDreamActivityなどです(ただし、これはProcess.myUid()やOs.getuid()を行うかなり回りくどい方法ですが)
私は自分用に、(Sootベースの)静的解析ツールを作成しました。これは、Binder.clearCallingIdentity()の後にのみ発生し得る、そのようなBinder.getCallingUid()呼び出し(およびその他の権限チェック)を報告します。(Sootが提供するJimple/Shimple IRを処理するカスタムロジックがありますが、Sootでそれを行うより良い方法があるかもしれません。しかし、それが現在私が持っているものです)
Android 12L Betaで、そのツールはActivityStartController#startActivityInTaskFragmentに1つ発見しました(注:Betaリリースはオープンソースではないため、当時ソースコードは利用できませんでしたが、Shimpleは一般的に読み取り可能なので、SootをJavaデコンパイラとしても使用していました)
startActivityInTaskFragmentの呼び出し方静的解析レポートの一部として、onTransact()実装(Binder呼び出しが開始される場所)からstartActivityInTaskFragmentへの呼び出し階層を取得しました。
IWindowOrganizerControllerのaidl生成コード内のonTransactWindowOrganizerController#applyTransaction(CallerInfo引数なし)WindowOrganizerController#applyTransaction(CallerInfo引数あり)WindowOrganizerController#applyHierarchyOpActivityStartController#startActivityInTaskFragmentapplyTransactionへのBinder呼び出しがTaskFragmentOrganizerクラスに存在することを発見し、すべてのBinder呼び出しを直接行うよりも便利なラッパーとして使用することにしました(どちらも公開APIではないため、いずれにせよリフレクションを使用する必要がありました)
まず、メソッド「2.」はenforceTaskPermissionを呼び出します。これはAndroid 12.0では、取得できないsignatureのみのMANAGE_ACTIVITY_TASKS権限をチェックしていましたが、Android 12Lではルールが緩和され、特定のトランザクションは権限なしで実行できるようになりました。startActivityInTaskFragmentの実行に必要な操作はどれも権限を必要としないことが判明しました(トランザクションにTaskFragmentOrganizerが関連付けられている場合)
したがって、HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENTを実行したいと考えています。そのためには、TaskFragmentをmLaunchTaskFragmentsに登録する必要があります。そうしないと、"Not allowed to operate with invalid fragment token"例外が報告されます。
HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENTを通じて、そのようなTaskFragmentを登録できます。これはcreateTaskFragment()を呼び出します。
(PoCコードでは、これらのトランザクションはSecondActivityで送信されます。HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENTはinitOrganizerAndFragment()によって送信され、HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENTはstartActivityInOrganizerによって送信されます)
これにより、startActivityInTaskFragmentを呼び出せるようになり、ここで起動されたアクティビティのインテントはシステムuidから送信されたものと見なされますが、それ自体では何もできないことが判明しました。システムによって起動されたアクティビティはURIグラントを実行できず、別のアプリのアクティビティを起動しようとすると、canEmbedActivityチェックによって停止されるためです。
canEmbedActivityのバイパスcanEmbedActivityをもう一度見てみましょう。taskFragment.getTask().effectiveUidがシステムのuidであるか、起動されたアプリのuidと一致する場合、埋め込みが許可されます。effectiveUidがシステムであるタスクに存在する必要があります。