ASLRは、プロセスの実行可能コード、スタック、ヒープ、およびライブラリのアドレスをランダム化します。これは、複数のインスタンス化にわたってコード内のアドレスをハードコードできないため、攻撃者の生活を困難にするためです。
しかし、これはシステムライブラリにどのように役立ちますか?それらは決して荷を下されません。たとえば、すべてのプロセスはlibcを使用するため、prinfのアドレスは変更されません。 printfのアドレスをハードコーディングしている場合、これはどのように役立ちますか?マシンが再起動しない限り、これは変わりません。
ASLRを有効にしています-
# cat /proc/sys/kernel/randomize_va_space
2
ここにいくつかのサンプルコードがあります-
unsigned long getEBP ( void )
{
asm("movl %ebp, %eax");
}
int main(void)
{
int (*p)(const char*, ...) = &printf;
printf("printf address = %p \n", p);
(*p)("printf address = %p\n", &printf);
printf ("EBP:%x\n" ,getEBP ());
}
そして、複数の実行にわたる出力-
# ./a.out
printf address = 0x4003c0
printf address = 0x4003c0
EBP:6a71d300
# ./a.out
printf address = 0x4003c0
printf address = 0x4003c0
EBP:93e5c100
EBPは変更されているはずですが、printfのアドレスは変更されていません。
何が欠けていますか?
編集:-fPICを使用したコンパイルは、RHEL VMでは役に立ちませんでした。
# ./a.out
printf address = 0x3047a4f0f0
printf address = 0x3047a4f0f0
EBP:7aaac900
# ./a.out
printf address = 0x3047a4f0f0
printf address = 0x3047a4f0f0
EBP:632eca20
Libc呼び出しが実際にランダム化され、応答が以下のようになっている場合、どのように実装されますか? libc自体はリロードされないため、printfの実際のアドレスは変更されません。どうすればランダム化できますか?
更新:
を使用してバイナリをコンパイルします -fPIE
または-fPIC
-pie -fPIE
:
https://stackoverflow.com/questions/2463150/fpie-position-independent-executable-option-gcc-ld
デフォルトのフラグでコンパイルされたバイナリ:
user01@user01 ~/test $ ./test_ASLR
printf address = 0x400420
printf address = 0x400420
EBP:9af703c0
user01@user01 ~/test $ ./test_ASLR
printf address = 0x400420
printf address = 0x400420
EBP:8411e900
user01@user01 ~/test $ ./test_ASLR
printf address = 0x400420
printf address = 0x400420
EBP:28f8e50
-pie -fPIE
フラグでコンパイルされたバイナリ:
user01@user01 ~/test $ ./test_ASLR
printf address = 0x7f8227963340
printf address = 0x7f8227963340
EBP:19e01ad0
user01@user01 ~/test $ ./test_ASLR
printf address = 0x7fecb2baa340
printf address = 0x7fecb2baa340
EBP:9c8148e0
user01@user01 ~/test $ ./test_ASLR
printf address = 0x7f5d00edb340
printf address = 0x7f5d00edb340
EBP:32b3c6d0
デフォルトのフラグを使用してコンパイルする場合、ほとんどの場合、.text
セグメントは定数アドレスに留まり、GOTはプログラムで使用される関数のポインターのリストで満たされます。これらのポインターは、遭遇したときに解決されます。この場合、GOTは一定の場所にあったため、コンパイラによってcall
に直接配置されます。例 :
0x8048446 <main+35> call printf@plt <0x80482f0>
これは、printfの実際の場所がランダム化されなかったことを意味しません。これがプログラムの最初のprintfの呼び出しだった場合、_dl_runtime_resolve
は適切な値を入力します。それ以外の場合は、次のようになります。
pwndbg> x/xi 0x80482f0
0x80482f0 <printf@plt>: jmp DWORD PTR ds:0x804a00c
pwndbg> telescope 0x804a00c
00:0000│ 0x804a00c (_GLOBAL_OFFSET_TABLE_+12) —▸ 0xf7e38670 (printf) ◂— call 0xf7f0eb09
これはlibc内のprintf
の実際の場所です。通常の場合、&printf
は実際にはprintf@plt
を指します。これはlibc内の実際のprintf
の単なるジャンプテーブルです。
-pie -fPIE
の場合、&printf
はlibcの実際のprintfを指します。このフラグは、以前は一定であったセクションにASLRを追加するだけです。どちらの場合も、ライブラリはランダムなオフセットでロードされました。