Stack Canaries

HackTricksをサポートする

StackGuardとStackShield

StackGuardは、EIP (Extended Instruction Pointer) の前に特別な値であるキャナリーを挿入します。具体的には、バッファオーバーフローから保護するために 0x000aff0d(null、改行、EOF、復帰)を挿入します。ただし、recv()memcpy()read()bcopy()などの関数は脆弱のままであり、EBP (Base Pointer) を保護しません。

StackShieldは、グローバルリターンスタックを維持することで、StackGuardよりも洗練されたアプローチを取ります。このセットアップにより、オーバーフローが害を及ぼさないように、格納されたリターンアドレス(EIP)と実際のリターンアドレスを比較してオーバーフローの発生を検出できます。さらに、StackShieldはリターンアドレスを境界値と比較して、EIP が予想されるデータスペースの外を指しているかどうかを検出できます。ただし、この保護はReturn-to-libc、ROP(Return-Oriented Programming)、ret2retなどの手法によって回避できるため、StackShieldもローカル変数を保護しません。

スタックスマッシュプロテクタ(ProPolice)-fstack-protector:

このメカニズムは、EBP の前にキャナリーを配置し、ローカル変数を再配置してバッファをより高いメモリアドレスに配置し、他の変数を上書きするのを防ぎます。また、スタック上に渡された引数をローカル変数の上に配置し、これらのコピーを引数として使用します。ただし、8つ未満の要素を持つ配列やユーザー構造体内のバッファを保護しません。

キャナリーは、/dev/urandom から派生したランダムな数値またはデフォルト値の 0xff0a0000 から派生したランダムな数値です。これはTLS (Thread Local Storage) に保存され、スレッド間で共有されるメモリスペースを持つことができます。これらの変数は最初に親プロセスからコピーされ、子プロセスはデータを変更することなくそのデータを変更できます。ただし、新しいキャナリーを作成せずに fork() を使用すると、すべてのプロセス(親プrocessと子プロセス)が同じキャナリーを共有するため、脆弱になります。 i386 アーキテクチャでは、キャナリーは gs:0x14 に、x86_64 では fs:0x28 に保存されます。

このローカル保護は、攻撃に脆弱なバッファを持つ関数を特定し、これらの関数の先頭にコードを挿入してキャナリーを配置し、最後にその整合性を検証します。

Webサーバーが fork() を使用すると、キャナリーバイトを1バイトずつ推測するブルートフォース攻撃を有効にします。ただし、fork() の後に execve() を使用すると、メモリスペースが上書きされ、攻撃が無効になります。 vfork() は、書き込みを試みるまで子プロセスが複製されないようにすることで、プロセスの作成とメモリの処理に異なるアプローチを提供します。

長さ

x64 バイナリでは、キャナリークッキーは 0x8 バイトの qword です。最初の7バイトはランダムで、最後の1バイトは ヌルバイト です。

x86 バイナリでは、キャナリークッキーは 0x4 バイトの dword です。最初の3バイトはランダムで、最後の1バイトは ヌルバイト です。

両方のキャナリーの最も下位バイトはヌルバイトです。なぜなら、それが低いアドレスから来るスタック内の最初のバイトであり、したがって文字列を読み取る関数はそれを読み取る前に停止するからです。

バイパス

キャナリーを漏洩 してからそれを上書きする(例:バッファオーバーフロー)。

  • 子プロセスでキャナリーがフォークされている場合、1バイトずつ ブルートフォース できる可能性があります:

BF Forked & Threaded Stack Canaries
  • バイナリ内に興味深い 漏洩または任意の読み取り脆弱性 がある場合、それを漏洩する可能性があります:

Print Stack Canary
  • スタックに格納されたポインタを上書き

スタックがスタックオーバーフローに脆弱である場合、上書きできる文字列や関数へのアドレスが含まれている可能性があり、スタックキャナリーに到達する必要がなく脆弱性を悪用できるかもしれません。次を確認してください:

Pointer 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 のプレゼンテーションも参照してください。これにより、通常 TLSmmap によって保存され、スレッドスタック が作成されるときにも mmap によって生成されると述べられており、前述の解説に示されているようにオーバーフローが可能になるかもしれません。

  • __stack_chk_fail のGOTエントリを変更

バイナリがPartial RELROを持っている場合、GOTエントリの __stack_chk_fail を変更 して、キャナリーが変更されてもプログラムをブロックしないダミー関数にすることができます。

この攻撃は、次の解説で実行されます:https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/

参考文献

HackTricksのサポート

Last updated