为什么`read()`系统调用的缓冲区溢出只在GDB中导致`EFAULT`?

短篇故事

我正在用 Assembly 编写一个简单的程序来模拟缓冲区溢出。缓冲区只是从 512 字节堆栈中分配的内存,然后read()使用来自 stdin fd 的 4096 字节调用系统调用。

当我在 GDB 之外执行有效负载时,缓冲区溢出工作正常。但是当我在 GDB 中时,系统调用read()返回EFAULT.

在这种情况下,我们的缓冲区溢出应该替换返回地址并使%rip到达secret_func


为什么在这种情况下缓冲区溢出在 GDB 中不起作用?


资源

代码测试.S

.section .rodata
str1:
    .ascii "Enter the input: "
str2:
    .ascii "nYou find a secret function!n"
str_end:

.section .text
.global _start

_start:
    xorl    %ebp, %ebp
    andq    $-16, %rsp
    callq   main
_exit:
    movl    %eax, %edi
    movl    $60, %eax
    syscall

main:
    subq    $512, %rsp

    movl    $1, %eax
    movl    $1, %edi
    leaq    str1(%rip), %rsi
    movl    $(str2 - str1), %edx
    syscall

    xorl    %eax, %eax
    xorl    %edi, %edi
    movq    %rsp, %rsi
    movl    $4096, %edx # Intentional to create buffer overflow
    syscall

    addq    $512, %rsp
    xorl    %eax, %eax
    retq

# We reach this function via buffer overflow (replace return address)
secret_func:
    movl    $1, %eax
    movl    $1, %edi
    leaq    str2(%rip), %rsi
    movl    $(str_end - str2), %edx
    syscall
    xorl    %eax, %eax
    jmp _exit

已编译 ELF 的 objdump

Disassembly of section .text:

0000000000401000 <_start>:
  401000:   31 ed                   xor    %ebp,%ebp
  401002:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  401006:   e8 09 00 00 00          call   401014 <main>

000000000040100b <_exit>:
  40100b:   89 c7                   mov    %eax,%edi
  40100d:   b8 3c 00 00 00          mov    $0x3c,%eax
  401012:   0f 05                   syscall 

0000000000401014 <main>:
  401014:   48 81 ec 00 02 00 00    sub    $0x200,%rsp
  40101b:   b8 01 00 00 00          mov    $0x1,%eax
  401020:   bf 01 00 00 00          mov    $0x1,%edi
  401025:   48 8d 35 d4 0f 00 00    lea    0xfd4(%rip),%rsi        # 402000 <str1>
  40102c:   ba 11 00 00 00          mov    $0x11,%edx
  401031:   0f 05                   syscall 
  401033:   31 c0                   xor    %eax,%eax
  401035:   31 ff                   xor    %edi,%edi
  401037:   48 89 e6                mov    %rsp,%rsi
  40103a:   ba 00 10 00 00          mov    $0x1000,%edx
  40103f:   0f 05                   syscall 
  401041:   48 81 c4 00 02 00 00    add    $0x200,%rsp
  401048:   31 c0                   xor    %eax,%eax
  40104a:   c3                      ret    

000000000040104b <secret_func>:
  40104b:   b8 01 00 00 00          mov    $0x1,%eax
  401050:   bf 01 00 00 00          mov    $0x1,%edi
  401055:   48 8d 35 b5 0f 00 00    lea    0xfb5(%rip),%rsi        # 402011 <str2>
  40105c:   ba 1d 00 00 00          mov    $0x1d,%edx
  401061:   0f 05                   syscall 
  401063:   31 c0                   xor    %eax,%eax
  401065:   eb a4                   jmp    40100b <_exit>

繁殖步骤

在没有 GDB 的情况下编译和运行(工作正常)

在这种情况下,我们计算返回地址的偏移量并将其替换为secret_func地址。

ammarfaizi2@integral:/tmp$ gcc -O3 -no-pie -static -nostartfiles -ffreestanding test.S -o test
ammarfaizi2@integral:/tmp$ perl -e 'print "A"x512,"x4bx10x40","x00"x5' > payload
ammarfaizi2@integral:/tmp$ ./test < payload
Enter the input: 
You find a secret function!
ammarfaizi2@integral:/tmp$ 

在 GDB 中编译并运行(read()返回 -14 (-EFAULT))

我们逐步执行read()系统调用,发现它返回 -14。它根本不从标准输入读取。

gef?  b main
Breakpoint 1 at 0x401014
gef?  r < input
[... GEF output elided ...]
gef?  si 11
[... GEF output elided ...]
gef?  x/5i $rip
=> 0x401041 <main+45>:          add    $0x200,%rsp
   0x401048 <main+52>:          xor    %eax,%eax
   0x40104a <main+54>:          ret    
   0x40104b <secret_func>:      mov    $0x1,%eax
   0x401050 <secret_func+5>:    mov    $0x1,%edi
gef?  p/d $rax
$2 = -14
gef?  shell errno 14
EFAULT 14 Bad address
gef?  

GDB 和 Linux 版本

ammarfaizi2@integral:/tmp$ gdb --version
GNU gdb (Ubuntu 10.1-2ubuntu2) 10.1.90.20210411-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
ammarfaizi2@integral:/tmp$ uname -r
5.13.0-rc2-fresh-tea-00005-g8ac91e6c6033
ammarfaizi2@integral:/tmp$ 

以上是为什么`read()`系统调用的缓冲区溢出只在GDB中导致`EFAULT`?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>