View
217
Download
1
Embed Size (px)
Citation preview
スタック保護システム : (propolice 、 StackGuard 、XP SP2)
江藤 博明日本アイ・ビー・エム株式会社、東京基礎研究所
目次
スタックのバッファ オーバーフロー スタックスマッシング攻撃とは? スタック プロテクタ ランドスケープ Stack Guard propolice Windows XP SP2 (/Gs option) 比較 まとめ
スタックのバッファ オーバーフローとは? バッファ オーバーフローは割り当てられたメモ
リ領域(バッファ)を超える量のビットを入力しようとすると発生する
以上が起こった場合、次の連続したメモリの塊が上書きされてしまう
戻りアドレス 関数ポインタ 前回のフレームポインタなど
また攻撃コードも挿入される これらは深刻なセキュリティ問題に繋がる
スタックレイアウトおよび攻撃により影響を受けたメモリ --- 関数 foo が bar によって呼び出される場合
int foo (void (*funcp)()) { char* ptr = point_to_an_array; char buf[128]; gets (buf); strncpy(ptr, buf, 8); (*funcp)();}
文字列増加
スタック増加
int bar (int val1) { int val2; foo (a_function_pointer);}
影響を受けたメモリ
最も攻撃を受けやすいターゲッ
ト
val1
val2
arguments (funcp)
return address
Previous Frame Pointer
pointer var (ptr)
buffer (buf)
攻撃シナリオ #1--- 戻りアドレスの変更
args (funcp)
return address
PFP
pointer var (ptr)
buffer (buf)
攻撃コード
① 戻りアドレスを、攻撃コードをポイントするように変更する。関数が戻った後、攻撃コードに導く。
② コードセグメント内に存在する命令はsystem() や exec() などのような攻撃コードとして使用できる。
①
② これらのポインタをスタックにセットする
“/bin/sh”
system()
攻撃シナリオ #2--- ポインタ変数の変更
args (funcp)
return address
PFP
pointer var (ptr)
Buffer (buf)
攻撃コード
① 関数ポインタを攻撃コードをポイントするよう変える。続く関数ポインタ呼び出しは攻撃コードへ導く。
② あらゆるメモリが、たとえスタック内になくても、影響を受けたポインタに値を保存する命令文によって変更されうる。
E.g. strncpy(ptr, buf, 8); *ptr = 0;
関数ポインタ
グローバル オフセット テーブル
①
②
攻撃シナリオ #3--- 前回のフレームポインタの変更
args (funcp)
return address
PFP
pointer var (ptr)
Buffer (buf)
攻撃コード
① 呼び出し側フレームを近くの場所に変更する。フレームには影響を受けた戻りアドレスが含まれている。
return address
PFP
スタック プロテクタ ランドスケープ コンパイラベースのプロテクタ
StackGuard 、 stack shield 、 propolice 、 XP SP2 /Gs
ランタイムスタック信頼性チェッカー Libsafe
アドレス空間の非実行可能な部分 Solar Designer の 「 non-exec stack patch 」、 Exec
Shield 、 OpenBSD の W^X 、 XP SP2 NX
単独のソリューションでは解決不可能 !!!
Stack Guard
StackGuard ではスタックの戻りアドレスの直前に「カナリア」と呼ばれる値 を挿入する。
関数を実行後、プロテクションツールは、「カナリア」が戻りアドレスにジャンプする前に修正されていないかをチェックする。
「カナリア」の値が不正に修正されている場合、プログラムは終了する。
脆弱性報告 “BYPASSING STACKGUARD AND STACKSHIELD ( STACKGU
ARD および STACKSHIELD の回避法)” , Phrack 56 “Four different tricks to bypass StackShield and StackGuard protec
tion ( StackShield および StackGuard 防御を回避する4つの方法)”
Stack Guard 2.1 様々な「カナリア」の値
ターミネーター カナリア 0x000aff0d ランダム カナリア random XOR カナリア random ^ return address
コンパイラを構築する際に、「カナリア」メソッドを選ぶ
args
return address
canary
PFP
Local variables including arrays
文字列増加
スタック増加
データのランダムな値
mprotect はこのデータへの書き込みアクセスを禁止する
開発中の Stack Guard
フレームポインタ問題を解決するために、「カナリア」を移動
戻りアドレス、 フレームポインタ、ローカル変数の広範的な完全性チェック
args
return address
PFP
canary
Local variables including arrays
XOR
データのランダムな値
文字列増加
スタック増加
propolice: 設計目標
“Safe Stack Usage Model” の導入 これは、理想的なスタックレイアウトとスタッ
クの完全性をチェックする手段を組み合わせたものである。
プログラムをできるだけ理想的なスタックレイアウトに近づけるよう変換する GNU gcc コンパイラのパッチはプログラムを変
換するコンパイル過程を追加する
Safe Stack Usage Model スタックの完全性チェック :
関数のプロローグで、 guard に予想不可能な値をいれる 関数のエピローグで、 guard 値の完全性を確かめる、もしくはプログラ
ムの実行を中止する 理想的スタックレイアウト :
A 配列もポインタ変数もなし B 配列のみ C 配列なし、ポインタ変数あり
args
return address
PFP
guard
arrays
Local variables
A
BC
オーバーフローによって影響を受け
ない
文字列増加
スタック増加
呼び出し側の関数がスタックスマッシング攻撃に対し安全である理由
args
return address
PFP
guard
arrays
Local variables
A
BC
関数がアクセスできる範囲
関数がアクセスできる範囲である args から guard まではポインタ変数がない。よってメモリはポインタ攻撃の影響を受けない。
関数が呼び出し側の関数に戻った場合、呼び出し側の関数の連続するメモリの塊はバッファ オーバーフローによる影響をうけない。
スタック増加
文字列増加
わかりやすい説明 : PFP と配列の間に「 guard 」 を構築する方法
foo () {
char *p;
char buf[128];
gets (buf);
}
Int32 random_number;
foo () {
volatile int32 guard;
char buf[128];
char *p;
guard = random_number;
gets (buf);
if (guard != random_number)
/* program halts */
}
1. 「 guard 」と呼ばれる値の挿入
2. ローカル変数の位置を入れ替える+ オプティマイザは random_number への二
度目のアクセスを削除する。- 配置されたバッファ alloca は guard の隣
に位置を変更できない。
わかりやすい説明 : ポインタタイプがある場合の関数引数の扱い方
foo (int a, void (*fn)()) {
char buf[128];
gets (buf);
(*fn)();
}
Int32 random_number;
foo (int a, void (*fn)()) {
volatile int32 guard;
char buf[128];
(void *safefn)() = fn;
guard = random_number;
gets (buf);
(*safefn)();
if (guard != random_number)
/* program halts */
}
1. ポインタを C領域から指定された変数にコピーする。実際、それはその変数にレジスタを指定しようとする。
2. 指定された変数で関数呼び出しの名前を変更する
propolice: スタック プロテクタ オプション -fstack-protector
スタック保護は関数がバイト配列の時のみ生成される。
-fstack-protector-all 常に「 guard 」を作成する。 バイト配列が使用されていれば、「 guard 」の隣
に指定される。 そうでなければ、配列が「 guard 」の隣に指定さ
れる。
propolice の現状http://www.research.ibm.com/trl/projects/security/ssp/ 実際の使用
Laser5, trusted debian, openbsd, gentoo など 対応アーキテクチャ
Ix86, powerpc, alpha, sparc, mips, vax, m68k, amd64
Gcc バージョン gcc2.95.3 – gcc3.4.1 gcc HEAD cvs 開発中
Microsoft XP SP2 --- Windows 2003 スタック保護 非実行スタック コンパイラ /Gs オプション
xor カナリアと propolice のコンビネーションメソッド
理想的なスタックレイアウトとはほど遠い 脆弱性報告
David Litchfield 氏、 “ Defeating the stack based buffer overflow prevention mechanism of Microsoft Windows 2003 server ( Microsoft Windows 2003 サーバにおけるスタックベースのバッファ オーバーフロー防御メカニズムを破る)”
Gs オプションがどのように機能するか カナリアは最初に配置されたバイト配列の前に挿入
される。 配列以外のローカル変数はスタック内でアルファベ
ット順に指定されるようである。
args
return address
PFP
Local variables
including arraysXOR
データのランダムな値
Stack point register
canary
First byte array
文字列増加
スタック増加
防御法の比較表 --- 防御レベル
StackGuard
2.1/3
MS /Gs propolice propolicestack-protector-all
バッファ オーバーフロー 適用可 不可 不可 適用可戻りアドレス 検知 検知 検知 検知
PFP 不可 / 検知 検知 検知 検知ローカル変数のポインタ 不可 / 検知 検知 検知 検知
Args のポインタ 不可 / 検知 検知 防御 防御関数ポインタ 不可 不可 防御 防御
ポインタによる修正 不可 不可 防御 防御
検知 : 関数の終わりに修正がみられる。防御 : 修正されない。
パフォーマンスに関して考慮すべき点SG/tc SG/rc SG/xc SG/3 MS/Gs propolice propolice/
all
全関数保護 yes yes yes yes no no yes
オーバーフローが検知されない時実行される追加命令の数Mem load 1 3 5 5 - 3 2 – 3 2 – 3
Mem save 1 1 1 1 1 1 1
その他 2 2 4 4 - 4 2 2
実験基準 (実行オーバーヘッド :%)
Ctag - 3 - - - 1 -
Perl - 8 - - - 4 -
ここで示すオーバーヘッド率は、すべての OS のデフォルトでこれを有効にすることを十分なものにする
まとめ
スタックオーバーフロー問題の説明 様々なスタックスマッシング攻撃の説明 StackGuard, propolice, および MS/Gs の特性
を説明 さまざまな観点によるそれぞれの防御法の比
較