スタックに対する攻撃とその対策:Beyond Zero-day Attacks(2)(2/3 ページ)
攻撃手法を技術的に理解するための連載、第2回目はバッファーオーバーフローを狙う「スタック」への攻撃を解説します。
GSオプションの導入(Visual C++ 2002)
これに対してVisual C++ 2002では、GSオプションとしてGS Cookieを導入しました(図5)。GSオプションでは関数から戻り番地に戻る前に、GS Cookieの値をチェックし、値が変わっている場合には異常終了します。
2003年にBlasterワームが感染を広げた際に、Windows XPは感染被害に遭いましたが、GSオプションでコンパイルされたWindows Server 2003は、同じ脆弱性があったにもかかわらず、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ではまず、この関数を呼び出す際にパラメータ“in”の内容を調整することで、関数の引数として渡されたポインター“out”をバッファーオーバーフローで上書きすることができます(図6の(1))。そして、outの値をデータをターゲットアドレスで上書きすることで、inにセットしたデータを、ターゲットアドレスに書き込むことが可能です(ただし、リスト4のように単純な関数では、関数のパラメータはレジスターに読み込まれると考えられるので、成功する可能性は低いと思います)。
この場合、関数内でデータの上書きが行われてしまうため、関数から戻る際にGS Cookieの変更を検知したとしても、既に攻撃が完了している可能性があります。
また、GS Cookieの値を特定することができる場合、これをinにセットし(図6の(2))、任意のアドレスでEIPを書き換えることで、GS Cookieのチェックを回避し、任意のアドレスのコードを実行することも可能です。
Visual C++ 2005では、図6の(1)のようなパラメータを上書きする攻撃を防ぐため、引数をローカル変数としてコピーし、配列をGS Cookieの直前に配置するように変更されています。この配置にすることで、配列へのバッファーオーバーフローで、引数を書き換えることができないため、図6の(1)の攻撃を行うことができません。
Copyright © ITmedia, Inc. All Rights Reserved.