Linuxカーネルの脆弱性が学位論文のデータ収集にどのように役立つかという話
数年前、私は彼がスマートフォンの画面に入力するジェスチャーで人を特定できるかどうかを調べることにしました。 特定の「キーボード手書き」ですが、タッチスクリーン専用です。 これを理解するには、多くの異なるユーザーからの何十万ものジェスチャーを分析する必要があります。 しかし...スマートフォンでこのデータを収集する方法は?
この問題を解決する方法についてお話します。 彼は長くて、とげだらけでしたが、とってもエキサイティングです! 彼をフォローして、Linux、Android、それらのセキュリティ、およびそれらの内部について新しい何かを学ぶことに興味があることを願っています。 私はLinuxデバイスの第一人者ではないので、一部の人には説明が不必要に詳細であるように見えるかもしれませんが、これも私のやり方であり、プロセスで学んだことをすべて詳しく説明します。 これにより、経験のあるLinuxユーザーが疎外されず、他のすべてのユーザーのエントリーしきい値がわずかに下がることを願っています。 だから。 Android用のタッチロガーを実装する方法は?
パス選択
[1]で行われたように、最も単純で明白な解決策は、別個のアプリケーションを記述し、その中でのみジェスチャーパラメーターを収集することです。 しかし、これはまったく面白くないタスクです。 それは単純すぎるからだけではありません。 データ収集を1つのアプリケーションに制限すると、
特別な条件下でのみ発生するユーザージェスチャのいくつかの重要な動作特性を見逃す危険があります。 したがって、ユーザーが作業しているアプリケーションに関係なくジェスチャを収集したいと思いますが、これを行うのはそれほど簡単ではありません。
モバイルOSは、デスクトップOSよりも桁違いに保護されています。 後者とは異なり、セキュリティとプロセスの分離について真剣に考えたときに開発されたものであり、同じAndroidでは、アプリケーションのビュー外でユーザーのジェスチャーを追跡することはできません。 他のモバイルOSでも同じ状況だと思います。 しかし、すべてが本当に十分に保護されているので、タッチの座標を取得するために、Linuxカーネルの脆弱性の助けに頼らなければなりませんでしたか?
たとえば、今では多くの人が、Androidアクセシビリティサービス-ユニバーサルアクセスサービスについて覚えています。 障害を持つ人々を支援したり、Android向けのさまざまな種類のトロイの木馬を作成したりするのに便利です。 アプリケーションのユニバーサルアクセスイベントにサブスクライブし、ユーザージェスチャに関するデータを受信できます。 どうやら! 使用して、すべてがすでにあなたのために書かれています。 しかし、残念ながら、このデータから必要なジェスチャーパラメーターを抽出することはできません。
まあ、多くのオプションは残っていません。 たとえば、開発者のメニューに「ディスプレイタッチ」という項目を含めるようにしてください。

その後、どのようなアプリケーションでも、ジェスチャーの痕跡が画面に残ります。 したがって、このコードは少なくともタッチの座標に関する情報にアクセスできます。 彼はどこでこのデータを入手したのだろうか? 答えを探して、Androidのソースに突入し、
これを見つけ
ます 。

mPointerLocationView
行により、
mPointerLocationView
オブジェクトはタッチスクリーンからのすべての入力イベント
mPointerLocationView
処理できます。
クラス階層と呼び出しグラフを調べると、最終的にはすべて
WindowManagerService
システムサービスで
registerPointerEventListener
メソッドを呼び出すことになります。
WindowManager
インターフェースを介した後者により、通常のアプリケーションはメソッドをリモートで呼び出すことができます。 アプリケーションで
WindowManager
にアクセスし、このメソッドを呼び出すだけで十分であるように思われ、アプリケーションはシステムからの座標を処理できるようになります。 ただし、注意点があります。Androidでは、システムサービスへのアクセスは
Binder IPCを介して提供され、対応する
AIDLファイルにあるメソッドのみをWindowManagerから呼び出すことができます。 残念ながら、このメソッドはAIDLに含まれていないため、ユーザーアプリケーションからリモートで呼び出すことはできません。 ただし、ベンダーであり、他にユーザーを追跡する方法がわからない場合は、Androidソースを変更し、デバイスのファームウェアにジェスチャーロギングを直接追加することができ
ます 。 確かに、私たちの場合、そのようなソリューションは適合しません。 データ収集のために1つ、2つのデバイスをフラッシュできますが、数十人のなじみのないテストユーザーはそのようなステップを踏むことはありません。 より普遍的な方法が必要です。
さて、ここに、私は代替手段を見つけるためにあらゆる手段を試しましたが、できませんでした。 そのため、AndroidはLinuxカーネルに基づいており、入力システムのアーキテクチャはLinuxディストリビューションと同じです。 タッチスクリーンを含む入力デバイスのドライバーは、
/dev/input/
にある文字デバイスを介してユーザースペースにデータを送信し、そこから読み取り可能にします。 また、ドライバーから直接、必要なすべてのデータを取得できます。座標、タイムスタンプ、タッチ領域、ジェスチャーのタイプなど。複数の調査に十分です。 確かに、キャッチもあります。Androidでは、これらのデバイスへのアクセスには、rootまたは入力グループに属するユーザーがいます。 Androidアプリケーションは、そのような権利を自慢できないユーザーのために起動されます。
この状況から抜け出す方法は、ルート化されたデバイスでデータを収集するためのアプリケーションを起動することです。 この目的のため
に 、タッチロガーの
2番目のバージョンを作成しました 。
このバージョンを使用してデータを収集しました。 助けたいと思う人は十分にいましたが、わいせつになったデバイスはわずかでしたので、あまり収集しませんでした。 ルートをインストールせずにデータを収集する方法を見つけた場合、より適切なテストデバイスが何桁もあります。これにより、分析のために数百メガバイトの貴重なデータが得られます。 Linuxカーネルの1つの素晴らしい脆弱性がこれに役立ちます。
CVE-2016-5195別名DirtyCOW
人にこの脆弱性の発見と修正の歴史は、それ自体非常に面白いものです。 彼女は
11年前に誤って発見されましたが、決して閉じられませんでした。 それを使用した既製のエクスプロイトが発見された後、2016年にのみ再発見および修正されました。 これが、11年の歴史を持つ脆弱性がゼロデイに変わった理由です。 注目すべきは、2.6.22以降のすべてのバージョンのカーネルに存在することです! つまり、Androidのすべてのバージョンで。 Androidセキュリティ速報では、この脆弱性
は 2016年12月に
登場しました。つまり、この日付以降にセキュリティ更新プログラムを受信したデバイスでのみ修正されました。 たとえば、Nexus 5は、他の数百のAndroidデバイスと同様に、露出したままです。
dirtyCOWは何をしますか? つまり、ユーザーが読み取り専用のファイルを上書きできます。 「any」とは、ファイルシステム自体が読み取り専用にマウントされている場合でも(Anythingを意味します(Androidの場合、/ systemセクションには、最もおいしいシステムファイルが保存されます)。 脆弱性のアクションのメカニズムに関する詳細は、例えば
ここにあります 。 覚えておくべきことは2つだけです。 最初:読み取り専用ファイルシステムへの変更は再起動後に消えます。2番目:ファイルサイズを変更することはできません。つまり、ディスクスペースを占有する以上の書き込みを行うことはできません。
さて、この脆弱性によって与えられた力により、読み取り可能なシステムファイルを一時的に上書きできます。 これは私たちの手を引き離しているように思えます;あなたはあなたがやりたいことができます! ただし、Androidは十分に保護されているため、この保護を回避して目標を達成する必要があります。
実装アイデア
私たちのタスクは、入力デバイスからデータを読み取ることができる通常のアプリケーションからプロセスを開始することです。 UID = 0(ルート)のユーザーまたは入力グループ(AndroidではGID = 1004)のユーザーから開始するプロセスのみが入力デバイスにアクセスできることを思い出させてください。
たとえば、ファイル
/system/bin/ping
考えてください。 ほぼすべてのAndroidデバイスにあり、すべての人が読み取りおよび実行できます。 注目に値するのは、このファイルにSUIDビットがあることです。 SUID(実行時に設定された所有者ユーザーID-実行時に所有者UIDを設定)は、Linuxのファイル属性の1つです。 通常、プロセスは、それを開始したユーザーの権限を継承します。 SUIDビットが存在すると、実行可能ファイルの所有者の権利、およびそのUIDとGIDでプロセスを開始できます。 したがって、pingの所有者はrootであるため、このファイルはrootとして実行されます。 私が得ているものを参照してください? pingをdirtyCOWで書き換えて実行すると、コードはルートとして実行され、入力デバイスを読み取ることができます! 紳士、おめでとう、私たちは成功しました、すべての偉大な仲間、私たちは同意しません...いいえ。
SELinux
はい、私たちの計画は4.2までのAndroidのすべてのバージョンで驚くほど機能します。 しかし、より新しいバージョンではそれらが発生する可能性があり、5.0からは、いくつかの問題が必ず発生します。 欠点はSELinux(Security-Enhanced Linux)です。これは、既存の任意のポリシーに加えて必須のセキュリティポリシーを実装するカーネルモジュールです。 仕組みについて簡単に説明します。 各ユーザー、プロセス、ファイル、ネットワークポート、キャラクターデバイスなどには、いわゆるセキュリティコンテキスト(特定のラベル、追加属性)があります。 システムには、ユーザーとプロセスのコンテキストに基づいて許可される操作を記述する一連のポリシーがあります。 ポリシーのセットに含まれないすべての操作は禁止され、システムによって厳密に抑制されます。
サードパーティのアプリケーションは、untrusted_appコンテキストを持つプロセスで実行されます。 すでにコンテキストの名前から、そのようなプロセスにはほとんど権限がないことがわかります。
/system/bin/ping
ファイルにはsystem_fileコンテキストがあり、はい、SELinuxはsystem_fileをuntrusted_appコンテキストから実行することを許可しません。 したがって、SUIDビットを持つ他のシステムファイルのように、Android 5.0以降のアプリケーションからpingを実行することはできません。
実際、SELinuxのコンテキストはいくつかの部分で構成されています。たとえば、pingの場合、コンテキストの完全な形式はu:object_r:system_file:s0
で、ユーザーの場合はu:r:untrusted_app:s0
です。 私たちの場合、それは問題ではないので、短いレコードを使用します。
他の方法
そのため、システムには、サードパーティのアプリケーションから起動するために利用可能なSUIDビットを持つファイルはありません。 しかし、いくつかのファイルを上書きし、システムにそれをルート自体として実行させるとどうなりますか? Androidには、サービスなどがあります。 あなたのアプリケーションから始まり、バックグラウンドで何かをするものではありません。 サービスは、システムの起動時に開始されるデーモンです。
init.rcファイルに記述されているサービスの例特に、アンドロイドはクラッシュした場合にサービスを再起動します。 さらに興味深いことに、多くのサービスはルートとして実行されます。 android 6.0.1では、これらはvold、health、debuggerd、installd、zygoteなどです。 そして、はい、ファイル
/system/bin/app_process
(別名zygote)は、すべてのユーザーが読み取り可能です!
別のファイルに記述されているZygoteサービス新しい計画の概要は徐々に浮上しています。 ペイロードで
/system/bin/app_process
を上書きし、zygoteサービスをドロップすると、アンドロイドはそれを再起動し、app_processファイル内のコードはルートとして実行されます。
問題は、接合子を落とす方法です。 実際、これはタッチロガーの実装において最も信頼できない瞬間です。 今
/system/bin/app_process
、ペイロードで
/system/bin/app_process
上書きすると、zygoteは「単独で」ドロップするという事実に依存しています。 しかし、これが常に機能するという保証はありません。 app_processファイルを書き換えたときに何が起こるか、そしてそれがクラッシュを引き起こす可能性があるかどうかを見てみましょう。
仮想メモリとmmap
Linuxでは、各プロセスに独自の仮想アドレススペースがあります。 スタック、ヒープ、環境変数など、プロセスに必要なすべてのデータが保存されます。 このスペースは、カーネルによってRAMの物理アドレスに変換されます。
この概念の優れた視覚化。ただし、同じスペースには、プロセス自体の実行可能ファイル、そのインタープリター、その依存関係からのライブラリー、およびさらに多くの異なるファイルがあります。 これらすべてをRAMに保存するのはもったいないので、LinuxにはRAMをまったく無駄にせずにプロセスメモリにファイルをロードするための非常にエレガントなメカニズムがあります。 これは、メモリにマップされたファイル、またはメモリに反映されたファイルと呼ばれます。 カーネルはプロセスのアドレス空間にファイルの内容を含むページを作成しますが、実際には、この仮想ページはカーネルによってRAMに変換されるのではなく、直接ディスクに変換されます。 つまり、プロセスはファイルの内容を使用して、割り当てられたメモリの大きな部分で動作しますが、実際にはディスクへのバイトの読み取りまたは書き込みを行います。 (ちなみに、dirtyCOWの仕組みを読んでいない場合は、メモリに反映されたファイルへのマルチスレッドアクセスのバグに基づいています)。
この「反映された」形式で、その実行可能ファイルがプロセスメモリに保存されます。 部分的にメモリにロードされますが、ほとんどはディスクに残ります。 したがって、ファイルの変更はすぐにプロセスメモリに表示されます。 実行可能ファイルを置換した後、プロセスがメモリから次の命令をロードするとします。 高い確率で、それは「不適切に」落下し、プロセスを落下させたり、同じ結果になってゴミになることさえあります。 ただし、実行ファイルの置換中のプロセスは、select()、read()、またはwaitpid()を実行することにより、ブロッキング呼び出しで「ハング」することがあります。 この場合、コールが終了し、彼が存在し続けるまで、彼には何も起こりません。 実行可能ファイルを置き換えた後のプロセスの存続には、おそらく他のシナリオもありますが、Linuxでの私の経験が少ないため、それらに慣れていません。
ファイルを上書きするときに何が起こるか、そしてそれがプロセスをクラッシュさせる(またはさせない)方法を見つけました。 再起動するためのより信頼性の高い方法を思いついたことがないので、そのままにしておきましょう。 さらに、この方法は非常にうまく機能します(サムスンデバイスは、独自のサムスンマジックを備えており、zygoteは実行可能ファイルを上書きしても落下しません。それがどのように機能するかはまだわかりません)。
zygoteに戻ります。 他のバイナリファイルで簡単に置き換えることができる、それほど重要でないサービスとは何ですか? Zygoteは、すべてのAndroidアプリケーションを生成するプロセスです。 同じクラッシュのクランのアイコンをクリックすると、zygoteはfork()を使用してプロセスのコピーを作成し、このコピーで目的のアプリケーションを起動します。 これは、リソースを節約し、起動を高速化するために行われます。 zygoteを殺すと、すべてのAndroidアプリケーションは彼の後に落ちます。 この状況は、タッチロガーから意味を奪います。 レンガからカスタムジェスチャーを収集する理由 幸いなことに、この問題は簡単に回避できます。
悪魔
まず、zygoteがクラッシュした場合、すべてのAndroidアプリケーションが突然終了するのはなぜですか? 結局のところ、Linuxの親プロセスの最後で、子は引き続き正常に動作し、initは新しい親になります。 しかし、実際には、起動時にzygoteが新しいプロセスのグループまたは新しいセッションを生成します。 このセッションでは、すべてのAndroidアプリケーション、Androidアプリケーションのすべての子プロセス、それらの子プロセスなどが起動されます。 セッションを生成したプロセスが終了すると、同じセッション内のその子孫の分岐ツリー全体がそれで終了します。 したがって、ターミナルエミュレーターを閉じると、そこで実行されているすべてのプロセスが終了します。 これが、zygoteがすべてのAndroidアプリをクラッシュした場合にクラッシュする理由です。
上記のように、これはいくつかの問題を引き起こします。 最初のもの。 ペイロードが起動され、アプリケーションが強制終了された後、誰がapp_processをその場所に返しますか? そして2つ目。 app_processが戻った後、ペイロードを実行したままにする方法
セッションの終了後もプロセスが機能し続けるためには、現在のセッションから解放する必要があります...新しいセッションが誕生しました! これは、setsid()システムコールを使用して行われます。 しかし、もっとトリッキーなこともできます。 1回の呼び出しで、新しいプロセスを開始し、現在のセッションから切り離し、標準入出力ストリームから切り離すことができます。 この魔法の呼び出しはdaemon()と呼ばれ、fork()とsetsid()の力を組み合わせて、デーモン自体が地獄の産物であるため、親がセッション全体で地獄に落ちても動作する新しいデーモンプロセスを作成します。
デーモン()を使用すると、上記の問題は両方とも完全に解決されます。 まず、アプリケーション内にデーモンを作成します。 zygoteの落下後も存続するため、ペイロードでapp_processを上書きし、開始するのを待ってからapp_processを返すことができます。
写真の最初の問題の解決策2番目は同じ方法で解決されます。システムがペイロードを開始すると、その中に別のデーモンを作成します。これはapp_processの復元後に機能します。 しかし、そうでしょうか? 実行可能ファイルを上書きするときにプロセスのクラッシュをキャンセルした人はいませんでした。悪魔でさえも保存されません。 実行中のファイルの実行中の置換のみがこれから保存されます。 なぜそれをしないのですか?
実行()
execveの本質()このシステムコールは、現在のプロセスを新しいプロセスに置き換え、引数で指定されたバイナリファイルを起動します。 これについては、対応する
manで詳しく読むことができます。
ところで、Linuxのほとんどすべてのプロセスはfork()+ execve()によって起動されます。
その結果、システムはペイロードを起動します。 デーモン()とexecve()を実行して別のbinarを起動し、exec_payloadと呼びます。 その後、app_processをその場所に戻しますが、exec_payloadは引き続き機能します。
写真の2番目の問題の解決策これで、通常のアプリケーションからルートとしてプロセスを開始する方法がわかりました。 より正確には、システムの半分を強制終了して、ルートとしてコードを実行し、すべてを「そのまま」返すようにする方法です。 しかし、困難はそれだけでは終わりません。 SELinuxを覚えていますか? したがって、彼はどこにも行かず、まだ多くの操作の実行を許可していません。
SELinux [2]
SELinuxコンテキストの観点から開始した後、exec_payloadがどのような状況になるかを検討してください。 Zygoteのコンテキストは同じです-zygote。 Exec_payloadは、起動時にそれを継承します。
/dev/input
内のキャラクターデバイスにも独自のコンテキストinput_deviceがあります。 そして、予想通り、SELinuxは、zygoteコンテキストを持つプロセスがinput_deviceコンテキストを持つファイルにアクセスすることを許可しません。 SELinuxを再び実行するために、本当にこのようにすべてを行ったのでしょうか?
そうでもない。 今回は状況はまだ少し良くなっており、私たちはより多くの権利を持っています。 zygoteはすべてのAndroidアプリケーションを実行することを覚えていますか? したがって、zygoteコンテキストを持つプロセスにuntrusted_appコンテキスト(または組み込みアプリケーションの場合はplatform_app、または理由がわかるisolated_app ...)を持つ子がある場合、zygoteにはselinuxコンテキストを変更する権利があることを意味します。 入力デバイスにアクセスできる目的のコンテキストを見つけることは残っています。 見つけやすいです。それはシェルです。
シェルは(同じ名前のコンテキストを持つ)ユーザーであり、
ADB (Androidデバッグブリッジ)を介してリモートでAndroidデバイスにアクセスすると、すべてのコマンドが実行されます。 彼は、initまたはカーネルコンテキストを使用するrootよりも権限は低くなりますが、Androidアプリケーションよりもはるかに多くの権限を持っています。 特に、シェルユーザーは読み取りおよび書き込み用の入力デバイスにアクセスできます。 コンテキストをzygoteからshellに変更し、入力グループに自分を追加することにより、exec_payloadは最終的に切望されている入力デバイスにアクセスできるようになります。
zygoteからシェルコンテキストを取得する #ifdef __aarch64__ void * selinux = dlopen("/system/lib64/libselinux.so", RTLD_LAZY); #else void* selinux = dlopen("/system/lib/libselinux.so", RTLD_LAZY); #endif if (selinux) { void* getcon = dlsym(selinux, "getcon"); const char* error = dlerror(); if (!error) { getcon_t* getcon_p = (getcon_t*) getcon; char* secontext; int ret = (*getcon_p)(&secontext); void* setcon = dlsym(selinux, "setcon"); const char* error = dlerror(); if (!error) { setcon_t* setcon_p = (setcon_t*) setcon; if ((*setcon_p)("u:r:shell:s0") != 0) { LOGV("Unable to set context: %s!", strerror(errno)); } (*getcon_p)(&secontext); LOGV("Current context: %s", secontext); } } dlclose(selinux); } else { LOGV("SELinux not found."); }
そして最後に、最後の問題を解決することが残っています。(システムの半分が落ちた後に再起動された)アプリケーションとデータを読み込むデーモンとの間でデータを交換する方法は/dev/input/event[X]
?いくつかのオプションが可能です。残念ながら、それらのうち最も適切なもの(UNIXソケット、FIFO)は利用できません(邪魔なSELinux!)。1つのプロセスでSDカードにデータを書き込み、別のプロセスでそこからデータを読み取ることは残ります。最もクールなオプションではありませんが、それでもです。さらに、amビルトインユーティリティを使用して、特別な目的でアプリケーションにデータを送信できます(実際、これはActivity Managerのコマンドインターフェイスです)。おそらく私が考えていない他の方法があります。データ収集の「手動」方法
エクスプロイトを使用してさまざまな目標を達成することは非常に信頼できません。あらゆるバージョンのAndroidを搭載したすべてのデバイスで動作する普遍的なエクスプロイトベースのソリューションを実装することはほとんど不可能です。私のタッチロガーの実装も例外ではありません。 app_processを書き換えた後、zygoteがどこにでも落ちるわけではありません。おそらく、すべてのSELinuxデバイスでは、コンテキストをzygoteからシェル、ペイロードに変更できるわけではありません。そして、私にはまだ知られていない他の落とし穴が確かにあります。ただし、私たちの目標は入力イベントを収集することです。テストユーザーがこれにデバイスを提供することに同意した場合、シンプルで100%ユニバーサルな別のソリューションを導入できます。確かに、それを起動するには、いくつかの手動操作を行う必要があります。 adbでexec_payloadをディレクトリにドロップした場合/data/local/tmp
(シェルがアクセスできる場所)、そこから起動します-デーモンは、アプリケーションが起動しているように動作し始めます。唯一のことは、UIDが異なる(0ではなく2000)ことですが、これは入力デバイスへのアクセスには影響しません。そのため、タッチロガーの3番目のバージョン(現在、頭に浮かびます)では、別のバックアップオプションとしてルート化されたデバイスで実行する可能性を残しながら、最後の手段としてこのようなオプションを提供します。その他のDirtyCOW Androidアプリケーション
もちろん、エクスプロイトは常に科学のような高貴な目的に使用されるわけではありません(ほとんど使用されません)。そして、おそらく、上記の特権エスカレーション方法を使用して、DirtyCOWが他のアプリケーションを見つけることができるのか疑問に思うでしょう。ご存じのように、これは完全なルートではないため、機能は非常に限られています。それにもかかわらず、これらの権利でいたずらを行うことができます。まず、入力デバイスへのアクセスを使用してパスワードを盗むことができます(タッチロガーでは、「一時停止」ボタンを作成して、このデータが自分のものになったり、間違った手に渡らないようにします) 2番目:amやpmなどの組み込みユーティリティを使用できます。これにより、アプリケーションを密かにインストールおよびアンインストールできます。次に、Amを使用すると、現在実行中のアクティビティに関する情報を他のアクティビティの上で取得できます。銀行のトロイの木馬に最適な機能、これは、銀行業務アプリケーションを開始するときに、独自のログインフォームの上に描画します。一般に、dirtyCOWは、他のツールと同様に、良い目的とそうでない目的の両方でさまざまな目的に使用できます。最後のトリック
結論として、最も興味深いのは、より特権のあるユーザーからコードを実行する別の方法です。突然必要になった場合、mediaserver、netd、debuggerd、またはその他のサービスの特権でコードを実行できます。実際、実行時に任意のコードを実行するために実行可能ファイルを上書きする必要はありません。これは、このファイルを読み取る権限がない場合、またはファイルのサイズが小さすぎて多少有用なもので上書きできない場合、非常に良いニュースです。トリックの要点は非常にシンプルでエレガントです。 Androidのほぼすべての実行可能ファイルは、libcutils.soシステムライブラリに依存しています。そして、実行可能ファイルを実行すると、このライブラリがプロセスメモリにロードされます。 ELF形式の共有ライブラリの特性は、特別なセクション.init
とを持っていること.init_array
です。これらのセクションにアドレスが配置される関数は、ライブラリがプロセスにロードされるときに実行されます。したがって、セクション内の関数を使用してライブラリをコンパイルし.init_array
、ファイル/system/lib/libcutils.so
を上書きすると、プロセスにロードされると、この関数はプロセスの特権で安全に実行されます。 __attribute__((constructor)) void say_hello() { payload_main(); }
コンストラクター属性を持つ関数は、コンパイル時に.init_array
ライブラリセクションに配置されます。しかし、このソリューションはどれほど普遍的でしょうか?Androidの異なるバージョンのプロセスが、この重要なライブラリで特定の機能を見つけることができないという事実に問題を抱えることは望みません。実際、libcutils自体の書き換えは非常に危険です。しかし、彼はまた、他のライブラリに依存しています。確かに、他のライブラリは非常に重要であり、私はそれらに触れたくありません。ここでは、もう少し単純ではありませんが、よりエレガントなハックが助けになります。DT_SONAME→DT_NEEDED
ELF形式の各ライブラリには、ライブラリに必要なすべてのリンカー情報を含むヘッダーがあります。彼女の依存関係と名前に関する情報に興味があります。これはすべて、たとえば次のコマンドを使用して学習できますobjdump -p
。 : [.....] STRTAB 0x00001660 STRSZ 0x000014ec GNU_HASH 0x00002b4c NEEDED liblog.so NEEDED libc++.so NEEDED libdl.so NEEDED libc.so NEEDED libm.so SONAME libcutils.so FINI_ARRAY 0x0000fbf0 [.....]
これがlibcutils.soヘッダースニペットの外観です。 NEEDEDフィールドには、libcutils自体と同じプロセスでリンカーによってロードされる依存関係が含まれています。ライブラリの名前を含むSONAMEフィールドに注意してください。リンカが通常の操作にそれを必要としないことは注目に値します;それはファイル名でライブラリを検索します。では、ELFヘッダーを解析して、この不要なフィールドの代わりに別の依存関係を挿入してみませんか?それほど重要ではないシステムライブラリから?ここで注意する必要があります。新しい依存関係の名前の長さは、ライブラリ自体の名前の長さを超えてはなりません(したがって、libcではなくlibcutilsを選択しました)。幸いなことに、優秀な候補があります:libmtp.so。大きく(数十キロバイト)、短い名前で、MTPプロトコルを使用してファイルを転送するためにUSB経由でコンピューターに接続する場合にのみ必要です。つまり、2分間、それなしで生きることができます。残りは技術の問題です。 ELFファイルを解析し、ライブラリ名を持つフィールドを見つけ、フィールドのタイプをDT_SONAME(0xE)からDT_NEEDED(0x1)に変更し、名前をlibmtp.soに変更します。できた!
Libcutils.soには新しい依存関係があります。 : [.....] STRTAB 0x00001660 STRSZ 0x000014ec GNU_HASH 0x00002b4c NEEDED liblog.so NEEDED libc++.so NEEDED libdl.so NEEDED libc.so NEEDED libm.so NEEDED libmtp.so FINI_ARRAY 0x0000fbf0 [.....]
これで問題は小さくなります:libmtp.soを自由裁量で書き直し、コードを実行するためにプロセスを再起動します。これも、zygoteをドロップすることで実行できます。これは、Androidアプリケーションだけでなく、多くのシステムサービスもそれに追随するためです。Android.Loki.28.originウイルスの作者に感謝する価値があります。この作者から、ライブラリに依存関係を実装することでこの素晴らしいアイデアを得ました。
結論の代わりに
AndroidのエクスプロイトがOSセキュリティの明白な脆弱性を使用していたため、免責で何でもできるようになりました。今日、Androidは十分に保護されており、バージョン4.3で導入され、5.0でデフォルトで有効化されたSELinuxは、保護に大きく貢献しました。それにもかかわらず、防御の抜け穴は依然として存在し、それらは依然として特権を増やして、それらを実際の目的で、そして悪のために使用できるようにします。Androidで最も見かけ上役に立たないエクスプロイトの1つがどのように有用なアプリケーションを見つけることができたかについて、あなたが興味を持っていたことを願っています。そして、タッチロガーの新しいバージョンが、最も価値のあるユーザーデータの車を収集し、タッチスクリーン上のジェスチャーの行動兆候の研究を前進させるのに役立つことを願っています。