次の脆弱なコード/プログラムを検討してください。
#include <string.h>
int main(int argc, char *argv[]) {
char buf[16];
strcpy(buf, argv[1]);
return 0;
}
NX と ASLR が有効になっている Linux を実行している IA-32 (x86、32 ビット) では、基本的に次の手順を含む GOT 上書き手法を使用してこれを悪用します。
- RIP までのオーバーフロー バッファ
- のアドレスで RIP を上書きします。
strcpy@plt
.text
からのクリーンなガジェットを使用してくださいpop edi ; pop ebp ; ret
。strcpy
- 次の引数を書き込みます
strcpy
:&bss
-address を宛先として、1 バイトを/bin/sh
使用して.text
/bin/sh
が完全に書き込まれるまで、手順 2 ~ 4 を繰り返します。&bss
strcpy
withの GOT エントリを上書きしsystem
ます (オフセットを使用するため、Libc の使用バージョンに関する知識が必要です - ここでは無視しましょう)- スタックに書き込み
strcpy@plt
、続いて 4 バイトのチャンク、最後にどのアドレス&bss
を指すか/bin/sh
- 利益
同じ緩和策を有効にして x86-64 でこれを悪用したいと考えています。しかしこれは想像以上に難しい。基本的に次の理由によります。
- x86-64 レジスタ ベースの呼び出し規約: 関数の引数は、スタックではなくレジスタを使用して渡されます。したがって、引数をスタックから適切なレジスタに転送するには、いくつかの追加の ROP ガジェットが必要です。これは小さな問題ですが、次の問題の影響も受けます。
64 ビットの戻りアドレス: x86-64 の RIP は、
.text
32 ビット長でさえありません。したがって、関数呼び出しを連鎖させるには、スタックに NULL バイトを書き込む必要があります。基本的には、連鎖呼び出しを使用してstrcpy
、NULL 終了文字strcpy
が常に書き込むことを利用して、必要なだけ NULL バイトを書き込むことができます。strcpy
ただし、RIP の最下位バイトを上書きするだけで、1 回だけ呼び出すことができます。|0x00000000| (most significant bytes) |0x00deadbe| <- RIP (least significant bytes) |0x41414141| |0x41414141| <- SFP | ... |
これらは、NX と ASLR が有効になっている x86-64 でプログラムを悪用したときに発生した主な問題です。これらの問題を解決するテクニックはありますか? それとも、x86-64 は、実際に機能するシェルを開くエクスプロイトを防げるのでしょうか?