Stack Canaries
StackGuardとStackShield
StackGuardは、EIP(Extended Instruction Pointer)の前に特別な値であるキャナリーを挿入します。具体的には、バッファオーバーフローから保護するために0x000aff0d
(null、改行、EOF、復帰)を挿入します。ただし、recv()
、memcpy()
、read()
、bcopy()
などの関数は脆弱のままであり、**EBP(ベースポインタ)**を保護しません。
StackShieldは、グローバルリターンスタックを維持することで、StackGuardよりも洗練されたアプローチを取ります。このセットアップにより、オーバーフローが害を及ぼさないように、格納された実際のリターンアドレスと比較してオーバーフローの発生を検出するためのリターンアドレス(EIP)をすべて格納します。さらに、StackShieldはリターンアドレスを境界値と比較して、EIPが予想されるデータスペースの外を指しているかどうかを検出できます。ただし、この保護はReturn-to-libc、ROP(Return-Oriented Programming)、ret2retなどのテクニックによって回避できるため、StackShieldもローカル変数を保護しません。
スタックスマッシュプロテクタ(ProPolice)-fstack-protector
:
-fstack-protector
:このメカニズムは、EBPの前にキャナリーを配置し、ローカル変数を再配置してバッファをより高いメモリアドレスに配置し、他の変数を上書きしないようにします。また、スタック上に渡された引数をローカル変数の上に配置し、これらのコピーを引数として使用します。ただし、8つ未満の要素を持つ配列やユーザー構造体内のバッファを保護しません。
キャナリーは、/dev/urandom
から派生したランダムな数値またはデフォルト値の0xff0a0000
から取得されます。これは**TLS(スレッドローカルストレージ)に保存され、スレッド間で共有されるメモリスペースを持つことができます。これらの変数は最初に親プロセスからコピーされ、子プロセスは親や兄弟に影響を与えることなくデータを変更できます。ただし、新しいキャナリーを作成せずにfork()
**を使用すると、すべてのプロセス(親と子供)が同じキャナリーを共有するため、脆弱になります。i386アーキテクチャでは、キャナリーはgs:0x14
に、x86_64ではfs:0x28
に保存されます。
このローカル保護は、攻撃に脆弱なバッファを持つ関数を特定し、これらの関数の先頭にキャナリーを配置し、最後にその整合性を検証するためのコードを挿入します。
Webサーバーがfork()
を使用すると、キャナリーバイトを1バイトずつ推測するブルートフォース攻撃を有効にします。ただし、fork()
の後にexecve()
を使用すると、メモリスペースが上書きされ、攻撃が無効になります。vfork()
は、書き込みを試みるまで子プロセスが複製されずに実行できるようにするため、プロセスの作成とメモリの処理に異なるアプローチを提供します。
長さ
x64
バイナリでは、キャナリークッキーは**0x8
バイトのqwordです。最初の7バイトはランダムで、最後のバイトはヌルバイト**です。
x86
バイナリでは、キャナリークッキーは**0x4
バイトのdwordです。最初の3バイトはランダムで、最後のバイトはヌルバイト**です。
両方のキャナリーの最下位バイトはヌルバイトです。なぜなら、それが低いアドレスから来るスタック内の最初のバイトであり、したがって文字列を読み取る関数はそれを読み取る前に停止するからです。
バイパス
キャナリーを漏洩してからそれを上書きする(例:バッファオーバーフロー)。
子プロセスでフォークされた場合、1バイトずつブルートフォースできる可能性があります:
バイナリ内に興味深い漏洩または任意の読み取り脆弱性がある場合、漏洩する可能性があります:
スタックに格納されたポインタを上書き
スタックがスタックオーバーフローに脆弱である場合、上書きできる文字列や関数へのアドレスが含まれている可能性があり、スタックキャナリーに到達する必要がなく脆弱性を悪用できます。次を確認してください:
pagePointer Redirectingマスターキャナリーとスレッドキャナリーの両方を変更
キャナリーで保護されたスレッド関数内のバッファオーバーフローを使用して、スレッドのマスターキャナリーを変更できます。その結果、チェックには2つの同じキャナリーが使用されるため、対策は無効になります(変更された場合でも)。
さらに、キャナリーで保護されたスレッド関数内のバッファオーバーフローを使用して、TLSに保存されたスレッドのマスターキャナリーを変更できます。これは、スレッドのスタック内のBOFを介してTLSが保存されているメモリ位置に到達できる可能性があるためです。 その結果、チェックには2つの同じキャナリーが使用されるため、対策は無効になります(変更された場合でも)。 この攻撃は、次の解説で実行されます:http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
また、https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015のプレゼンテーションもチェックしてください。これにより、通常TLSは**mmap
によって保存され、スレッドのスタック**が作成されるときにもmmap
によって生成されると述べられており、前述の解説に示されているようにオーバーフローが可能になるかもしれません。
__stack_chk_fail
のGOTエントリを変更
バイナリがPartial RELROを持っている場合、__stack_chk_fail
のGOTエントリを変更するための任意の書き込みを使用して、プログラムをブロックしないダミー関数に変更できます。
この攻撃は、次の解説で実行されます:https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/
参考文献
Last updated