Description

Category: Pwnable

Source: DEFCON CTF 2019 Qulas.

Points: 5

Author: Jisoon Park(js00n.park)

Description:

2 Fast 2 Furious

speedrun-002.quals2019.oooverflow.io 31337

Files: speedrun-002

Write-up

주어진 파일을 decompile 한 후 main() 함수부터 살펴보자. (함수 이름은 직접 지정하였다.)

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  setvbuf(stdout, 0LL, 2, 0LL);
  if ( !getenv("DEBUG") )
    alarm(5u);
  welcome();
  attackme();
  bye();
  return 0LL;
}

int welcome()
{
  return puts("We meet again on these pwning streets.");
}

int attackme()
{
  int result; // eax
  char buf; // [rsp+0h] [rbp-590h]
  char v2; // [rsp+190h] [rbp-400h]

  puts("What say you now?");
  read(0, &buf, 0x12CuLL);
  if ( !strncmp(&buf, "Everything intelligent is so boring.", 0x24uLL) )
    result = bof(&v2);
  else
    result = puts("What a ho-hum thing to say.");
  return result;
}

int bye()
{
  return puts("Fare thee well.");
}

ssize_t __fastcall bof(void *a1)
{
  puts("What an interesting thing to say.\nTell me more.");
  read(0, a1, 0x7DAuLL);
  return write(1, "Fascinating.\n", 0xDuLL);
}

attackme() 함수를 보면 처음으로 입력받은 문자열이 의도한 문자열인지 확인한 후 0x400 byte 크기를 갖는 v2 변수를 인자로 하여 bof() 함수를 호출한다.

bof() 함수는 최대 0x7da 만큼의 입력을 받으므로, 여기서 bof 취약점이 발생하는 것을 알 수 있다.

별다른 추가적인 취약점이 없는 상황에서 이 문제를 풀려면 우선 puts 함수의 존재 여부와(존재한다.) PIE가 적용되지 않아 항상 동일한 주소값이 유지되는지를 확인해봐야 한다.

다행히 PIE가 적용되지 않았다. 그렇다면 LIBC 주소를 leak 한 후, ROP(return oriented programming)을 활용한 RTL(return to LIBC) 공격이 가능하다.

간단히 objdump를 활용해서 puts의 plt 주소와 got 주소를 확인해보자.

0x4005b0 번지로 점프하면 puts를 실행할 수 있고, LIBC 상의 puts 주소는 0x60128 번지에 저장되는 것을 확인할 수 있다.

64bit 환경이니 rdi에 값을 넣어줄 수 있는 widget을 찾아보면 0x4008a3 번지에 있는 것을 알 수 있다.

이제 rbp + 8 번지에 0x4008a3, 0x60128, 0x4005b0, 0x40074c(attack() 함수의 주소)를 순서대로 넣어주면 attack() 함수의 종료 시점에서 rop chain에 따라 LIBC 상의 puts() 함수의 주소가 출력되고 (puts을 이용하여 출력하였으므로 little endian 형식으로 출력된다.) 다시 attack() 함수가 실행될 것이다.

출력된 puts() 함수의 주소의 마지막 12 bit을 확인해 보면 0x9c0인 것을 알 수 있다. (ASLR이 적용될 때 ..fff000 mask가 적용된다.)

에서 puts의 주소가 0x9c0인 64bit libc를 검색해보면 libc6_2.27-3ubuntu1_amd64를 찾을 수 있고, 이로부터 libc의 puts offset을 이용하여 libc가 로드된 주소를 계산할 수 있다.

libc 안에 "/bin/sh" 문자열도 있고, execv의 offset도 알 수 있으므로, execve("/bin/sh", 0, 0)이 실행되도록 rop chain을 작성하면 shell을 얻을 수 있다. (exploit)

(이유는 모르겠으나 system() 함수로는 shell을 얻을 수 없었다...)

Flag : **OOO{I_didn't know p1zzA places__mAde pwners.}**

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

nothing more to say  (0) 2019.11.26
2 small 2 Pwn  (0) 2019.11.26
horcruxes  (0) 2019.11.26
shellql  (0) 2019.11.26
xor  (0) 2019.11.26

+ Recent posts