検索
連載
Beyond Zero-day Attacks(2):

スタックに対する攻撃とその対策 (2/3)

攻撃手法を技術的に理解するための連載、第2回目はバッファーオーバーフローを狙う「スタック」への攻撃を解説します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena

GSオプションの導入(Visual C++ 2002)

 これに対してVisual C++ 2002では、GSオプションとしてGS Cookieを導入しました(図5)。GSオプションでは関数から戻り番地に戻る前に、GS Cookieの値をチェックし、値が変わっている場合には異常終了します。

 2003年にBlasterワームが感染を広げた際に、Windows XPは感染被害に遭いましたが、GSオプションでコンパイルされたWindows Server 2003は、同じ脆弱性があったにもかかわらず、GS Cookieによりに感染を防ぐことができました。


図5 GS Cookieによるバッファーオーバーフローの対策

 GS Cookieの具体的な動きについては、コードを見た方が分かりやすいと思いますので、マイクロソフトがMSDNで公開している、「Compiler Security Checks In Depth」の例をリスト2として紹介します。なお、リスト2はスタックにEBPをプッシュしていないので、図5とEIPの位置が4バイト違う点に注意してください。

/GSなし /GS (Visual Studio 2003)
関数の先頭 sub esp,20h sub esp,24h
mov eax,dword ptr [___security_cookie (408040h)]
xor eax,dword ptr [esp+24h]
mov dword ptr [esp+20h],eax
関数の最後 add esp,20h
ret
mov ecx,dword ptr [esp+20h]
xor ecx,dword ptr [esp+24h]
add esp,24h
jmp __security_check_cookie (4010B2h)"
リスト 2 GSオプションで生成されるコード

 それぞれの動きと違いは以下の通りです。

  • 関数の先頭
    • GSオプションがない場合は、単にローカル変数の領域を確保(espー20h)する
    • GSオプションを使っている場合は、GS Cookieをローカル変数の直後(esp+24h)にsecurity_cookieと、戻り番地(EIP)の値のXORを、GS Cookieとしてセットする
  • 関数の最後
    • GSオプションがない場合は、単にESPをローカル変数の分だけ戻してリターン
    • GSオプションでは、GS Cookieと戻り番地をXORし、ローカル変数の領域を戻したあと、チェックルーチンにジャンプする。チェックルーチンでは、GS Cookieと戻り番地(EIP)をXORした値が、security_cookieと一致すれば、戻り番地が変更されていないと判断し、制御を戻り番地に移す。一致しない場合は変更されていると判断し、異常終了する

 論理演算に慣れていない方に向けて、上記の演算をした結果をリスト3に記載しておきます。

0x00408040 xor 0x00123456 = 0x0052b416 戻り番地が、0x00123456の場合、GS Cookieは、0x0052b416になります。
0x0052b416 xor 0x00123456 = 0x00408040 戻り番地が同じ場合、GS Cookieと戻り番地のxorは、security_cookieと同じ値になります。
0x0052b416 xor 0x00444444 = 0x0016f052 戻り番地が違う場合、GS Cookieと戻り番地のxorは、security_cookieと違う値となります。
リスト 3 GS Cookieの判断に利用される論理演算(排他的論理和:XOR)

GSオプションの強化(Visual C++ 2005)

 GSオプションはBlasterの例のように、一定の効果がありますが、いくつかの回避策が考えられます(リスト4)。

void vulnerable(char *in, char *out) {
	char buf[256];
	strcpy(buf, in);  // overflow!
	strcpy(out, buf); // out is corrupt
	return; //canary checked
}
リスト 4 引数を上書きするバッファーオーバーフローの例

 リスト4ではまず、この関数を呼び出す際にパラメータ“in”の内容を調整することで、関数の引数として渡されたポインター“out”をバッファーオーバーフローで上書きすることができます(図6の(1))。そして、outの値をデータをターゲットアドレスで上書きすることで、inにセットしたデータを、ターゲットアドレスに書き込むことが可能です(ただし、リスト4のように単純な関数では、関数のパラメータはレジスターに読み込まれると考えられるので、成功する可能性は低いと思います)。

 この場合、関数内でデータの上書きが行われてしまうため、関数から戻る際にGS Cookieの変更を検知したとしても、既に攻撃が完了している可能性があります。

 また、GS Cookieの値を特定することができる場合、これをinにセットし(図6の(2))、任意のアドレスでEIPを書き換えることで、GS Cookieのチェックを回避し、任意のアドレスのコードを実行することも可能です。


図6

 Visual C++ 2005では、図6の(1)のようなパラメータを上書きする攻撃を防ぐため、引数をローカル変数としてコピーし、配列をGS Cookieの直前に配置するように変更されています。この配置にすることで、配列へのバッファーオーバーフローで、引数を書き換えることができないため、図6の(1)の攻撃を行うことができません。


図7 Visual Studio 2005における関数呼び出し時のメモリー配置

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る