デスクトップLinuxでの特権のエスカレーション:GUIアプリケーションからのルートアクセスの取得

数か月前、Rafal Wojtczukは、Xサーバーにアクセスできる非特権プロセス(つまり、通常のユーザーとして実行されているGUIアプリケーション)からスーパーユーザー権限を取得できる深刻な悪用を思い付きました。 つまり、GUIプログラム(PDFファイルのリーダーなど)が侵害された場合(特別に準備されたPDFファイルなど)、コンピューターの完全な所有権に対するすべてのセキュリティ障壁を突破できます。 SElinuxサンドボックス( SElinux“ sandbox -X” )でも保存されません。 そして、問題は長年にわたって存在しました-明らかに、2.6カーネルの最初のバージョンから。



この脆弱性のレビューは8月17日に[2]でリリースされました。簡単に、時には拡張された形で、場所についてお話したいと思います。



仕組み





この不名誉の主な理由は長い間知られていた[3] 。 問題は、特にLinuxのメモリ割り当てアルゴリズムです。 誰もが知っているように、プロセスが動的メモリを取得できる主な場所は2つあります。スタックとヒープです。 また、物理メモリが対応していないアドレス空間を割り当てることもできます-これには、プロセスの仮想メモリのファイルシステム内のファイルへのマッピング(mmap)、または単に抽象カーネルオブジェクトへのマッピング(System-Vスタイルの共有メモリ)があります。 少なくとも書き込みを開始する瞬間まで、割り当てられたメモリが物理的に存在する必要さえありません。





x86-32のメモリ割り当て、[3]からの写真。 x86-64では、すべてが非常によく似ており、スケールが大きいだけです:)



スタックは、メモリを割り当てる最も簡単な方法です。 必要に応じて、最上位アドレスから最下位アドレスに成長し、スタックの次のページがなくなったことが突然判明した場合、そこに書き込もうとするとプロセッサがページフォールトをスローし、カーネルはスタックにさらに数ページを割り当てます(または割り当てに失敗した場合はプロセスを強制終了します) 。 最初に、スタックは(x86-32で)デフォルトで128 MBのアドレス空間の上部にメモリを割り当てますが、実際にはこのメモリは必要に応じて割り当てられ、突然すべてが他の場所でビジーになると、malloc()およびmmap()が使用を開始します「スタック」メモリ。



アグレッシブな割り当ての結果、割り当てられたセクションの1つがスタックの下限に隣接していることが判明する場合があります(これは悪用されたプロパティです)。 ここで、深く再帰的な関数を呼び出したとします。 スタックポインターは境界線に近づき始め、最終的に境界線を越えます...私たちが割り当てたメモリはスタックのすぐ後ろから始まるため、何も起こりません。 したがって、スタックの一部があります。これは実際にはスタックではなく、別の方法で割り当てられた部分です。



すべてが1つのプロセスの境界内で行われましたが、できることは、このプロセスを台無しにして、起動元のユーザーアカウントで何か悪いことをすることです。



プロセスから抜け出し、Xサーバーにアクセスする方法



Xサーバープロセス(同じ/ usr / X11 / bin / X)は、ルート権限で動作します。 そして、このプロセスのコンテキストでコードを実行する方法を見つけた場合、意図した目標を達成します。



Xサーバーのアドレス空間でメモリを取得するには、 MIT-SHM拡張機能を使用します。 一般に、Xサーバーがクライアントプログラムと交換するプロトコルは大きくて重く、さまざまな目的でさまざまな時期に発明された多数の拡張機能が含まれています。 特に、MIT-SHMは非常に単純な問題を解決します。 画面に大きなビットマップ画像を表示する必要があるとします。 これを行うには、クライアントプロセスからサーバーに、このラスター(pixmap)を構成する大量のバイトを送信する必要があります。 ただし、クライアントとサーバーが同じマシン上に存在する場合、これらのすべてのバイトをカーネルにコピーするためにリソースを費やす必要はなく、その逆も同様です。代わりに、両方のプロセスの仮想メモリにマップされる共有メモリの一部を割り当てることができます-これはMIT-SHMが行うことです



目的のためにこのAPIを暗黙のうちに使用し、Xサーバーの利用可能なすべてのメモリをアドレス空間にほぼ次のようにマッピングできます。

shm_seg_size=shmmax

while shm_seg_size >= PAGE_SIZE

shm_seg=shmget(..., shm_seg_size,...)

XShmAttach(..., shm_seg...)

, shm_seg_size/=2







(shmmaxはshmgetに割り当て可能な最大メモリサイズで、/ proc / sys / kernel / shmmaxにあります)



すべてが正常であれば、割り当てられたセクションの1つがスタックスペースの外にあることがわかりますが、サーバー側の共有メモリアドレスは私たちのものとは完全に異なるため、どのセクションかはまだわかりません。 ちょっとしたことを見つけるために、Xプロトコルの通常の手段でウィジェットからピラミッドを積み上げて、再帰的なトラバースを引き起こすコマンドを送信できます(記事[2]で、再帰的に機能する関数Fについて説明されています。 Xソース;))。 その結果、プロセスはスタックからポップされますが、XShmAttachによって次のアドレスが既に割り当てられているため、誰も気付かないでしょう。 次に、メモリを調べて、ゼロ以外のバイトがあるセクションを見つけます(shmgetは選択したセクションをゼロで詰まらせます)。



そして再び、悪名高い再帰関数Fを起動し、それが機能する間、スタックに好きなもの、つまり新しい戻りアドレスを書き込みます。 ここでは、 return-to-libcなどの従来のエクスプロイトテクノロジーを既に適用できます。主なことは、関数の実行中にスタックを時間通りに置き換えることです(はい、 ASLRについて知っていますが、この記事の一部として、何らかの方法でそれを無効にしたと仮定します;) )



そして今何をすべきか?



まあ、まず、更新を待つことができます。 Linus Torvalds(個人的に!)スタックとヒープおよびmmapで使用可能なアドレス空間との間の未割り当てページからバリアを作成するパッチ追加しました 。 その結果、スタックに十分なメモリがない場合、プログラムは存在しないアドレスへの書き込みを試み、SIGBUSに分類しようとします(もちろん、これもあまり良くありませんが、特権をエスカレートするよりも明らかに優れています)。 カーネル2.6.35.2および2.6.34.4では、この修正はすでに導入されています。



何らかの理由でカーネルを変更したくない場合は、xorg.confファイルでMIT-SHMをオフにできます。



Section "Extensions"

Option "MIT-SHM" "disable"

EndSection







確かに、これは同じ脆弱性の他の可能なバリエーションからは保存されません。



参照資料






All Articles