为什么`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$
THE END
二维码