mirror of
https://github.com/Matir/skel.git
synced 2026-05-26 13:35:42 -07:00
2983 lines
110 KiB
Python
2983 lines
110 KiB
Python
#!/usr/bin/env python
|
|
|
|
# Copyright 2015 ant4g0nist
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import os
|
|
import re
|
|
import cmd
|
|
import sys
|
|
import time
|
|
import shlex
|
|
import random
|
|
import string
|
|
import struct
|
|
import commands
|
|
import datetime
|
|
import optparse
|
|
import argparse
|
|
import platform
|
|
import httplib
|
|
from struct import *
|
|
from sys import version_info
|
|
from struct import pack
|
|
from binascii import *
|
|
from ctypes import *
|
|
import subprocess
|
|
|
|
|
|
#install package
|
|
def install(name):
|
|
subprocess.call(['sudo','pip', 'install', name])
|
|
|
|
try:
|
|
from capstone import *
|
|
except:
|
|
print "[+]\tGonna try installing capstone."
|
|
install('capstone')
|
|
from capstone import *
|
|
|
|
PYROPGADGET_VERSION = 'ich'
|
|
|
|
if sys.version_info.major == 3:
|
|
xrange = range
|
|
|
|
import lldb
|
|
|
|
#global vars#
|
|
lisaversion = 'v-ni'
|
|
PAGE_SIZE=4096
|
|
MAX_DISTANCE=PAGE_SIZE*10
|
|
g_ignore_frame_pointer= False
|
|
reportexploitable=""
|
|
###################
|
|
|
|
REGISTERS = {
|
|
8 : ["al", "ah", "bl", "bh", "cl", "ch", "dl", "dh"],
|
|
16: ["ax", "bx", "cx", "dx"],
|
|
32: ["eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", "eip"],
|
|
64: ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "rip",
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"]
|
|
}
|
|
|
|
####################################
|
|
# Misc Utils #
|
|
####################################
|
|
def banner(debugger,command,result,dict):
|
|
|
|
lisa2="""
|
|
|
|
lllllll iiii
|
|
l:::::l i::::i
|
|
l:::::l iiii
|
|
l:::::l
|
|
l::::l iiiiiii ssssssssss aaaaaaaaaaaaa
|
|
l::::l i:::::i ss::::::::::s a::::::::::::a
|
|
l::::l i::::i ss:::::::::::::s aaaaaaaaa:::::a
|
|
l::::l i::::i s::::::ssss:::::s a::::a
|
|
l::::l i::::i s:::::s ssssss aaaaaaa:::::a
|
|
l::::l i::::i s::::::s aa::::::::::::a
|
|
l::::l i::::i s::::::s a::::aaaa::::::a
|
|
l::::l i::::i ssssss s:::::s a::::a a:::::a
|
|
l::::::li::::::is:::::ssss::::::sa::::a a:::::a
|
|
l::::::li::::::is::::::::::::::s a:::::aaaa::::::a
|
|
l::::::li::::::i s:::::::::::ss a::::::::::aa:::a
|
|
lllllllliiiiiiii sssssssssss aaaaaaaaaa aaaa
|
|
"""
|
|
print tty_colors.green()+random.choice([lisa2])+tty_colors.default()
|
|
print tty_colors.red()+"\t-An Exploit Dev Swiss Army Knife. Version: "+lisaversion+tty_colors.default()
|
|
|
|
#convert to hex
|
|
def to_hex(var):
|
|
"""
|
|
converts given value to hex
|
|
"""
|
|
return hex(var)
|
|
|
|
#hextoascii
|
|
def hex2ascii(debugger,hex,result,dict):
|
|
"""
|
|
converts Hex to ascii
|
|
ex: h2a 0x41414141 prints AAAA
|
|
"""
|
|
print hex.replace('0x','').decode('hex')
|
|
|
|
#generate random hex of length between n - m
|
|
def urandom(debugger,n,result,dict):
|
|
"""
|
|
Generates random hex of given length
|
|
"""
|
|
if not n:
|
|
print 'rand command an argument: example: rand 23'
|
|
return
|
|
print open('/dev/urandom','r').read(random.randint(int(n)/2,int(n)/2)).encode('hex')
|
|
|
|
# run os commands
|
|
def shell(debugger,command,result,dict):
|
|
"""
|
|
runs shell command and prints output
|
|
"""
|
|
try:
|
|
if command:
|
|
os.system(command)
|
|
else:
|
|
print 'Please enter a proper shell command.Eg: shell ls'
|
|
return
|
|
except:
|
|
print 'Please enter a proper shell command.Eg: shell ls'
|
|
return
|
|
|
|
#term colors
|
|
class TerminalColors:
|
|
'''Simple terminal colors class'''
|
|
def __init__(self, enabled = True):
|
|
# TODO: discover terminal type from "file" and disable if
|
|
# it can't handle the color codes
|
|
self.enabled = enabled
|
|
|
|
def reset(self):
|
|
'''Reset all terminal colors and formatting.'''
|
|
if self.enabled:
|
|
return "\x1b[0m";
|
|
return ''
|
|
|
|
def bold(self, on = True):
|
|
'''Enable or disable bold depending on the "on" parameter.'''
|
|
if self.enabled:
|
|
if on:
|
|
return "\x1b[1m";
|
|
else:
|
|
return "\x1b[22m";
|
|
return ''
|
|
|
|
def italics(self, on = True):
|
|
'''Enable or disable italics depending on the "on" parameter.'''
|
|
if self.enabled:
|
|
if on:
|
|
return "\x1b[3m";
|
|
else:
|
|
return "\x1b[23m";
|
|
return ''
|
|
|
|
def underline(self, on = True):
|
|
'''Enable or disable underline depending on the "on" parameter.'''
|
|
if self.enabled:
|
|
if on:
|
|
return "\x1b[4m";
|
|
else:
|
|
return "\x1b[24m";
|
|
return ''
|
|
|
|
def inverse(self, on = True):
|
|
'''Enable or disable inverse depending on the "on" parameter.'''
|
|
if self.enabled:
|
|
if on:
|
|
return "\x1b[7m";
|
|
else:
|
|
return "\x1b[27m";
|
|
return ''
|
|
|
|
def strike(self, on = True):
|
|
'''Enable or disable strike through depending on the "on" parameter.'''
|
|
if self.enabled:
|
|
if on:
|
|
return "\x1b[9m";
|
|
else:
|
|
return "\x1b[29m";
|
|
return ''
|
|
|
|
def black(self, fg = True):
|
|
'''Set the foreground or background color to black.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[30m";
|
|
else:
|
|
return "\x1b[40m";
|
|
return ''
|
|
|
|
def red(self, fg = True):
|
|
'''Set the foreground or background color to red.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[31m";
|
|
else:
|
|
return "\x1b[41m";
|
|
return ''
|
|
|
|
def green(self, fg = True):
|
|
'''Set the foreground or background color to green.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[32m";
|
|
else:
|
|
return "\x1b[42m";
|
|
return ''
|
|
|
|
def yellow(self, fg = True):
|
|
'''Set the foreground or background color to yellow.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[43m";
|
|
else:
|
|
return "\x1b[33m";
|
|
return ''
|
|
|
|
def blue(self, fg = True):
|
|
'''Set the foreground or background color to blue.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[34m";
|
|
else:
|
|
return "\x1b[44m";
|
|
return ''
|
|
|
|
def magenta(self, fg = True):
|
|
'''Set the foreground or background color to magenta.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[35m";
|
|
else:
|
|
return "\x1b[45m";
|
|
return ''
|
|
|
|
def cyan(self, fg = True):
|
|
'''Set the foreground or background color to cyan.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[36m";
|
|
else:
|
|
return "\x1b[46m";
|
|
return ''
|
|
|
|
def white(self, fg = True):
|
|
'''Set the foreground or background color to white.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[37m";
|
|
else:
|
|
return "\x1b[47m";
|
|
return ''
|
|
|
|
def default(self, fg = True):
|
|
'''Set the foreground or background color to the default.
|
|
The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
|
|
if self.enabled:
|
|
if fg:
|
|
return "\x1b[39m";
|
|
else:
|
|
return "\x1b[49m";
|
|
return ''
|
|
|
|
####################################
|
|
# LLDB #
|
|
####################################
|
|
|
|
#set malloc debugging features
|
|
def setMallocDebug(debugger,c,result,dict):
|
|
"""sets DYLD_INSERT_LIBRARIES to /usr/lib/libgmalloc.dylib"""
|
|
execute(debugger,'settings set target.env-vars DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib',result,dict)
|
|
return True
|
|
|
|
#execute given LLDB command
|
|
def execute(debugger,lldb_command,result,dict):
|
|
"""
|
|
Execute given command and print the outout to stdout
|
|
"""
|
|
debugger.HandleCommand(lldb_command)
|
|
|
|
#execute command and return output
|
|
def executeReturnOutput(debugger,lldb_command,result,dict):
|
|
"""Execute given command and returns the outout"""
|
|
ci = debugger.GetCommandInterpreter()
|
|
res=lldb.SBCommandReturnObject()
|
|
ci.HandleCommand(lldb_command,res)
|
|
output= res.GetOutput()
|
|
error = res.GetError()
|
|
return (output,error)
|
|
|
|
def s(debugger,command,result,dict):
|
|
"""step command"""
|
|
executeReturnOutput(debugger,"thread step-in",result,dict)
|
|
context(debugger,command,result,dict)
|
|
|
|
def si(debugger,command,result,dict):
|
|
"""step into command"""
|
|
executeReturnOutput(debugger,"thread step-inst",result,dict)
|
|
context(debugger,command,result,dict)
|
|
|
|
def so(debugger,command,result,dict):
|
|
"""step over"""
|
|
executeReturnOutput(debugger,"thread step-over",result,dict)
|
|
context(debugger,command,result,dict)
|
|
|
|
def stepnInstructions(debugger,count,result,dict):
|
|
"""step-in n time"""
|
|
c=0
|
|
|
|
while c<int(count):
|
|
command=""
|
|
executeReturnOutput(debugger,"thread step-in",result,dict)
|
|
c+=1
|
|
|
|
context(debugger,command,result,dict)
|
|
|
|
def testjump(debugger,command,result,dict):
|
|
"""
|
|
Test if jump instruction is taken or not
|
|
Returns:
|
|
True if jump is taken or False if not
|
|
"""
|
|
inst=None
|
|
flags = get_eflags(debugger,command,result,dict)
|
|
if not flags:
|
|
return None
|
|
|
|
if not inst:
|
|
pc =getregvalue(debugger,"pc",result,dict)
|
|
|
|
inst, error = executeReturnOutput(debugger,"x/1i $pc",result,dict)
|
|
if not inst:
|
|
return None
|
|
|
|
opcode = inst.split(' ')[2]
|
|
|
|
if opcode == "jmp":
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "je" and flags["ZF"]:
|
|
print inst.split(' ')[4]
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jne" and not flags["ZF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jg" and not flags["ZF"] and (flags["SF"] == flags["OF"]):
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jge" and (flags["SF"] == flags["OF"]):
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "ja" and not flags["CF"] and not flags["ZF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jae" and not flags["CF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jl" and (flags["SF"] != flags["OF"]):
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jle" and (flags["ZF"] or (flags["SF"] != flags["OF"])):
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jb" and flags["CF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jbe" and (flags["CF"] or flags["ZF"]):
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jo" and flags["OF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jno" and not flags["OF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jz" and flags["ZF"]:
|
|
return (True,inst.split(' ')[4])
|
|
if opcode == "jnz" and flags["OF"]:
|
|
return (True,inst.split(' ')[4])
|
|
|
|
return (False,None)
|
|
|
|
def context(debugger,command,result,dict):
|
|
"""Prints context of current execution"""
|
|
|
|
try:
|
|
#disas
|
|
op, error=executeReturnOutput(debugger,"disassemble -c 2 -s $pc",result,dict)
|
|
print tty_colors.red()+"[*] Disassembly :\n"+tty_colors.default()
|
|
print op
|
|
|
|
#stack
|
|
op, error=executeReturnOutput(debugger,"x/10x $sp",result,dict)
|
|
print tty_colors.red()+"[*] Stack :\n"+tty_colors.default()
|
|
print tty_colors.blue()+op+tty_colors.default()
|
|
|
|
#registers
|
|
op, error=executeReturnOutput(debugger,"register read",result,dict)
|
|
print tty_colors.red()+"[*] Registers\t:"+tty_colors.default()
|
|
print op.split("\n\n")[0].split('General Purpose Registers:\n')[1].split('eflags')[0]
|
|
print '\n'
|
|
|
|
#jump
|
|
dis, error=executeReturnOutput(debugger,'disassemble -c 1 -s $pc',result,dict)
|
|
if dis:
|
|
dis = dis.split(': ')[1].split()[0]
|
|
|
|
if 'j' in dis:
|
|
jumpto, destination = testjump(debugger,command,result,dict)
|
|
if jumpto==True:
|
|
print tty_colors.red()+"[*] Jumping to\t:"+destination+tty_colors.default()
|
|
else:
|
|
print tty_colors.red()+"[*] Jump not taken."+tty_colors.default()
|
|
else:
|
|
print error,
|
|
|
|
except Exception as e:
|
|
print 'error running context'
|
|
|
|
def get_eflags(debugger,command,result,dict):
|
|
"""
|
|
Get flags value from EFLAGS register
|
|
|
|
Returns:
|
|
- dictionary of named flags
|
|
"""
|
|
|
|
# Eflags bit masks, source vdb
|
|
EFLAGS_CF = 1 << 0
|
|
EFLAGS_PF = 1 << 2
|
|
EFLAGS_AF = 1 << 4
|
|
EFLAGS_ZF = 1 << 6
|
|
EFLAGS_SF = 1 << 7
|
|
EFLAGS_TF = 1 << 8
|
|
EFLAGS_IF = 1 << 9
|
|
EFLAGS_DF = 1 << 10
|
|
EFLAGS_OF = 1 << 11
|
|
|
|
flags = {"CF":0, "PF":0, "AF":0, "ZF":0, "SF":0, "TF":0, "IF":0, "DF":0, "OF":0}
|
|
eflags = getregvalue(debugger,"eflags",result,dict)
|
|
|
|
if not eflags:
|
|
eflags = getregvalue(debugger,"rflags",result,dict)
|
|
eflags=int(eflags,16)
|
|
flags["CF"] = bool(eflags & EFLAGS_CF)
|
|
flags["PF"] = bool(eflags & EFLAGS_PF)
|
|
flags["AF"] = bool(eflags & EFLAGS_AF)
|
|
flags["ZF"] = bool(eflags & EFLAGS_ZF)
|
|
flags["SF"] = bool(eflags & EFLAGS_SF)
|
|
flags["TF"] = bool(eflags & EFLAGS_TF)
|
|
flags["IF"] = bool(eflags & EFLAGS_IF)
|
|
flags["DF"] = bool(eflags & EFLAGS_DF)
|
|
flags["OF"] = bool(eflags & EFLAGS_OF)
|
|
|
|
return flags
|
|
|
|
|
|
####################################
|
|
# Exploitation #
|
|
####################################
|
|
|
|
|
|
#pattern create and pattern offset
|
|
def pattern_create(debugger,size,result,dict):
|
|
"""creates a cyclic pattern of given length"""
|
|
|
|
try:length=int(size)
|
|
except:print "[+] Usage: pattern_create <length> [set a] [set b] [set c]"
|
|
seta="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
setb="abcdefghijklmnopqrstuvwxyz"
|
|
setc="0123456789"
|
|
|
|
string="" ; a=0 ; b=0 ; c=0
|
|
|
|
while len(string) < length:
|
|
string += seta[a] + setb[b] + setc[c]
|
|
c+=1
|
|
if c == len(setc):c=0;b+=1
|
|
if b == len(setb):b=0;a+=1
|
|
if a == len(seta):a=0
|
|
|
|
print tty_colors.red()+ string[:length]+tty_colors.default()
|
|
|
|
return string[:length]
|
|
|
|
#check if given pattern is in cyclic pattern
|
|
def check_if_cyclic(debugger,pat,result,dict):
|
|
"""check if given pattern is in cyclic pattern"""
|
|
|
|
seta="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
setb="abcdefghijklmnopqrstuvwxyz"
|
|
setc="0123456789"
|
|
|
|
if not pat:
|
|
print '[+] Usage: check_if_cyclic <some string>'
|
|
return
|
|
|
|
string=pat ; a=0 ; b=0 ; c=0
|
|
length=len(string)
|
|
i=0
|
|
while i<(length-2):
|
|
if string[i].isalpha():
|
|
if string[i].islower():
|
|
if string[i+1].isupper():
|
|
if string[i+2].isdigit():
|
|
pass
|
|
else:
|
|
print tty_colors.red()+"Not a cyclic pattern"+tty_colors.default()
|
|
return False
|
|
else:
|
|
print tty_colors.red()+"Not a cyclic pattern"+tty_colors.default()
|
|
return False
|
|
|
|
elif string[i].isupper():
|
|
if string[i+1].islower():
|
|
if string[i+2].isdigit():
|
|
pass
|
|
else:
|
|
print tty_colors.red()+"Not a cyclic pattern"+tty_colors.default()
|
|
return False
|
|
else:
|
|
print tty_colors.red()+"Not a cyclic pattern"+tty_colors.default()
|
|
return False
|
|
|
|
elif string[i].isdigit():
|
|
if string[i+1].isalpha():
|
|
if string[i+1].isupper():
|
|
if string[i+2].islower():
|
|
pass
|
|
else:
|
|
print tty_colors.red()+"Not a cyclic pattern"+tty_colors.default()
|
|
return False
|
|
else:
|
|
print tty_colors.red()+"Not a cyclic pattern"+tty_colors.default()
|
|
return False
|
|
|
|
|
|
i+=3
|
|
print "seems to be a valid pattern"
|
|
return True
|
|
|
|
#pattern search
|
|
def pattern_offset(debugger,sizepat,result,dict):
|
|
"""search offset of pattern."""
|
|
|
|
if len(sizepat.split(' '))==2:
|
|
try:
|
|
size=int(sizepat.split(' ')[0])
|
|
pat=sizepat.split(' ')[1]
|
|
pattern=pattern_create(debugger,size,result,dict)
|
|
if "0x" in pat:
|
|
pat=pat.replace("0x","")
|
|
pat=pat.decode("hex")[::-1]
|
|
try:
|
|
p=int(pat)
|
|
pat=pat.decode('hex')
|
|
except:
|
|
pass
|
|
found=[m.start() for m in re.finditer(pat, pattern)]
|
|
if found!=-1:
|
|
print 'offsets:',found
|
|
except:
|
|
print "please check the syntax"
|
|
print "pattern_offset 250 Aa2A"
|
|
|
|
elif len(sizepat.split(' '))==1:
|
|
try:
|
|
size=10000
|
|
pat=sizepat.split(' ')[1]
|
|
pattern=pattern_create(debugger,size,result,dict,True)
|
|
if "0x" in pat:
|
|
pat=pat.replace("0x","")
|
|
pat=pat.decode("hex")[::-1]
|
|
try:
|
|
p=int(pat)
|
|
pat=pat.decode('hex')
|
|
except:
|
|
pass
|
|
found=[m.start() for m in re.finditer(pat, pattern)]
|
|
# found=pattern.find(pat)
|
|
if found!=-1:
|
|
print found
|
|
except:
|
|
print "please check the syntax"
|
|
print "pattern_offset 250 Aa2A"
|
|
|
|
#return address in register
|
|
def getregvalue(debugger,reg,result,dict):
|
|
output,error=executeReturnOutput(debugger,'register read '+reg,result,dict)
|
|
return output.split("= ")[-1].split(" ")[0]
|
|
|
|
lldb_stop_reasons = { 'eStateCrashed' : 8, 'eStateDetached' : 9, 'eStateExited' : 10, 'eStateInvalid' : 0,
|
|
'eStateLaunching' : 4, 'eStateRunning' : 6, 'eStateStepping' : 7,'eStateStopped' : 5,
|
|
'eStateSuspended' : 11, 'eStopReasonBreakpoint' : 3, 'eStopReasonException' : 6,
|
|
'eStopReasonExec' : 7, 'eStopReasonInvalid' : 0, 'eStopReasonNone' : 1, 'eStopReasonSignal' : 5,
|
|
'eStopReasonThreadExiting' : 9, 'eStopReasonTrace' : 2, 'eStopReasonWatchpoint' : 4
|
|
}
|
|
|
|
def getexception(exception_description):
|
|
type1 = exception_description
|
|
try:
|
|
exception = re.search("EXC_(.+?) ",type1).group().strip(' ')
|
|
except:
|
|
exception = None
|
|
try:
|
|
code = re.search("\(code(.+?),",type1).group().split('=')[1].strip(',')
|
|
except:
|
|
try:
|
|
code = re.search("\(code(.+?)\)",type1).group().split('=')[1].strip(')')
|
|
except:
|
|
code = None
|
|
try:
|
|
address = re.search(", address(.+?)\)",type1).group().split('=')[1].strip(')')
|
|
except:
|
|
address = None
|
|
|
|
return exception,code,address
|
|
|
|
def getsignal(signal_description):
|
|
return signal_description.split(' ')[1]
|
|
|
|
|
|
def type_for_two_memory(access_address, disassembly):
|
|
first_reg_val = value_for_first_register(disassembly)
|
|
if first_reg_val != access_address:
|
|
return "read"
|
|
else:
|
|
return "write"
|
|
|
|
def stack_access_crash(access_address, sp_val):
|
|
access_address=int(access_address,0)
|
|
sp_val = int(sp_val,0)
|
|
if ((sp_val - access_address) <= PAGE_SIZE):
|
|
return True
|
|
return False
|
|
|
|
def getexceptiontype(access_address, disassembly, registers):
|
|
if disassembly!=None:
|
|
last_comma = disassembly.find(',')
|
|
right_paren = disassembly.find(']')
|
|
|
|
sp = registers['sp']
|
|
|
|
if disassembly[right_paren+1:].find(']')!=-1:
|
|
type_=type_for_two_memory(access_address, disassembly)
|
|
return type_
|
|
|
|
elif disassembly.find('call')!=-1:
|
|
if not right_paren or last_comma:
|
|
type_ = "recursion"
|
|
elif stack_access_crash(access_address,sp):
|
|
print 'ohhh'
|
|
type_ = "recursion"
|
|
else:
|
|
type_ = "exec"
|
|
return type_
|
|
|
|
elif disassembly.find("cmp")!=-1 or disassembly.find("test")!=-1 or disassembly.find("fld")!=-1:
|
|
type_ = "read"
|
|
return type_
|
|
|
|
elif disassembly.find("fst")!=-1:
|
|
type_ = "write"
|
|
return type_
|
|
|
|
elif disassembly.find("mov")!=-1:
|
|
if last_comma>right_paren:
|
|
type_ = "read"
|
|
else:
|
|
type_ = "write"
|
|
return type_
|
|
|
|
elif disassembly.find('jmp')!=-1:
|
|
type_ = "exec"
|
|
return type_
|
|
|
|
elif disassembly.find('push')!=-1:
|
|
if right_paren:
|
|
type_ = "read"
|
|
else:
|
|
type_ = "recursion"
|
|
return type_
|
|
|
|
elif disassembly.find('inc')!=-1 or disassembly.find('dec')!=-1:
|
|
type_ = "write"
|
|
return type_
|
|
|
|
elif disassembly.find("stos")!=-1:
|
|
type_ = "write"
|
|
return type_
|
|
|
|
elif disassembly.find("lods")!=-1:
|
|
type_ = "read"
|
|
return type_
|
|
|
|
else:
|
|
type_ = "unknown"
|
|
return type_
|
|
|
|
if disassembly.find("st") == 2:
|
|
type_ = "write"
|
|
return type_
|
|
|
|
elif disassembly.find("ld") == 2:
|
|
type_ = "read"
|
|
return type_
|
|
|
|
elif disassembly.find("push") == 2:
|
|
type_ = "recursion"
|
|
return type_
|
|
|
|
else:
|
|
type_ = "unknown"
|
|
return type_
|
|
|
|
else:
|
|
type_ = "unknown"
|
|
return type_
|
|
|
|
def is_stack_suspicious(exc_address, exception, backtrace):
|
|
global reportexploitable
|
|
global is_exploitable
|
|
|
|
suspicious_functions = [
|
|
"__chk_fail", "__stack_chk_fail", "szone_error", "CFRelease", "CFRetain", "_CFRelease", "_CFRetain",
|
|
"malloc", "calloc", "realloc", "objc_msgSend",
|
|
"szone_free", "free_small", "tiny_free_list_add_ptr", "tiny_free_list_remove_ptr",
|
|
"small_free_list_add_ptr", "small_free_list_remove_ptr", "large_entries_free_no_lock",
|
|
"large_free_no_lock", "szone_batch_free", "szone_destroy", "free",
|
|
"CSMemDisposeHandle", "CSMemDisposePtr",
|
|
"append_int", "release_file_streams_for_task", "__guard_setup",
|
|
"_CFStringAppendFormatAndArgumentsAux", "WTF::fastFree", "WTF::fastMalloc",
|
|
"WTF::FastCalloc", "WTF::FastRealloc", " WTF::tryFastCalloc", "WTF::tryFastMalloc",
|
|
"WTF::tryFastRealloc", "WTF::TCMalloc_Central_FreeList", "GMfree", "GMmalloc_zone_free",
|
|
"GMrealloc", "GMmalloc_zone_realloc"]
|
|
|
|
if exc_address=="0xbbadbeef":
|
|
# WebCore functions call CRASH() in various assertions or if the amount to allocate was too big. CRASH writes a null byte to 0xbbadbeef.
|
|
is_exploitable=False
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
reportexploitable = "Not exploitable. Seems to be a safe crash. Calls to CRASH() function writes a null byte to 0xbbadbeef"
|
|
print tty_colors.red()+"Not exploitable. Seems to be a safe crash. Calls to CRASH() function writes a null byte to 0xbbadbeef"+tty_colors.default()
|
|
|
|
return
|
|
|
|
if "0 ???" in backtrace:
|
|
is_exploitable = True
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"This crash is suspected to be exploitable because the crashing instruction is outside of a known function, i.e. in dynamically generated code"+tty_colors.default()
|
|
reportexploitable="This crash is suspected to be exploitable because the crashing instruction is outside of a known function, i.e. in dynamically generated code"
|
|
return
|
|
|
|
for i in suspicious_functions:
|
|
if i in backtrace:
|
|
if exception == "EXC_BREAKPOINT" and (i=="CFRelease" or i=="CFRetain"):
|
|
is_exploitable = "no"
|
|
return
|
|
elif i=="_CFRelease" or i=="CFRelease" and "CGContextDelegateFinalize" in backtrace:
|
|
return
|
|
elif i=="objc_msgSend" and exc_address<<PAGE_SIZE:
|
|
continue
|
|
else:
|
|
is_exploitable = True
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"The crash is suspected to be an exploitable issue due to the suspicious function in the stack trace of the crashing thread: "+i+tty_colors.default()
|
|
reportexploitable="The crash is suspected to be an exploitable issue due to the suspicious function in the stack trace of the crashing thread."
|
|
return
|
|
|
|
#return whether or not the base pointer is far away from the stack pointer.
|
|
def bp_inconsistent_with_sp(bp_val,sp_val):
|
|
#define MAX_DISTANCE (PAGE_SIZE * 10)
|
|
# No check if bp_val > sp_val since bp_val - sp_val may have underflowed.
|
|
|
|
if (int(bp_val,0) - int(sp_val,0)) > MAX_DISTANCE:
|
|
return True
|
|
return False
|
|
|
|
class Lisa:
|
|
def __init__(self, debugger,result,dict):
|
|
|
|
self.debugger = debugger
|
|
self.target = self.debugger.GetSelectedTarget()
|
|
self.process = self.target.process
|
|
self.thread = self.process.selected_thread
|
|
self.frame = self.thread.GetFrameAtIndex(0)
|
|
self.pc = hex(self.frame.pc)
|
|
self.sp = hex(self.frame.sp)
|
|
self.bp = hex(self.frame.fp)
|
|
|
|
disas,disas_error = executeReturnOutput(debugger,"disassemble -c 1 -s $pc",result,dict)
|
|
|
|
if disas_error:
|
|
self.pc_disas = None
|
|
else:
|
|
self.pc_disas = re.search("->(.+?)\n",disas).group().split(':')[1]
|
|
|
|
self.backtrace, self.backtrace_error = executeReturnOutput(debugger,"bt",result,dict)
|
|
|
|
self.crash_reason = self.thread.GetStopReason()
|
|
|
|
if self.crash_reason == lldb_stop_reasons['eStopReasonException']:
|
|
self.exception = self.thread.GetStopDescription(80)
|
|
self.exception,self.exc_code,self.exc_address = getexception(self.exception)
|
|
self.signal = None
|
|
|
|
print tty_colors.red()+"Exception : "+self.exception+tty_colors.default()
|
|
|
|
elif self.crash_reason == lldb_stop_reasons['eStopReasonSignal']:
|
|
self.signal = self.thread.GetStopDescription(80)
|
|
self.signal = getsignal(self.signal)
|
|
self.exc_address = None
|
|
self.exception = None
|
|
|
|
print tty_colors.red()+"Signal : "+self.signal+tty_colors.default()
|
|
|
|
else:
|
|
return
|
|
|
|
self.gen_registers = list(self.frame.registers)[0]
|
|
self.registers = {}
|
|
|
|
for i in self.gen_registers.__iter__():
|
|
self.registers[i.name]=i.value
|
|
|
|
max_offset = 1024
|
|
if self.exception:
|
|
if self.exception=="EXC_BAD_ACCESS":
|
|
# check pc == access_address
|
|
|
|
if self.exc_address and int(self.exc_address,0)==int(self.pc,0):
|
|
# IP over write
|
|
is_exploitable = True
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"Trying to execute a bad address, this is a potentially exploitable issue"+tty_colors.default()
|
|
reportexploitable="Trying to execute a bad address, this is a potentially exploitable issue"
|
|
|
|
else:
|
|
self.access_type = getexceptiontype(self.exc_address, self.pc_disas, self.registers)
|
|
|
|
if self.exc_address and int(self.exc_address,16)<int(PAGE_SIZE):
|
|
is_exploitable=False
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.blue()+"Null Dereference. Probably not exploitable"+tty_colors.default()
|
|
reportexploitable="Null Dereference. Probably not exploitable"
|
|
return
|
|
|
|
elif self.access_type == "recursion":
|
|
is_exploitable=False
|
|
|
|
stack=self.backtrace
|
|
MINIMUM_RECURSION_LENGTH = 300
|
|
stack_length= len(stack.split("\n"))
|
|
|
|
if stack_length>MINIMUM_RECURSION_LENGTH:
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"The crash is suspected to be not exploitable due to unbounded recursion since there were %d stack frames."%stack_length+tty_colors.default()
|
|
reportexploitable="The crash is suspected to be not exploitable due to unbounded recursion since there were %d stack frames."%stack_length
|
|
return
|
|
else:
|
|
is_exploitable=True
|
|
|
|
if self.access_type == "exec":
|
|
is_exploitable = True
|
|
|
|
addr = self.exc_address
|
|
max_offset = 1024
|
|
|
|
if (addr > 0x55555555 - max_offset and addr < 0x55555555 + max_offset):
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"The access address indicates the use of freed memory if MallocScribble was used, or uninitialized memory if libgmalloc and MALLOC_FILL_SPACE was used."+tty_colors.default()
|
|
reportexploitable="The access address indicates the use of freed memory if MallocScribble was used, or uninitialized memory if libgmalloc and MALLOC_FILL_SPACE was used."
|
|
|
|
elif (addr > 0xaaaaaaaa - max_offset and addr < 0xaaaaaaaa + max_offset):
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"The access address indicates that uninitialized memory was being used if MallocScribble was used."+tty_colors.default()
|
|
reportexploitable="The access address indicates that uninitialized memory was being used if MallocScribble was used."
|
|
|
|
elif "EXC_I386_GPFLT" == self.exc_code:
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"The exception code indicates that the access address was invalid in the 64-bit ABI (it was > 0x0000800000000000)."+tty_colors.default()
|
|
reportexploitable="The exception code indicates that the access address was invalid in the 64-bit ABI (it was > 0x0000800000000000)."
|
|
|
|
elif not g_ignore_frame_pointer and bp_inconsistent_with_sp(self.bp,self.sp):
|
|
is_exploitable = True
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.red()+"Presumed exploitable based on the discrepancy between the stack pointer and base pointer registers. "+tty_colors.default()
|
|
reportexploitable="Presumed exploitable based on the discrepancy between the stack pointer and base pointer registers."
|
|
|
|
else:
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
|
|
if self.access_type=="read" or self.access_type=="write":
|
|
print tty_colors.red()+"Crash "+self.access_type+"'g invalid address."+tty_colors.default()
|
|
reportexploitable= "Crash "+self.access_type+"'g invalid address."
|
|
else:
|
|
print tty_colors.red()+"Crash accessing invalid address."+tty_colors.default()
|
|
reportexploitable= "Crash accessing invalid address."
|
|
|
|
elif self.exception=="EXC_BAD_INSTRUCTION":
|
|
is_exploitable = True
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.blue()+"Illegal instruction at %s, probably a exploitable issue unless the crash was in libdispatch/xpc."%self.pc+tty_colors.default()
|
|
reportexploitable="Illegal instruction at %s, probably a exploitable issue unless the crash was in libdispatch/xpc."%self.pc
|
|
|
|
elif self.exception=="EXC_ARITHMETIC":
|
|
is_exploitable = False
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.blue()+"Arithmetic exception at %s, probably not exploitable."%self.pc+tty_colors.default()
|
|
reportexploitable="Arithmetic exception at %s, probably not exploitable."%self.pc
|
|
|
|
elif self.exception=="EXC_SOFTWARE":
|
|
is_exploitable=False
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.blue()+"Software exception, probably not exploitable."+tty_colors.default()
|
|
reportexploitable="Software exception, probably not exploitable."
|
|
|
|
elif self.exception=="EXC_BREAKPOINT":
|
|
is_exploitable=False
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
print tty_colors.blue()+"Software breakpoint, probably not exploitable."+tty_colors.default()
|
|
reportexploitable="Software breakpoint, probably not exploitable."
|
|
|
|
elif self.exc_address=="EXC_CRASH":
|
|
is_exploitable= False
|
|
print tty_colors.red()+"Exploitable = %s"%is_exploitable+tty_colors.default()
|
|
|
|
elif self.signal:
|
|
is_stack_suspicious(self.exc_address, self.exception, self.backtrace)
|
|
|
|
|
|
def exploitable(debugger,cmd,res,dict):
|
|
"""checks if the crash is exploitable"""
|
|
lisa_=Lisa(debugger,res,dict)
|
|
|
|
class ShellStorm:
|
|
def __init__(self):
|
|
pass
|
|
|
|
def searchShellcode(self, keyword):
|
|
try:
|
|
print "Connecting to shell-storm.org..."
|
|
s = httplib.HTTPConnection("shell-storm.org")
|
|
s.request("GET", "/api/?s="+str(keyword))
|
|
res = s.getresponse()
|
|
data_l = res.read().split('\n')
|
|
except:
|
|
print "Cannot connect to shell-storm.org"
|
|
return None
|
|
|
|
data_dl = []
|
|
for data in data_l:
|
|
try:
|
|
desc = data.split("::::")
|
|
try:
|
|
dico = {
|
|
'ScAuthor': desc[0],
|
|
'ScArch': desc[1],
|
|
'ScTitle': desc[2],
|
|
'ScId': desc[3],
|
|
'ScUrl': desc[4],
|
|
'ScSize': int(''.join(x for x in desc[2][-10:-5] if x.isdigit()))
|
|
}
|
|
except Exception:
|
|
dico = {
|
|
'ScAuthor': desc[0],
|
|
'ScArch': desc[1],
|
|
'ScTitle': desc[2],
|
|
'ScId': desc[3],
|
|
'ScUrl': desc[4],
|
|
'ScSize': 0
|
|
}
|
|
|
|
|
|
data_dl.append(dico)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
return sorted(data_dl, key=lambda x: x['ScSize'], reverse=True)
|
|
except Exception:
|
|
print("Could not sort by size")
|
|
|
|
return data_dl
|
|
|
|
def displayShellcode(self, shellcodeId):
|
|
if shellcodeId is None:
|
|
return None
|
|
|
|
try:
|
|
print "Connecting to shell-storm.org..."
|
|
s = httplib.HTTPConnection("shell-storm.org")
|
|
except:
|
|
print "Cannot connect to shell-storm.org"
|
|
return None
|
|
|
|
try:
|
|
s.request("GET", "/shellcode/files/shellcode-"+str(shellcodeId)+".php")
|
|
res = s.getresponse()
|
|
data = res.read().split("<pre>")[1].split("<body>")[0]
|
|
except:
|
|
print "Failed to download shellcode from shell-storm.org"
|
|
return None
|
|
|
|
data = data.replace(""", "\"")
|
|
data = data.replace("&", "&")
|
|
data = data.replace("<", "<")
|
|
data = data.replace(">", ">")
|
|
|
|
return data
|
|
|
|
@staticmethod
|
|
def version():
|
|
print "shell-storm API - v0.1"
|
|
print "Search and display all shellcodes in shell-storm database"
|
|
print "Jonathan Salwan - @JonathanSalwan - 2012"
|
|
print "http://shell-storm.org"
|
|
return
|
|
|
|
def extract_from_universal_binary(debugger,command,result,dict):
|
|
"""Uses lipo to extract a given architecture from a Universal binary
|
|
Syntax: extract x86_64 <input file> <output file>
|
|
Ex: extract x86_64 /usr/lib/system/libsystem_kernel.dylib ./libsystem_kernel.dylib
|
|
|
|
"""
|
|
args = shlex.split(command)
|
|
if len(args)==3:
|
|
architecture = args[0]
|
|
intputfile = args[1]
|
|
outputfile = args[2]
|
|
|
|
commands.getoutput('lipo '+intputfile+' -extract '+architecture+' -output '+outputfile)
|
|
else:
|
|
print "Syntax: extract x86_64 /usr/lib/system/libsystem_kernel.dylib ./libsystem_kernel.dylib"
|
|
|
|
|
|
|
|
def shellcode(debugger, command, result, dict):
|
|
"""Searches shell-storm for shellcode
|
|
Syntax:shellcode"""
|
|
mod = shlex.split(command)
|
|
if len(mod)!=2:
|
|
print "Syntax: shellcode <option> <arg>\n"
|
|
print "Options: -search <keyword>"
|
|
print " -display <shellcode id>"
|
|
print " -save <shellcode id>"
|
|
return
|
|
|
|
arg = mod[1]
|
|
mod = mod[0]
|
|
if mod != "-search" and mod != "-display" and mod != "-save":
|
|
syntax()
|
|
return
|
|
|
|
if mod == "-search":
|
|
api = ShellStorm()
|
|
res_dl = api.searchShellcode(arg)
|
|
if not res_dl:
|
|
print "Shellcode not found"
|
|
sys.exit(0)
|
|
|
|
print "Found %d shellcodes" % len(res_dl)
|
|
print "%s\t%s %s" %("ScId", "Size", "Title")
|
|
for data_d in res_dl:
|
|
if data_d['ScSize'] == 0:
|
|
print "[%s]\tn/a %s - %s"%(data_d['ScId'], data_d['ScArch'], data_d['ScTitle'])
|
|
else:
|
|
print "[%s]\t%s%s - %s"%(data_d['ScId'], str(data_d['ScSize']).ljust(5), data_d['ScArch'], data_d['ScTitle'])
|
|
|
|
elif mod == "-display":
|
|
res = ShellStorm().displayShellcode(arg)
|
|
if not res:
|
|
print "Shellcode id not found"
|
|
return
|
|
print tty_colors.red()+res+tty_colors.default()
|
|
|
|
elif mod == "-save":
|
|
res = ShellStorm().displayShellcode(arg)
|
|
|
|
if not res:
|
|
print "Shellcode id not found"
|
|
return
|
|
f=open('shellcode_'+str(time.time())+'.c','w')
|
|
f.write(res)
|
|
f.close()
|
|
print tty_colors.red()+"Written to file shellcode_"+str(time.time())+'.c'+tty_colors.default()
|
|
|
|
|
|
|
|
def dump(debugger,command,result,dict):
|
|
"""Dump's Memory of the process in a given address range
|
|
Syntax: dump outfile 0x6080000fe680 0x6080000fe680+1000
|
|
dump will not read over 1024 bytes of data. To overwride this use -f
|
|
Syntax: dump outfile 0x6080000fe680 0x6080000fe680+1000 -f"""
|
|
|
|
args = shlex.split(command)
|
|
if len(args)<3:
|
|
print "Syntax: dump outfile 0x6080000fe680 0x6080000fe680+1000"
|
|
return
|
|
|
|
outfile = args[0]
|
|
start_range = args[1]
|
|
end_range = args[2]
|
|
force=False
|
|
if len(args)>3:
|
|
force=True
|
|
|
|
if force:
|
|
output,error=executeReturnOutput(debugger,"memory read -b --force --outfile "+outfile+' '+start_range+' '+end_range,result,dict)
|
|
else:
|
|
output,error=executeReturnOutput(debugger,"memory read -b --outfile "+outfile+' '+start_range+' '+end_range,result,dict)
|
|
|
|
if not error:
|
|
print output,
|
|
else:
|
|
if "--force" in error:
|
|
print "dump will not read over 1024 bytes of data. To overwride this use -f."
|
|
print "Syntax: dump outfile 0x6080000fe680 0x6080000fe680+1000 -f"
|
|
else:
|
|
print error
|
|
|
|
class MACH_HEADER(Structure):
|
|
_fields_ = [
|
|
("magic", c_uint),
|
|
("cputype", c_uint),
|
|
("cpusubtype", c_uint),
|
|
("filetype", c_uint),
|
|
("ncmds", c_uint),
|
|
("sizeofcmds", c_uint),
|
|
("flags", c_uint)
|
|
]
|
|
|
|
class LOAD_COMMAND(Structure):
|
|
_fields_ = [
|
|
("cmd", c_uint),
|
|
("cmdsize", c_uint)
|
|
]
|
|
|
|
class SEGMENT_COMMAND(Structure):
|
|
_fields_ = [
|
|
("cmd", c_uint),
|
|
("cmdsize", c_uint),
|
|
("segname", c_ubyte * 16),
|
|
("vmaddr", c_uint),
|
|
("vmsize", c_uint),
|
|
("fileoff", c_uint),
|
|
("filesize", c_uint),
|
|
("maxprot", c_uint),
|
|
("initprot", c_uint),
|
|
("nsects", c_uint),
|
|
("flags", c_uint)
|
|
]
|
|
|
|
class SEGMENT_COMMAND64(Structure):
|
|
_fields_ = [
|
|
("cmd", c_uint),
|
|
("cmdsize", c_uint),
|
|
("segname", c_ubyte * 16),
|
|
("vmaddr", c_ulonglong),
|
|
("vmsize", c_ulonglong),
|
|
("fileoff", c_ulonglong),
|
|
("filesize", c_ulonglong),
|
|
("maxprot", c_uint),
|
|
("initprot", c_uint),
|
|
("nsects", c_uint),
|
|
("flags", c_uint)
|
|
]
|
|
|
|
class SECTION(Structure):
|
|
_fields_ = [
|
|
("sectname", c_ubyte * 16),
|
|
("segname", c_ubyte * 16),
|
|
("addr", c_uint),
|
|
("size", c_uint),
|
|
("offset", c_uint),
|
|
("align", c_uint),
|
|
("reloff", c_uint),
|
|
("nreloc", c_uint),
|
|
("flags", c_uint),
|
|
("reserved1", c_uint),
|
|
("reserved2", c_uint)
|
|
]
|
|
|
|
class SECTION64(Structure):
|
|
_fields_ = [
|
|
("sectname", c_ubyte * 16),
|
|
("segname", c_ubyte * 16),
|
|
("addr", c_ulonglong),
|
|
("size", c_ulonglong),
|
|
("offset", c_uint),
|
|
("align", c_uint),
|
|
("reloff", c_uint),
|
|
("nreloc", c_uint),
|
|
("flags", c_uint),
|
|
("reserved1", c_uint),
|
|
("reserved2", c_uint)
|
|
]
|
|
|
|
class MACHOFlags:
|
|
CPU_TYPE_I386 = 0x7
|
|
CPU_TYPE_X86_64 = (CPU_TYPE_I386 | 0x1000000)
|
|
CPU_TYPE_MIPS = 0x8
|
|
CPU_TYPE_ARM = 12
|
|
CPU_TYPE_SPARC = 14
|
|
CPU_TYPE_POWERPC = 18
|
|
CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | 0x1000000)
|
|
LC_SEGMENT = 0x1
|
|
LC_SEGMENT_64 = 0x19
|
|
S_ATTR_SOME_INSTRUCTIONS = 0x00000400
|
|
S_ATTR_PURE_INSTRUCTIONS = 0x80000000
|
|
|
|
class MachOHeader(Structure):
|
|
|
|
_fields_ = [
|
|
|
|
("magic", c_uint),
|
|
("cputype", c_uint),
|
|
("cpusubtype", c_uint),
|
|
("filetype", c_uint),
|
|
("ncmds", c_uint),
|
|
("sizeofcmds", c_uint),
|
|
("flags", c_uint)
|
|
|
|
]
|
|
|
|
""" This class parses the Mach-O """
|
|
class MACHO:
|
|
def __init__(self, binary):
|
|
self.__binary = bytearray(binary)
|
|
|
|
self.__machHeader = None
|
|
self.__rawLoadCmd = None
|
|
self.__sections_l = []
|
|
|
|
self.__setHeader()
|
|
self.__setLoadCmd()
|
|
|
|
def __setHeader(self):
|
|
self.__machHeader = MACH_HEADER.from_buffer_copy(self.__binary)
|
|
|
|
if self.getArchMode() == CS_MODE_32:
|
|
self.__rawLoadCmd = self.__binary[28:28+self.__machHeader.sizeofcmds]
|
|
|
|
elif self.getArchMode() == CS_MODE_64:
|
|
self.__rawLoadCmd = self.__binary[32:32+self.__machHeader.sizeofcmds]
|
|
|
|
def __setLoadCmd(self):
|
|
base = self.__rawLoadCmd
|
|
for i in range(self.__machHeader.ncmds):
|
|
command = LOAD_COMMAND.from_buffer_copy(base)
|
|
|
|
if command.cmd == MACHOFlags.LC_SEGMENT:
|
|
segment = SEGMENT_COMMAND.from_buffer_copy(base)
|
|
self.__setSections(segment.nsects, base[56:], 32)
|
|
|
|
elif command.cmd == MACHOFlags.LC_SEGMENT_64:
|
|
segment = SEGMENT_COMMAND64.from_buffer_copy(base)
|
|
self.__setSections(segment.nsects, base[72:], 64)
|
|
|
|
base = base[command.cmdsize:]
|
|
|
|
def __setSections(self, sectionsNumber, base, sizeHeader):
|
|
for i in range(sectionsNumber):
|
|
if sizeHeader == 32:
|
|
section = SECTION.from_buffer_copy(base)
|
|
base = base[68:]
|
|
self.__sections_l += [section]
|
|
elif sizeHeader == 64:
|
|
section = SECTION64.from_buffer_copy(base)
|
|
base = base[80:]
|
|
self.__sections_l += [section]
|
|
|
|
def getEntryPoint(self):
|
|
|
|
for section in self.__sections_l:
|
|
if section.sectname[0:6] == "__text":
|
|
return section.addr
|
|
|
|
def getExecSections(self):
|
|
ret = []
|
|
for section in self.__sections_l:
|
|
if section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS or section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS:
|
|
ret += [{
|
|
"name" : section.sectname,
|
|
"offset" : section.offset,
|
|
"size" : section.size,
|
|
"vaddr" : section.addr,
|
|
"opcodes" : bytes(self.__binary[section.offset:section.offset+section.size])
|
|
}]
|
|
return ret
|
|
|
|
def getDataSections(self):
|
|
ret = []
|
|
for section in self.__sections_l:
|
|
if not section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS and not section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS:
|
|
ret += [{
|
|
"name" : section.sectname,
|
|
"offset" : section.offset,
|
|
"size" : section.size,
|
|
"vaddr" : section.addr,
|
|
"opcodes" : str(self.__binary[section.offset:section.offset+section.size])
|
|
}]
|
|
return ret
|
|
|
|
def getArch(self):
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_I386 or self.__machHeader.cputype == MACHOFlags.CPU_TYPE_X86_64:
|
|
return CS_ARCH_X86
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM:
|
|
return CS_ARCH_ARM
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_MIPS:
|
|
return CS_ARCH_MIPS
|
|
else:
|
|
print("[Error] MACHO.getArch() - Architecture not supported")
|
|
return None
|
|
|
|
def getArchMode(self):
|
|
if self.__machHeader.magic == 0xfeedface:
|
|
return 4
|
|
elif self.__machHeader.magic == 0xfeedfacf:
|
|
return 8
|
|
else:
|
|
print("[Error] MACHO.getArchMode() - Bad Arch size")
|
|
return None
|
|
pass
|
|
|
|
def getFormat(self):
|
|
return "Mach-O"
|
|
|
|
class FAT_HEADER(BigEndianStructure):
|
|
_fields_ = [
|
|
("magic", c_uint),
|
|
("nfat_arch", c_uint)
|
|
]
|
|
|
|
class FAT_ARC(BigEndianStructure):
|
|
_fields_ = [
|
|
("cputype", c_uint),
|
|
("cpusubtype", c_uint),
|
|
("offset", c_uint),
|
|
("size", c_uint),
|
|
("align", c_uint)
|
|
]
|
|
|
|
class MACHOFlags:
|
|
CPU_TYPE_I386 = 0x7
|
|
CPU_TYPE_X86_64 = (CPU_TYPE_I386 | 0x1000000)
|
|
CPU_TYPE_MIPS = 0x8
|
|
CPU_TYPE_ARM = 12
|
|
CPU_TYPE_SPARC = 14
|
|
CPU_TYPE_POWERPC = 18
|
|
CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | 0x1000000)
|
|
LC_SEGMENT = 0x1
|
|
LC_SEGMENT_64 = 0x19
|
|
S_ATTR_SOME_INSTRUCTIONS = 0x00000400
|
|
S_ATTR_PURE_INSTRUCTIONS = 0x80000000
|
|
|
|
""" This class parses the Universal binary """
|
|
class UNIVERSAL:
|
|
def __init__(self, binary):
|
|
self.__binary = bytearray(binary)
|
|
self.__machoBinaries = []
|
|
|
|
self.__fatHeader = None
|
|
self.__rawLoadCmd = None
|
|
self.__sections_l = []
|
|
|
|
self.__setHeader()
|
|
self.__setBinaries()
|
|
|
|
def __setHeader(self):
|
|
self.__fatHeader = FAT_HEADER.from_buffer_copy(self.__binary)
|
|
|
|
def __setBinaries(self):
|
|
offset = 8
|
|
for i in xrange(self.__fatHeader.nfat_arch):
|
|
header = FAT_ARC.from_buffer_copy(self.__binary[offset:])
|
|
rawBinary = self.__binary[header.offset:header.offset+header.size]
|
|
if rawBinary[:4] == unhexlify(b"cefaedfe") or rawBinary[:4] == unhexlify(b"cffaedfe"):
|
|
self.__machoBinaries.append(MACHO(rawBinary))
|
|
else:
|
|
print("[Error] Binary #"+str(i+1)+" in Universal binary has an unsupported format")
|
|
offset += sizeof(header)
|
|
|
|
def getExecSections(self):
|
|
ret = []
|
|
for binary in self.__machoBinaries:
|
|
ret += binary.getExecSections()
|
|
return ret
|
|
|
|
def getDataSections(self):
|
|
ret = []
|
|
for binary in self.__machoBinaries:
|
|
ret += binary.getDataSections()
|
|
return ret
|
|
|
|
def getFormat(self):
|
|
return "Universal"
|
|
|
|
# TODO: These three will just return whatever is in the first binary.
|
|
# Perhaps the rest of ROPgadget should support loading multiple binaries?
|
|
def getEntryPoint(self):
|
|
for binary in self.__machoBinaries:
|
|
return binary.getEntryPoint()
|
|
|
|
def getArch(self):
|
|
for binary in self.__machoBinaries:
|
|
return binary.getArch()
|
|
|
|
def getArchMode(self):
|
|
for binary in self.__machoBinaries:
|
|
return binary.getArchMode()
|
|
|
|
|
|
|
|
def deleteDuplicateGadgets(currentGadgets):
|
|
gadgets_content_set = set()
|
|
unique_gadgets = []
|
|
for gadget in currentGadgets:
|
|
gad = gadget["gadget"]
|
|
if gad in gadgets_content_set:
|
|
continue
|
|
gadgets_content_set.add(gad)
|
|
unique_gadgets += [gadget]
|
|
return unique_gadgets
|
|
|
|
def alphaSortgadgets(currentGadgets):
|
|
return sorted(currentGadgets, key=lambda key : key["gadget"])
|
|
|
|
|
|
|
|
class Options:
|
|
def __init__(self, options, binary, gadgets):
|
|
self.__options = options
|
|
self.__gadgets = gadgets
|
|
self.__binary = binary
|
|
|
|
if options.filter: self.__filterOption()
|
|
if options.only: self.__onlyOption()
|
|
if options.range: self.__rangeOption()
|
|
if options.badbytes: self.__deleteBadBytes()
|
|
|
|
def __filterOption(self):
|
|
new = []
|
|
if not self.__options.filter:
|
|
return
|
|
filt = self.__options.filter.split("|")
|
|
if not len(filt):
|
|
return
|
|
for gadget in self.__gadgets:
|
|
flag = 0
|
|
insts = gadget["gadget"].split(" ; ")
|
|
for ins in insts:
|
|
if ins.split(" ")[0] in filt:
|
|
flag = 1
|
|
break
|
|
if not flag:
|
|
new += [gadget]
|
|
self.__gadgets = new
|
|
|
|
def __onlyOption(self):
|
|
new = []
|
|
if not self.__options.only:
|
|
return
|
|
only = self.__options.only.split("|")
|
|
if not len(only):
|
|
return
|
|
for gadget in self.__gadgets:
|
|
flag = 0
|
|
insts = gadget["gadget"].split(" ; ")
|
|
for ins in insts:
|
|
if ins.split(" ")[0] not in only:
|
|
flag = 1
|
|
break
|
|
if not flag:
|
|
new += [gadget]
|
|
self.__gadgets = new
|
|
|
|
def __rangeOption(self):
|
|
new = []
|
|
rangeS = int(self.__options.range.split('-')[0], 16)
|
|
rangeE = int(self.__options.range.split('-')[1], 16)
|
|
if rangeS == 0 and rangeE == 0:
|
|
return
|
|
for gadget in self.__gadgets:
|
|
vaddr = gadget["vaddr"]
|
|
if vaddr >= rangeS and vaddr <= rangeE:
|
|
new += [gadget]
|
|
self.__gadgets = new
|
|
|
|
def __deleteBadBytes(self):
|
|
if not self.__options.badbytes:
|
|
return
|
|
new = []
|
|
#Filter out empty badbytes (i.e if badbytes was set to 00|ff| there's an empty badbyte after the last '|')
|
|
#and convert each one to the corresponding byte
|
|
bbytes = [bb.decode('hex') for bb in self.__options.badbytes.split("|") if bb]
|
|
archMode = self.__binary.getArchMode()
|
|
for gadget in self.__gadgets:
|
|
gadAddr = pack("<L", gadget["vaddr"]) if archMode == CS_MODE_32 else pack("<Q", gadget["vaddr"])
|
|
try:
|
|
for x in bbytes:
|
|
if x in gadAddr: raise
|
|
new += [gadget]
|
|
except:
|
|
pass
|
|
self.__gadgets = new
|
|
|
|
def getGadgets(self):
|
|
return self.__gadgets
|
|
|
|
|
|
class ROPMakerX86:
|
|
def __init__(self, binary, gadgets, liboffset=0x0):
|
|
self.__binary = binary
|
|
self.__gadgets = gadgets
|
|
|
|
# If it's a library, we have the option to add an offset to the addresses
|
|
self.__liboffset = liboffset
|
|
|
|
self.__generate()
|
|
|
|
|
|
def __lookingForWrite4Where(self, gadgetsAlreadyTested):
|
|
for gadget in self.__gadgets:
|
|
if gadget in gadgetsAlreadyTested:
|
|
continue
|
|
f = gadget["gadget"].split(" ; ")[0]
|
|
# regex -> mov dword ptr [r32], r32
|
|
regex = re.search("mov dword ptr \[(?P<dst>([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P<src>([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))$", f)
|
|
if regex:
|
|
lg = gadget["gadget"].split(" ; ")[1:]
|
|
try:
|
|
for g in lg:
|
|
if g.split()[0] != "pop" and g.split()[0] != "ret":
|
|
raise
|
|
# we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
|
|
if g != "ret":
|
|
if g.split()[0] == "ret" and g.split()[1] != "":
|
|
raise
|
|
print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
|
|
return [gadget, regex.group("dst"), regex.group("src")]
|
|
except:
|
|
continue
|
|
return None
|
|
|
|
def __lookingForSomeThing(self, something):
|
|
for gadget in self.__gadgets:
|
|
lg = gadget["gadget"].split(" ; ")
|
|
if lg[0] == something:
|
|
try:
|
|
for g in lg[1:]:
|
|
if g.split()[0] != "pop" and g.split()[0] != "ret":
|
|
raise
|
|
# we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
|
|
if g != "ret":
|
|
if g.split()[0] == "ret" and g.split()[1] != "":
|
|
raise
|
|
print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
|
|
return gadget
|
|
except:
|
|
continue
|
|
return None
|
|
|
|
def __padding(self, gadget, regAlreadSetted):
|
|
lg = gadget["gadget"].split(" ; ")
|
|
for g in lg[1:]:
|
|
if g.split()[0] == "pop":
|
|
reg = g.split()[1]
|
|
try:
|
|
print("\tp += pack('<I', 0x%08x) # padding without overwrite %s" %(regAlreadSetted[reg], reg))
|
|
except KeyError:
|
|
print("\tp += pack('<I', 0x41414141) # padding")
|
|
|
|
def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall):
|
|
|
|
sects = self.__binary.getDataSections()
|
|
dataAddr = None
|
|
for s in sects:
|
|
if s["name"] == ".data":
|
|
dataAddr = s["vaddr"] + self.__liboffset
|
|
if dataAddr == None:
|
|
print("\n[-] Error - Can't find a writable section")
|
|
return
|
|
|
|
print("\t#!/usr/bin/env python2")
|
|
print("\t# execve generated by ROPgadget\n" )
|
|
print("\tfrom struct import pack\n")
|
|
|
|
print("\t# Padding goes here")
|
|
print("\tp = ''\n")
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
|
|
print("\tp += pack('<I', 0x%08x) # @ .data" %(dataAddr))
|
|
self.__padding(popDst, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
|
|
print("\tp += '/bin'")
|
|
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
|
|
self.__padding(write4where, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
|
|
print("\tp += pack('<I', 0x%08x) # @ .data + 4" %(dataAddr + 4))
|
|
self.__padding(popDst, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
|
|
print("\tp += '//sh'")
|
|
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr + 4}) # Don't overwrite reg dst
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
|
|
self.__padding(write4where, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popDst["vaddr"], popDst["gadget"]))
|
|
print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
|
|
self.__padding(popDst, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"]))
|
|
self.__padding(xorSrc, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(write4where["vaddr"], write4where["gadget"]))
|
|
self.__padding(write4where, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popEbx["vaddr"], popEbx["gadget"]))
|
|
print("\tp += pack('<I', 0x%08x) # @ .data" %(dataAddr))
|
|
self.__padding(popEbx, {})
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popEcx["vaddr"], popEcx["gadget"]))
|
|
print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
|
|
self.__padding(popEcx, {"ebx": dataAddr}) # Don't overwrite ebx
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(popEdx["vaddr"], popEdx["gadget"]))
|
|
print("\tp += pack('<I', 0x%08x) # @ .data + 8" %(dataAddr + 8))
|
|
self.__padding(popEdx, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(xorEax["vaddr"], xorEax["gadget"]))
|
|
self.__padding(xorEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx
|
|
|
|
for i in range(11):
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(incEax["vaddr"], incEax["gadget"]))
|
|
self.__padding(incEax, {"ebx": dataAddr, "ecx": dataAddr + 8}) # Don't overwrite ebx and ecx
|
|
|
|
print("\tp += pack('<I', 0x%08x) # %s" %(syscall["vaddr"], syscall["gadget"]))
|
|
|
|
def __generate(self):
|
|
|
|
# To find the smaller gadget
|
|
self.__gadgets.reverse()
|
|
|
|
print("\nROP chain generation\n===========================================================")
|
|
|
|
print("\n- Step 1 -- Write-what-where gadgets\n")
|
|
|
|
gadgetsAlreadyTested = []
|
|
while True:
|
|
write4where = self.__lookingForWrite4Where(gadgetsAlreadyTested)
|
|
if not write4where:
|
|
print("\t[-] Can't find the 'mov dword ptr [r32], r32' gadget")
|
|
return
|
|
|
|
popDst = self.__lookingForSomeThing("pop %s" %(write4where[1]))
|
|
if not popDst:
|
|
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[1]))
|
|
gadgetsAlreadyTested += [write4where[0]]
|
|
continue
|
|
|
|
popSrc = self.__lookingForSomeThing("pop %s" %(write4where[2]))
|
|
if not popSrc:
|
|
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2]))
|
|
gadgetsAlreadyTested += [write4where[0]]
|
|
continue
|
|
|
|
xorSrc = self.__lookingForSomeThing("xor %s, %s" %(write4where[2], write4where[2]))
|
|
if not xorSrc:
|
|
print("\t[-] Can't find the 'xor %s, %s' gadget. Try with another 'mov [r], r'\n" %(write4where[2], write4where[2]))
|
|
gadgetsAlreadyTested += [write4where[0]]
|
|
continue
|
|
else:
|
|
break
|
|
|
|
print("\n- Step 2 -- Init syscall number gadgets\n")
|
|
|
|
xorEax = self.__lookingForSomeThing("xor eax, eax")
|
|
if not xorEax:
|
|
print("\t[-] Can't find the 'xor eax, eax' instuction")
|
|
return
|
|
|
|
incEax = self.__lookingForSomeThing("inc eax")
|
|
if not incEax:
|
|
print("\t[-] Can't find the 'inc eax' instuction")
|
|
return
|
|
|
|
print("\n- Step 3 -- Init syscall arguments gadgets\n")
|
|
|
|
popEbx = self.__lookingForSomeThing("pop ebx")
|
|
if not popEbx:
|
|
print("\t[-] Can't find the 'pop ebx' instruction")
|
|
return
|
|
|
|
popEcx = self.__lookingForSomeThing("pop ecx")
|
|
if not popEcx:
|
|
print("\t[-] Can't find the 'pop ecx' instruction")
|
|
return
|
|
|
|
popEdx = self.__lookingForSomeThing("pop edx")
|
|
if not popEdx:
|
|
print("\t[-] Can't find the 'pop edx' instruction")
|
|
return
|
|
|
|
print("\n- Step 4 -- Syscall gadget\n")
|
|
|
|
syscall = self.__lookingForSomeThing("int 0x80")
|
|
if not syscall:
|
|
print("\t[-] Can't find the 'syscall' instruction")
|
|
return
|
|
|
|
print("\n- Step 5 -- Build the ROP chain\n")
|
|
|
|
self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorEax, incEax, popEbx, popEcx, popEdx, syscall)
|
|
|
|
class ROPMakerX64:
|
|
def __init__(self, binary, gadgets, liboffset=0x0):
|
|
self.__binary = binary
|
|
self.__gadgets = gadgets
|
|
|
|
# If it's a library, we have the option to add an offset to the addresses
|
|
self.__liboffset = liboffset
|
|
|
|
self.__generate()
|
|
|
|
|
|
def __lookingForWrite4Where(self, gadgetsAlreadyTested):
|
|
for gadget in self.__gadgets:
|
|
if gadget in gadgetsAlreadyTested:
|
|
continue
|
|
f = gadget["gadget"].split(" ; ")[0]
|
|
regex = re.search("mov .* ptr \[(?P<dst>([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))\], (?P<src>([(rax)|(rbx)|(rcx)|(rdx)|(rsi)|(rdi)|(r9)|(r10)|(r11)|(r12)|(r13)|(r14)|(r15)]{3}))$", f)
|
|
if regex:
|
|
lg = gadget["gadget"].split(" ; ")[1:]
|
|
try:
|
|
for g in lg:
|
|
if g.split()[0] != "pop" and g.split()[0] != "ret":
|
|
raise
|
|
# we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
|
|
if g != "ret":
|
|
if g.split()[0] == "ret" and g.split()[1] != "":
|
|
raise
|
|
print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
|
|
return [gadget, regex.group("dst"), regex.group("src")]
|
|
except:
|
|
continue
|
|
return None
|
|
|
|
def __lookingForSomeThing(self, something):
|
|
for gadget in self.__gadgets:
|
|
lg = gadget["gadget"].split(" ; ")
|
|
if lg[0] == something:
|
|
try:
|
|
for g in lg[1:]:
|
|
if g.split()[0] != "pop" and g.split()[0] != "ret":
|
|
raise
|
|
if g != "ret":
|
|
# we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer
|
|
if g.split()[0] == "ret" and g.split()[1] != "":
|
|
raise
|
|
print("\t[+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"]))
|
|
return gadget
|
|
except:
|
|
continue
|
|
return None
|
|
|
|
def __padding(self, gadget, regAlreadSetted):
|
|
lg = gadget["gadget"].split(" ; ")
|
|
for g in lg[1:]:
|
|
if g.split()[0] == "pop":
|
|
reg = g.split()[1]
|
|
try:
|
|
print("\tp += pack('<Q', 0x%016x) # padding without overwrite %s" %(regAlreadSetted[reg], reg))
|
|
except KeyError:
|
|
print("\tp += pack('<Q', 0x4141414141414141) # padding")
|
|
|
|
def __buildRopChain(self, write4where, popDst, popSrc, xorSrc, xorRax, incRax, popRdi, popRsi, popRdx, syscall):
|
|
|
|
sects = self.__binary.getDataSections()
|
|
dataAddr = None
|
|
for s in sects:
|
|
if s["name"] == ".data":
|
|
dataAddr = s["vaddr"] + self.__liboffset
|
|
if dataAddr is None:
|
|
print("\n[-] Error - Can't find a writable section")
|
|
return
|
|
|
|
print("\t#!/usr/bin/env python2")
|
|
print("\t# execve generated by ROPgadget\n")
|
|
print("\tfrom struct import pack\n")
|
|
|
|
print("\t# Padding goes here")
|
|
print("\tp = ''\n")
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(popDst["vaddr"], popDst["gadget"]))
|
|
print("\tp += pack('<Q', 0x%016x) # @ .data" %(dataAddr))
|
|
self.__padding(popDst, {})
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(popSrc["vaddr"], popSrc["gadget"]))
|
|
print("\tp += '/bin//sh'")
|
|
self.__padding(popSrc, {popDst["gadget"].split()[1]: dataAddr}) # Don't overwrite reg dst
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(write4where["vaddr"], write4where["gadget"]))
|
|
self.__padding(write4where, {})
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(popDst["vaddr"], popDst["gadget"]))
|
|
print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
|
|
self.__padding(popDst, {})
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(xorSrc["vaddr"], xorSrc["gadget"]))
|
|
self.__padding(xorSrc, {})
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(write4where["vaddr"], write4where["gadget"]))
|
|
self.__padding(write4where, {})
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(popRdi["vaddr"], popRdi["gadget"]))
|
|
print("\tp += pack('<Q', 0x%016x) # @ .data" %(dataAddr))
|
|
self.__padding(popRdi, {})
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(popRsi["vaddr"], popRsi["gadget"]))
|
|
print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
|
|
self.__padding(popRsi, {"rdi": dataAddr}) # Don't overwrite rdi
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(popRdx["vaddr"], popRdx["gadget"]))
|
|
print("\tp += pack('<Q', 0x%016x) # @ .data + 8" %(dataAddr + 8))
|
|
self.__padding(popRdx, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(xorRax["vaddr"], xorRax["gadget"]))
|
|
self.__padding(xorRax, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi
|
|
|
|
for i in range(59):
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(incRax["vaddr"], incRax["gadget"]))
|
|
self.__padding(incRax, {"rdi": dataAddr, "rsi": dataAddr + 8}) # Don't overwrite rdi and rsi
|
|
|
|
print("\tp += pack('<Q', 0x%016x) # %s" %(syscall["vaddr"], syscall["gadget"]))
|
|
|
|
def __generate(self):
|
|
|
|
# To find the smaller gadget
|
|
self.__gadgets.reverse()
|
|
|
|
print("\nROP chain generation\n===========================================================")
|
|
|
|
print("\n- Step 1 -- Write-what-where gadgets\n")
|
|
|
|
gadgetsAlreadyTested = []
|
|
while True:
|
|
write4where = self.__lookingForWrite4Where(gadgetsAlreadyTested)
|
|
if not write4where:
|
|
print("\t[-] Can't find the 'mov qword ptr [r64], r64' gadget")
|
|
return
|
|
|
|
popDst = self.__lookingForSomeThing("pop %s" %(write4where[1]))
|
|
if not popDst:
|
|
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[1]))
|
|
gadgetsAlreadyTested += [write4where[0]]
|
|
continue
|
|
|
|
popSrc = self.__lookingForSomeThing("pop %s" %(write4where[2]))
|
|
if not popSrc:
|
|
print("\t[-] Can't find the 'pop %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2]))
|
|
gadgetsAlreadyTested += [write4where[0]]
|
|
continue
|
|
|
|
xorSrc = self.__lookingForSomeThing("xor %s, %s" %(write4where[2], write4where[2]))
|
|
if not xorSrc:
|
|
print("\t[-] Can't find the 'xor %s, %s' gadget. Try with another 'mov [reg], reg'\n" %(write4where[2], write4where[2]))
|
|
gadgetsAlreadyTested += [write4where[0]]
|
|
continue
|
|
else:
|
|
break
|
|
|
|
print("\n- Step 2 -- Init syscall number gadgets\n")
|
|
|
|
xorRax = self.__lookingForSomeThing("xor rax, rax")
|
|
if not xorRax:
|
|
print("\t[-] Can't find the 'xor rax, rax' instuction")
|
|
return
|
|
|
|
incRax = self.__lookingForSomeThing("inc rax")
|
|
incEax = self.__lookingForSomeThing("inc eax")
|
|
incAx = self.__lookingForSomeThing("inc al")
|
|
addRax = self.__lookingForSomeThing("add rax, 1")
|
|
addEax = self.__lookingForSomeThing("add eax, 1")
|
|
addAx = self.__lookingForSomeThing("add al, 1")
|
|
|
|
instr = [incRax, incEax, incAx, addRax, addEax, addAx]
|
|
|
|
if all(v is None for v in instr):
|
|
print("\t[-] Can't find the 'inc rax' or 'add rax, 1' instuction")
|
|
return
|
|
|
|
for i in instr:
|
|
if i is not None:
|
|
incRax = i
|
|
break
|
|
|
|
print("\n- Step 3 -- Init syscall arguments gadgets\n")
|
|
|
|
popRdi = self.__lookingForSomeThing("pop rdi")
|
|
if not popRdi:
|
|
print("\t[-] Can't find the 'pop rdi' instruction")
|
|
return
|
|
|
|
popRsi = self.__lookingForSomeThing("pop rsi")
|
|
if not popRsi:
|
|
print("\t[-] Can't find the 'pop rsi' instruction")
|
|
return
|
|
|
|
popRdx = self.__lookingForSomeThing("pop rdx")
|
|
if not popRdx:
|
|
print("\t[-] Can't find the 'pop rdx' instruction")
|
|
return
|
|
|
|
print("\n- Step 4 -- Syscall gadget\n")
|
|
|
|
syscall = self.__lookingForSomeThing("syscall")
|
|
if not syscall:
|
|
print("\t[-] Can't find the 'syscall' instruction")
|
|
return
|
|
|
|
print("\n- Step 5 -- Build the ROP chain\n")
|
|
|
|
self.__buildRopChain(write4where[0], popDst, popSrc, xorSrc, xorRax, incRax, popRdi, popRsi, popRdx, syscall)
|
|
|
|
|
|
class ROPMaker:
|
|
def __init__(self, binary, gadgets, offset):
|
|
self.__binary = binary
|
|
self.__gadgets = gadgets
|
|
self.__offset = offset
|
|
|
|
self.__handlerArch()
|
|
|
|
def __handlerArch(self):
|
|
|
|
if self.__binary.getArch() == CS_ARCH_X86 \
|
|
and self.__binary.getArchMode() == CS_MODE_32 \
|
|
and self.__binary.getFormat() == "ELF":
|
|
ROPMakerX86(self.__binary, self.__gadgets, self.__offset)
|
|
|
|
elif self.__binary.getArch() == CS_ARCH_X86 \
|
|
and self.__binary.getArchMode() == CS_MODE_64 \
|
|
and self.__binary.getFormat() == "ELF":
|
|
ROPMakerX64(self.__binary, self.__gadgets, self.__offset)
|
|
|
|
else:
|
|
print("\n[Error] ROPMaker.__handlerArch - Arch not supported yet for the rop chain generation")
|
|
|
|
|
|
class Gadgets:
|
|
def __init__(self, binary, options, offset):
|
|
self.__binary = binary
|
|
self.__options = options
|
|
self.__offset = offset
|
|
|
|
|
|
def __checkInstructionBlackListedX86(self, insts):
|
|
bl = ["db", "int3"]
|
|
for inst in insts:
|
|
for b in bl:
|
|
if inst.split(" ")[0] == b:
|
|
return True
|
|
return False
|
|
|
|
def __checkMultiBr(self, insts, br):
|
|
count = 0
|
|
for inst in insts:
|
|
if inst.split()[0] in br:
|
|
count += 1
|
|
return count
|
|
|
|
def __passCleanX86(self, gadgets, multibr=False):
|
|
new = []
|
|
br = ["ret", "retf", "int", "sysenter", "jmp", "call", "syscall"]
|
|
for gadget in gadgets:
|
|
insts = gadget["gadget"].split(" ; ")
|
|
if len(insts) == 1 and insts[0].split(" ")[0] not in br:
|
|
continue
|
|
if insts[-1].split(" ")[0] not in br:
|
|
continue
|
|
if self.__checkInstructionBlackListedX86(insts):
|
|
continue
|
|
if not multibr and self.__checkMultiBr(insts, br) > 1:
|
|
continue
|
|
if len([m.start() for m in re.finditer("ret", gadget["gadget"])]) > 1:
|
|
continue
|
|
new += [gadget]
|
|
return new
|
|
|
|
def __gadgetsFinding(self, section, gadgets, arch, mode):
|
|
|
|
C_OP = 0
|
|
C_SIZE = 1
|
|
C_ALIGN = 2
|
|
|
|
ret = []
|
|
md = Cs(arch, mode)
|
|
for gad in gadgets:
|
|
|
|
allRefRet = [m.start() for m in re.finditer(gad[C_OP], section["opcodes"])]
|
|
for ref in allRefRet:
|
|
for i in range(self.__options.depth):
|
|
if (section["vaddr"]+ref-(i*gad[C_ALIGN])) % gad[C_ALIGN] == 0:
|
|
off = self.__offset
|
|
decodes = md.disasm(section["opcodes"][ref-(i*gad[C_ALIGN]):ref+gad[C_SIZE]], section["vaddr"]+ref)
|
|
gadget = ""
|
|
for decode in decodes:
|
|
gadget += (decode.mnemonic + " " + decode.op_str + " ; ").replace(" ", " ")
|
|
if len(gadget) > 0:
|
|
gadget = gadget[:-3]
|
|
off = self.__offset
|
|
ret += [{"vaddr" : off+section["vaddr"]+ref-(i*gad[C_ALIGN]), "gadget" : gadget, "decodes" : decodes, "bytes": section["opcodes"][ref-(i*gad[C_ALIGN]):ref+gad[C_SIZE]]}]
|
|
return ret
|
|
|
|
def addROPGadgets(self, section):
|
|
|
|
arch = self.__binary.getArch()
|
|
arch_mode = self.__binary.getArchMode()
|
|
|
|
if arch == CS_ARCH_X86:
|
|
gadgets = [
|
|
[b"\xc3", 1, 1], # ret
|
|
[b"\xc2[\x00-\xff]{2}", 3, 1], # ret <imm>
|
|
[b"\xcb", 1, 1], # retf
|
|
[b"\xca[\x00-\xff]{2}", 3, 1] # retf <imm>
|
|
]
|
|
|
|
elif arch == CS_ARCH_MIPS: gadgets = [] # MIPS doesn't contains RET instruction set. Only JOP gadgets
|
|
elif arch == CS_ARCH_PPC:
|
|
gadgets = [
|
|
[b"\x4e\x80\x00\x20", 4, 4] # blr
|
|
]
|
|
arch_mode = arch_mode + CS_MODE_BIG_ENDIAN
|
|
|
|
elif arch == CS_ARCH_SPARC:
|
|
gadgets = [
|
|
[b"\x81\xc3\xe0\x08", 4, 4], # retl
|
|
[b"\x81\xc7\xe0\x08", 4, 4], # ret
|
|
[b"\x81\xe8\x00\x00", 4, 4] # restore
|
|
]
|
|
arch_mode = CS_MODE_BIG_ENDIAN
|
|
|
|
elif arch == CS_ARCH_ARM: gadgets = [] # ARM doesn't contains RET instruction set. Only JOP gadgets
|
|
elif arch == CS_ARCH_ARM64:
|
|
gadgets = [
|
|
[b"\xc0\x03\x5f\xd6", 4, 4] # ret
|
|
]
|
|
arch_mode = CS_MODE_ARM
|
|
|
|
else:
|
|
print("Gadgets().addROPGadgets() - Architecture not supported")
|
|
return None
|
|
|
|
if len(gadgets) > 0 :
|
|
return self.__gadgetsFinding(section, gadgets, arch, arch_mode)
|
|
return gadgets
|
|
|
|
|
|
def addJOPGadgets(self, section):
|
|
arch = self.__binary.getArch()
|
|
arch_mode = self.__binary.getArchMode()
|
|
|
|
|
|
|
|
if arch == CS_ARCH_X86:
|
|
gadgets = [
|
|
[b"\xff[\x20\x21\x22\x23\x26\x27]{1}", 2, 1], # jmp [reg]
|
|
[b"\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 2, 1], # jmp [reg]
|
|
[b"\xff[\x10\x11\x12\x13\x16\x17]{1}", 2, 1], # jmp [reg]
|
|
[b"\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 2, 1] # call [reg]
|
|
]
|
|
|
|
|
|
elif arch == CS_ARCH_MIPS:
|
|
gadgets = [
|
|
[b"\x09\xf8\x20\x03[\x00-\xff]{4}", 8, 4], # jrl $t9
|
|
[b"\x08\x00\x20\x03[\x00-\xff]{4}", 8, 4], # jr $t9
|
|
[b"\x08\x00\xe0\x03[\x00-\xff]{4}", 8, 4] # jr $ra
|
|
]
|
|
elif arch == CS_ARCH_PPC: gadgets = [] # PPC architecture doesn't contains reg branch instruction
|
|
elif arch == CS_ARCH_SPARC:
|
|
gadgets = [
|
|
[b"\x81\xc0[\x00\x40\x80\xc0]{1}\x00", 4, 4] # jmp %g[0-3]
|
|
]
|
|
arch_mode = CS_MODE_BIG_ENDIAN
|
|
elif arch == CS_ARCH_ARM64:
|
|
gadgets = [
|
|
[b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00\x02]{1}\x1f\xd6", 4, 4], # br reg
|
|
[b"[\x00\x20\x40\x60\x80\xa0\xc0\xe0]{1}[\x00\x02]{1}\x5C\x3f\xd6", 4, 4] # blr reg
|
|
]
|
|
arch_mode = CS_MODE_ARM
|
|
elif arch == CS_ARCH_ARM:
|
|
if self.__options.thumb or self.__options.rawMode == "thumb":
|
|
gadgets = [
|
|
[b"[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}\x47", 2, 2], # bx reg
|
|
[b"[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}\x47", 2, 2], # blx reg
|
|
[b"[\x00-\xff]{1}\xbd", 2, 2] # pop {,pc}
|
|
]
|
|
arch_mode = CS_MODE_THUMB
|
|
else:
|
|
gadgets = [
|
|
[b"[\x10-\x19\x1e]{1}\xff\x2f\xe1", 4, 4], # bx reg
|
|
[b"[\x30-\x39\x3e]{1}\xff\x2f\xe1", 4, 4], # blx reg
|
|
[b"[\x00-\xff]{1}\x80\xbd\xe8", 4, 4] # pop {,pc}
|
|
]
|
|
arch_mode = CS_MODE_ARM
|
|
else:
|
|
print("Gadgets().addJOPGadgets() - Architecture not supported")
|
|
return None
|
|
|
|
if len(gadgets) > 0 :
|
|
return self.__gadgetsFinding(section, gadgets, arch, arch_mode)
|
|
return gadgets
|
|
|
|
|
|
def addSYSGadgets(self, section):
|
|
|
|
arch = self.__binary.getArch()
|
|
arch_mode = self.__binary.getArchMode()
|
|
|
|
if arch == CS_ARCH_X86:
|
|
gadgets = [
|
|
[b"\xcd\x80", 2, 1], # int 0x80
|
|
[b"\x0f\x34", 2, 1], # sysenter
|
|
[b"\x0f\x05", 2, 1], # syscall
|
|
[b"\x65\xff\x15\x10\x00\x00\x00", 7, 1], # call DWORD PTR gs:0x10
|
|
[b"\xcd\x80\xc3", 3, 1], # int 0x80 ; ret
|
|
[b"\x0f\x34\xc3", 3, 1], # sysenter ; ret
|
|
[b"\x0f\x05\xc3", 3, 1], # syscall ; ret
|
|
[b"\x65\xff\x15\x10\x00\x00\x00\xc3", 8, 1], # call DWORD PTR gs:0x10 ; ret
|
|
]
|
|
|
|
elif arch == CS_ARCH_MIPS:
|
|
gadgets = [
|
|
[b"\x0c\x00\x00\x00", 4, 4] # syscall
|
|
]
|
|
elif arch == CS_ARCH_PPC: gadgets = [] # TODO (sc inst)
|
|
elif arch == CS_ARCH_SPARC: gadgets = [] # TODO (ta inst)
|
|
elif arch == CS_ARCH_ARM64: gadgets = [] # TODO
|
|
elif arch == CS_ARCH_ARM:
|
|
if self.__options.thumb or self.__options.rawMode == "thumb":
|
|
gadgets = [
|
|
[b"\x00-\xff]{1}\xef", 2, 2] # svc
|
|
]
|
|
arch_mode = CS_MODE_THUMB
|
|
else:
|
|
gadgets = [
|
|
[b"\x00-\xff]{3}\xef", 4, 4] # svc
|
|
]
|
|
arch_mode = CS_MODE_ARM
|
|
else:
|
|
print("Gadgets().addSYSGadgets() - Architecture not supported")
|
|
return None
|
|
|
|
if len(gadgets) > 0 :
|
|
return self.__gadgetsFinding(section, gadgets, arch, arch_mode)
|
|
return []
|
|
|
|
|
|
def passClean(self, gadgets, multibr):
|
|
|
|
arch = self.__binary.getArch()
|
|
if arch == CS_ARCH_X86: return self.__passCleanX86(gadgets, multibr)
|
|
elif arch == CS_ARCH_MIPS: return gadgets
|
|
elif arch == CS_ARCH_PPC: return gadgets
|
|
elif arch == CS_ARCH_SPARC: return gadgets
|
|
elif arch == CS_ARCH_ARM: return gadgets
|
|
elif arch == CS_ARCH_ARM64: return gadgets
|
|
else:
|
|
print("Gadgets().passClean() - Architecture not supported")
|
|
return None
|
|
|
|
|
|
class Core(cmd.Cmd):
|
|
def __init__(self, options):
|
|
cmd.Cmd.__init__(self)
|
|
self.__options = options
|
|
self.__binary = None
|
|
self.__gadgets = []
|
|
self.__offset = 0
|
|
self.prompt = '(ROPgadget)> '
|
|
|
|
|
|
def __checksBeforeManipulations(self):
|
|
if self.__binary == None or self.__binary.getBinary() == None or self.__binary.getArch() == None or self.__binary.getArchMode() == None:
|
|
return False
|
|
return True
|
|
|
|
|
|
def __getAllgadgets(self):
|
|
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
G = Gadgets(self.__binary, self.__options, self.__offset)
|
|
execSections = self.__binary.getExecSections()
|
|
|
|
# Find ROP/JOP/SYS gadgets
|
|
self.__gadgets = []
|
|
for section in execSections:
|
|
if not self.__options.norop: self.__gadgets += G.addROPGadgets(section)
|
|
if not self.__options.nojop: self.__gadgets += G.addJOPGadgets(section)
|
|
if not self.__options.nosys: self.__gadgets += G.addSYSGadgets(section)
|
|
|
|
# Pass clean single instruction and unknown instructions
|
|
self.__gadgets = G.passClean(self.__gadgets, self.__options.multibr)
|
|
|
|
# Delete duplicate gadgets
|
|
if not self.__options.all:
|
|
self.__gadgets = deleteDuplicateGadgets(self.__gadgets)
|
|
|
|
# Applicate some Options
|
|
self.__gadgets = Options(self.__options, self.__binary, self.__gadgets).getGadgets()
|
|
|
|
# Sorted alphabetically
|
|
self.__gadgets = alphaSortgadgets(self.__gadgets)
|
|
|
|
return True
|
|
|
|
|
|
def __lookingForGadgets(self):
|
|
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
arch = self.__binary.getArchMode()
|
|
print("Gadgets information\n============================================================")
|
|
for gadget in self.__gadgets:
|
|
vaddr = gadget["vaddr"]
|
|
insts = gadget["gadget"]
|
|
bytes = gadget["bytes"]
|
|
bytesStr = " // " + bytes.encode('hex') if self.__options.dump else ""
|
|
|
|
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts) + bytesStr)
|
|
|
|
print("\nUnique gadgets found: %d" %(len(self.__gadgets)))
|
|
return True
|
|
|
|
|
|
def __lookingForAString(self, string):
|
|
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
dataSections = self.__binary.getDataSections()
|
|
arch = self.__binary.getArchMode()
|
|
print("Strings information\n============================================================")
|
|
for section in dataSections:
|
|
allRef = [m.start() for m in re.finditer(string, section["opcodes"])]
|
|
for ref in allRef:
|
|
vaddr = self.__offset + section["vaddr"] + ref
|
|
string = section["opcodes"][ref:ref+len(string)]
|
|
rangeS = int(self.__options.range.split('-')[0], 16)
|
|
rangeE = int(self.__options.range.split('-')[1], 16)
|
|
if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE):
|
|
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(string))
|
|
return True
|
|
|
|
|
|
def __lookingForOpcodes(self, opcodes):
|
|
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
execSections = self.__binary.getExecSections()
|
|
arch = self.__binary.getArchMode()
|
|
print("Opcodes information\n============================================================")
|
|
for section in execSections:
|
|
allRef = [m.start() for m in re.finditer(opcodes.decode("hex"), section["opcodes"])]
|
|
for ref in allRef:
|
|
vaddr = self.__offset + section["vaddr"] + ref
|
|
rangeS = int(self.__options.range.split('-')[0], 16)
|
|
rangeE = int(self.__options.range.split('-')[1], 16)
|
|
if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE):
|
|
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(opcodes))
|
|
return True
|
|
|
|
|
|
def __lookingForMemStr(self, memstr):
|
|
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
sections = self.__binary.getExecSections()
|
|
sections += self.__binary.getDataSections()
|
|
arch = self.__binary.getArchMode()
|
|
print("Memory bytes information\n=======================================================")
|
|
chars = list(memstr)
|
|
for char in chars:
|
|
try:
|
|
for section in sections:
|
|
allRef = [m.start() for m in re.finditer(char, section["opcodes"])]
|
|
for ref in allRef:
|
|
vaddr = self.__offset + section["vaddr"] + ref
|
|
rangeS = int(self.__options.range.split('-')[0], 16)
|
|
rangeE = int(self.__options.range.split('-')[1], 16)
|
|
if (rangeS == 0 and rangeE == 0) or (vaddr >= rangeS and vaddr <= rangeE):
|
|
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : '%c'" %(char))
|
|
raise
|
|
except:
|
|
pass
|
|
return True
|
|
|
|
|
|
def analyze(self):
|
|
|
|
try:
|
|
self.__offset = int(self.__options.offset, 16) if self.__options.offset else 0
|
|
except ValueError:
|
|
print("[Error] The offset must be in hexadecimal")
|
|
return False
|
|
|
|
if self.__options.console:
|
|
if self.__options.binary:
|
|
self.__binary = Binary(self.__options)
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
self.cmdloop()
|
|
return True
|
|
|
|
self.__binary = Binary(self.__options)
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
if self.__options.string: return self.__lookingForAString(self.__options.string)
|
|
elif self.__options.opcode: return self.__lookingForOpcodes(self.__options.opcode)
|
|
elif self.__options.memstr: return self.__lookingForMemStr(self.__options.memstr)
|
|
else:
|
|
self.__getAllgadgets()
|
|
self.__lookingForGadgets()
|
|
if self.__options.ropchain:
|
|
ROPMaker(self.__binary, self.__gadgets, self.__offset)
|
|
return True
|
|
|
|
|
|
def gadgets(self):
|
|
return self.__gadgets
|
|
|
|
|
|
|
|
|
|
# Console methods ============================================
|
|
|
|
def do_binary(self, s, silent=False):
|
|
# Do not split the filename with spaces since it might contain
|
|
# whitespaces
|
|
if len(s) == 0:
|
|
if not silent:
|
|
return self.help_binary()
|
|
return False
|
|
|
|
binary = s
|
|
|
|
self.__options.binary = binary
|
|
self.__binary = Binary(self.__options)
|
|
if self.__checksBeforeManipulations() == False:
|
|
return False
|
|
|
|
if not silent:
|
|
print("[+] Binary loaded")
|
|
|
|
|
|
def help_binary(self):
|
|
print("Syntax: binary <file> -- Load a binary")
|
|
return False
|
|
|
|
|
|
def do_EOF(self, s, silent=False):
|
|
return self.do_quit(s, silent)
|
|
|
|
def do_quit(self, s, silent=False):
|
|
return True
|
|
|
|
|
|
def help_quit(self):
|
|
print("Syntax: quit -- Terminates the application")
|
|
return False
|
|
|
|
|
|
def do_load(self, s, silent=False):
|
|
|
|
if self.__binary == None:
|
|
if not silent:
|
|
print("[-] No binary loaded.")
|
|
return False
|
|
|
|
if not silent:
|
|
print("[+] Loading gadgets, please wait...")
|
|
self.__getAllgadgets()
|
|
|
|
if not silent:
|
|
print("[+] Gadgets loaded !")
|
|
|
|
|
|
def help_load(self):
|
|
print("Syntax: load -- Load all gadgets")
|
|
return False
|
|
|
|
|
|
def do_display(self, s, silent=False):
|
|
self.__lookingForGadgets()
|
|
|
|
|
|
def help_display(self):
|
|
print("Syntax: display -- Display all gadgets loaded")
|
|
return False
|
|
|
|
|
|
def do_depth(self, s, silent=False):
|
|
try:
|
|
depth = int(s.split()[0])
|
|
except:
|
|
if not silent:
|
|
return self.help_depth()
|
|
return False
|
|
if depth <= 0:
|
|
if not silent:
|
|
print("[-] The depth value must be > 0")
|
|
return False
|
|
self.__options.depth = int(depth)
|
|
|
|
if not silent:
|
|
print("[+] Depth updated. You have to reload gadgets")
|
|
|
|
|
|
def help_depth(self):
|
|
print("Syntax: depth <value> -- Set the depth search engine")
|
|
return False
|
|
|
|
|
|
def do_badbytes(self, s, silent=False):
|
|
try:
|
|
bb = s.split()[0]
|
|
except:
|
|
if not silent:
|
|
return self.help_badbytes()
|
|
else:
|
|
return False
|
|
self.__options.badbytes = bb
|
|
|
|
if not silent:
|
|
print("[+] Bad bytes updated. You have to reload gadgets")
|
|
|
|
|
|
def help_badbytes(self):
|
|
print("Syntax: badbytes <badbyte1|badbyte2...> -- ")
|
|
return False
|
|
|
|
|
|
def __withK(self, listK, gadget):
|
|
if len(listK) == 0:
|
|
return True
|
|
for a in listK:
|
|
if a not in gadget:
|
|
return False
|
|
return True
|
|
|
|
def __withoutK(self, listK, gadget):
|
|
for a in listK:
|
|
if a in gadget:
|
|
return False
|
|
return True
|
|
|
|
def do_search(self, s, silent=False):
|
|
args = s.split()
|
|
if not len(args):
|
|
return self.help_search()
|
|
withK, withoutK = [], []
|
|
for a in args:
|
|
if a[0:1] == "!":
|
|
withoutK += [a[1:]]
|
|
else:
|
|
withK += [a]
|
|
if self.__checksBeforeManipulations() == False:
|
|
if not silent:
|
|
print("[-] You have to load a binary")
|
|
return False
|
|
arch = self.__binary.getArchMode()
|
|
for gadget in self.__gadgets:
|
|
vaddr = gadget["vaddr"]
|
|
insts = gadget["gadget"]
|
|
if self.__withK(withK, insts) and self.__withoutK(withoutK, insts):
|
|
# What to do if silent = True?
|
|
print(("0x%08x" %(vaddr) if arch == CS_MODE_32 else "0x%016x" %(vaddr)) + " : %s" %(insts))
|
|
|
|
|
|
def help_search(self):
|
|
print("Syntax: search <keyword1 keyword2 keyword3...> -- Filter with or without keywords")
|
|
print("keyword = with")
|
|
print("!keyword = witout")
|
|
return False
|
|
|
|
|
|
def count(self):
|
|
return len(self.__gadgets)
|
|
|
|
def do_count(self, s, silent=False):
|
|
if not silent:
|
|
print("[+] %d loaded gadgets." % self.count())
|
|
|
|
|
|
def help_count(self):
|
|
print("Shows the number of loaded gadgets.")
|
|
return False
|
|
|
|
|
|
def do_filter(self, s, silent=False):
|
|
try:
|
|
self.__options.filter = s.split()[0]
|
|
except:
|
|
if not silent:
|
|
return self.help_filter()
|
|
return False
|
|
|
|
if not silent:
|
|
print("[+] Filter setted. You have to reload gadgets")
|
|
|
|
|
|
def help_filter(self):
|
|
print("Syntax: filter <filter1|filter2|...> - Suppress specific instructions")
|
|
return False
|
|
|
|
|
|
def do_only(self, s, silent=False):
|
|
try:
|
|
self.__options.only = s.split()[0]
|
|
except:
|
|
if not silent:
|
|
return self.help_only()
|
|
return False
|
|
|
|
if not silent:
|
|
print("[+] Only setted. You have to reload gadgets")
|
|
|
|
|
|
def help_only(self):
|
|
print("Syntax: only <only1|only2|...> - Only show specific instructions")
|
|
return False
|
|
|
|
|
|
def do_range(self, s, silent=False):
|
|
try:
|
|
rangeS = int(s.split('-')[0], 16)
|
|
rangeE = int(s.split('-')[1], 16)
|
|
self.__options.range = s.split()[0]
|
|
except:
|
|
if not silent:
|
|
return self.help_range()
|
|
return False
|
|
|
|
if rangeS > rangeE:
|
|
if not silent:
|
|
print("[-] The start value must be greater than the end value")
|
|
return False
|
|
|
|
if not silent:
|
|
print("[+] Range setted. You have to reload gadgets")
|
|
|
|
|
|
def help_range(self):
|
|
print("Syntax: range <start-and> - Search between two addresses (0x...-0x...)")
|
|
return False
|
|
|
|
|
|
def do_settings(self, s, silent=False):
|
|
print("All: %s" %(self.__options.all))
|
|
print("Badbytes: %s" %(self.__options.badbytes))
|
|
print("Binary: %s" %(self.__options.binary))
|
|
print("Depth: %s" %(self.__options.depth))
|
|
print("Filter: %s" %(self.__options.filter))
|
|
print("Memstr: %s" %(self.__options.memstr))
|
|
print("MultiBr: %s" %(self.__options.multibr))
|
|
print("NoJOP: %s" %(self.__options.nojop))
|
|
print("NoROP: %s" %(self.__options.norop))
|
|
print("NoSYS: %s" %(self.__options.nosys))
|
|
print("Offset: %s" %(self.__options.offset))
|
|
print("Only: %s" %(self.__options.only))
|
|
print("Opcode: %s" %(self.__options.opcode))
|
|
print("ROPchain: %s" %(self.__options.ropchain))
|
|
print("Range: %s" %(self.__options.range))
|
|
print("RawArch: %s" %(self.__options.rawArch))
|
|
print("RawMode: %s" %(self.__options.rawMode))
|
|
print("String: %s" %(self.__options.string))
|
|
print("Thumb: %s" %(self.__options.thumb))
|
|
|
|
def help_settings(self):
|
|
print("Display setting's environment")
|
|
return False
|
|
|
|
|
|
def do_nojop(self, s, silent=False):
|
|
try:
|
|
arg = s.split()[0]
|
|
except:
|
|
return self.help_nojop()
|
|
|
|
if arg == "enable":
|
|
self.__options.nojop = True
|
|
if not silent:
|
|
print("[+] NoJOP enable. You have to reload gadgets")
|
|
|
|
elif arg == "disable":
|
|
self.__options.nojop = False
|
|
if not silent:
|
|
print("[+] NoJOP disable. You have to reload gadgets")
|
|
|
|
else:
|
|
if not silent:
|
|
return self.help_nojop()
|
|
return False
|
|
|
|
|
|
def help_nojop(self):
|
|
print("Syntax: nojop <enable|disable> - Disable JOP search engin")
|
|
return False
|
|
|
|
|
|
def do_norop(self, s, silent=False):
|
|
try:
|
|
arg = s.split()[0]
|
|
except:
|
|
return self.help_norop()
|
|
|
|
if arg == "enable":
|
|
self.__options.norop = True
|
|
if not silent:
|
|
print("[+] NoROP enable. You have to reload gadgets")
|
|
|
|
elif arg == "disable":
|
|
self.__options.norop = False
|
|
if not silent:
|
|
print("[+] NoROP disable. You have to reload gadgets")
|
|
|
|
else:
|
|
if not silent:
|
|
return self.help_norop()
|
|
return False
|
|
|
|
|
|
def help_norop(self):
|
|
print("Syntax: norop <enable|disable> - Disable ROP search engin")
|
|
return False
|
|
|
|
|
|
def do_nosys(self, s, silent=False):
|
|
try:
|
|
arg = s.split()[0]
|
|
except:
|
|
return self.help_nosys()
|
|
|
|
if arg == "enable":
|
|
self.__options.nosys = True
|
|
if not silent:
|
|
print("[+] NoSYS enable. You have to reload gadgets")
|
|
|
|
elif arg == "disable":
|
|
self.__options.nosys = False
|
|
if not silent:
|
|
print("[+] NoSYS disable. You have to reload gadgets")
|
|
|
|
else:
|
|
if not silent:
|
|
return self.help_nosys()
|
|
|
|
return False
|
|
|
|
|
|
def help_nosys(self):
|
|
print("Syntax: nosys <enable|disable> - Disable SYS search engin")
|
|
return False
|
|
|
|
|
|
def do_thumb(self, s, silent=False):
|
|
try:
|
|
arg = s.split()[0]
|
|
except:
|
|
return self.help_thumb()
|
|
|
|
if arg == "enable":
|
|
self.__options.thumb = True
|
|
if not silent:
|
|
print("[+] Thumb enable. You have to reload gadgets")
|
|
|
|
elif arg == "disable":
|
|
self.__options.thumb = False
|
|
if not silent:
|
|
print("[+] Thumb disable. You have to reload gadgets")
|
|
|
|
else:
|
|
if not silent:
|
|
return self.help_thumb()
|
|
return False
|
|
|
|
|
|
def help_thumb(self):
|
|
print("Syntax: thumb <enable|disable> - Use the thumb mode for the search engine (ARM only)")
|
|
return False
|
|
|
|
|
|
def do_all(self, s, silent=False):
|
|
if s == "enable":
|
|
self.__options.all = True
|
|
if not silent:
|
|
print("[+] Showing all gadgets enabled. You have to reload gadgets")
|
|
|
|
elif s == "disable":
|
|
self.__options.all = False
|
|
if not silent:
|
|
print("[+] Showing all gadgets disabled. You have to reload gadgets")
|
|
|
|
else:
|
|
if not silent:
|
|
return self.help_all()
|
|
|
|
return False
|
|
|
|
|
|
def help_multibr(self):
|
|
print("Syntax: multibr <enable|disable> - Enable/Disable multiple branch gadgets")
|
|
return False
|
|
|
|
|
|
def do_multibr(self, s, silent=False):
|
|
if s == "enable":
|
|
self.__options.multibr = True
|
|
if not silent:
|
|
print("[+] Multiple branch gadgets enabled. You have to reload gadgets")
|
|
|
|
elif s == "disable":
|
|
self.__options.multibr = False
|
|
if not silent:
|
|
print("[+] Multiple branch gadgets disabled. You have to reload gadgets")
|
|
|
|
else:
|
|
if not silent:
|
|
return self.help_all()
|
|
|
|
return False
|
|
|
|
|
|
def help_all(self):
|
|
print("Syntax: all <enable|disable - Show all gadgets (disable removing duplice gadgets)")
|
|
return False
|
|
|
|
class Binary:
|
|
def __init__(self, options):
|
|
self.__fileName = options.binary
|
|
self.__rawBinary = None
|
|
self.__binary = None
|
|
|
|
try:
|
|
fd = open(self.__fileName, "rb")
|
|
self.__rawBinary = fd.read()
|
|
fd.close()
|
|
except:
|
|
print("[Error] Can't open the binary or binary not found")
|
|
return None
|
|
|
|
if self.__rawBinary[:4] == unhexlify(b"cafebabe"):
|
|
self.__binary = UNIVERSAL(self.__rawBinary)
|
|
elif self.__rawBinary[:4] == unhexlify(b"cefaedfe") or self.__rawBinary[:4] == unhexlify(b"cffaedfe"):
|
|
self.__binary = MACHO(self.__rawBinary)
|
|
|
|
def getFileName(self):
|
|
return self.__fileName
|
|
|
|
def getRawBinary(self):
|
|
return self.__rawBinary
|
|
|
|
def getBinary(self):
|
|
return self.__binary
|
|
|
|
def getEntryPoint(self):
|
|
return self.__binary.getEntryPoint()
|
|
|
|
def getDataSections(self):
|
|
return self.__binary.getDataSections()
|
|
|
|
def getExecSections(self):
|
|
return self.__binary.getExecSections()
|
|
|
|
def getArch(self):
|
|
return self.__binary.getArch()
|
|
|
|
def getArchMode(self):
|
|
return self.__binary.getArchMode()
|
|
|
|
def getFormat(self):
|
|
return self.__binary.getFormat()
|
|
class Args:
|
|
def __init__(self, arguments=None):
|
|
self.__args = None
|
|
custom_arguments_provided = True
|
|
|
|
# If no custom arguments are provided, use the program arguments
|
|
if not arguments:
|
|
arguments = args
|
|
custom_arguments_provided = False
|
|
|
|
sys.argv=arguments
|
|
self.__parse(arguments, custom_arguments_provided)
|
|
|
|
def __parse(self, arguments, custom_arguments_provided=False):
|
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description="""description:
|
|
rop(ROPgadget) lets you search your gadgets on a binary. It supports several
|
|
file formats and architectures and uses the Capstone disassembler for
|
|
the search engine.
|
|
|
|
formats supported:
|
|
- ELF
|
|
- PE
|
|
- Mach-O
|
|
- Raw
|
|
|
|
architectures supported:
|
|
- x86
|
|
- x86-64
|
|
- ARM
|
|
- ARM64
|
|
- MIPS
|
|
- PowerPC
|
|
- Sparc
|
|
""",
|
|
epilog="""examples:
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --ropchain
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --depth 3
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --string "main"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --console
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|7f|42"
|
|
rop --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000
|
|
rop --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5
|
|
rop --binary ./test-suite-binaries/elf-ARM64-bash --depth 5
|
|
rop --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32""")
|
|
|
|
parser.add_argument("--binary", type=str, metavar="<binary>", help="Specify a binary filename to analyze")
|
|
parser.add_argument("--opcode", type=str, metavar="<opcodes>", help="Search opcode in executable segment")
|
|
parser.add_argument("--string", type=str, metavar="<string>", help="Search string in readable segment")
|
|
parser.add_argument("--memstr", type=str, metavar="<string>", help="Search each byte in all readable segment")
|
|
parser.add_argument("--depth", type=int, metavar="<nbyte>", default=10, help="Depth for search engine (default 10)")
|
|
parser.add_argument("--only", type=str, metavar="<key>", help="Only show specific instructions")
|
|
parser.add_argument("--filter", type=str, metavar="<key>", help="Suppress specific instructions")
|
|
parser.add_argument("--range", type=str, metavar="<start-end>", default="0x0-0x0", help="Search between two addresses (0x...-0x...)")
|
|
parser.add_argument("--badbytes", type=str, metavar="<byte>", help="Rejects specific bytes in the gadget's address")
|
|
parser.add_argument("--rawArch", type=str, metavar="<arch>", help="Specify an arch for a raw file")
|
|
parser.add_argument("--rawMode", type=str, metavar="<mode>", help="Specify a mode for a raw file")
|
|
parser.add_argument("--offset", type=str, metavar="<hexaddr>", help="Specify an offset for gadget addresses")
|
|
parser.add_argument("--ropchain", action="store_true", help="Enable the ROP chain generation")
|
|
parser.add_argument("--thumb" , action="store_true", help="Use the thumb mode for the search engine (ARM only)")
|
|
parser.add_argument("--console", action="store_true", help="Use an interactive console for search engine")
|
|
parser.add_argument("--norop", action="store_true", help="Disable ROP search engine")
|
|
parser.add_argument("--nojop", action="store_true", help="Disable JOP search engine")
|
|
parser.add_argument("--nosys", action="store_true", help="Disable SYS search engine")
|
|
parser.add_argument("--multibr", action="store_true", help="Enable multiple branch gadgets")
|
|
parser.add_argument("--all", action="store_true", help="Disables the removal of duplicate gadgets")
|
|
parser.add_argument("--dump", action="store_true", help="Outputs the gadget bytes")
|
|
|
|
self.__args = parser.parse_args(arguments)
|
|
|
|
if self.__args.depth < 2:
|
|
print("[Error] The depth must be >= 2")
|
|
sys.exit(-1)
|
|
|
|
elif not custom_arguments_provided and not self.__args.binary and not self.__args.console:
|
|
print("[Error] Need a binary filename (--binary/--console or --help)")
|
|
sys.exit(-1)
|
|
|
|
elif self.__args.range:
|
|
try:
|
|
rangeS = int(self.__args.range.split('-')[0], 16)
|
|
rangeE = int(self.__args.range.split('-')[1], 16)
|
|
except:
|
|
print("[Error] A range must be set in hexadecimal. Ex: 0x08041000-0x08042000")
|
|
sys.exit(-1)
|
|
if rangeS > rangeE:
|
|
print("[Error] The start value must be greater than end value")
|
|
sys.exit(-1)
|
|
|
|
def __printVersion(self):
|
|
print("Version: %s" %(PYROPGADGET_VERSION))
|
|
print("Author: Jonathan Salwan" )
|
|
print("Author page: https://twitter.com/JonathanSalwan" )
|
|
print("Project page: http://shell-storm.org/project/ROPgadget/" )
|
|
|
|
def getArgs(self):
|
|
return self.__args
|
|
|
|
def rop(debugger,args,result,dict):
|
|
args=shlex.split(args)
|
|
|
|
if args:
|
|
Core(Args(arguments=args).getArgs()).analyze()
|
|
else:
|
|
print """description:
|
|
rop(ROPgadget) lets you search your gadgets on a binary. It supports several
|
|
file formats and architectures and uses the Capstone disassembler for
|
|
the search engine.
|
|
|
|
formats supported:
|
|
- ELF
|
|
- PE
|
|
- Mach-O
|
|
- Raw
|
|
|
|
architectures supported:
|
|
- x86
|
|
- x86-64
|
|
- ARM
|
|
- ARM64
|
|
- MIPS
|
|
- PowerPC
|
|
- Sparc
|
|
epilog=examples:
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --ropchain
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --depth 3
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --string "main"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh"
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --console
|
|
rop --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|7f|42"
|
|
rop --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000
|
|
rop --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5
|
|
rop --binary ./test-suite-binaries/elf-ARM64-bash --depth 5
|
|
rop --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32"""
|
|
|
|
def __lldb_init_module(debugger, dict):
|
|
|
|
debugger.HandleCommand('command script add --function lisa.exploitable exploitable')
|
|
debugger.HandleCommand('command script add --function lisa.pattern_create pattern_create')
|
|
debugger.HandleCommand('command script add --function lisa.pattern_offset pattern_offset')
|
|
debugger.HandleCommand('command script add --function lisa.check_if_cyclic check_if_cyclic')
|
|
debugger.HandleCommand('command script add --function lisa.stepnInstructions sf')
|
|
debugger.HandleCommand('command script add --function lisa.context ct')
|
|
debugger.HandleCommand('command script add --function lisa.s s')
|
|
debugger.HandleCommand('command script add --function lisa.si si')
|
|
debugger.HandleCommand('command script add --function lisa.so so')
|
|
debugger.HandleCommand('command script add --function lisa.banner banner')
|
|
debugger.HandleCommand('command script add --function lisa.exploitable exploitable')
|
|
debugger.HandleCommand('command script add -function lisa.setMallocDebug setmalloc')
|
|
debugger.HandleCommand('command script add -function lisa.shellcode shellcode')
|
|
debugger.HandleCommand('command script add -function lisa.extract_from_universal_binary extract')
|
|
debugger.HandleCommand('command script add --function lisa.dump dump')
|
|
debugger.HandleCommand('command script add --function lisa.rop rop')
|
|
|
|
tty_colors = TerminalColors (True)
|