Angr_CTF
https://github.com/jakespringer/angr_ctf/tree/master
PS:可执行程序存放于dist目录
00_angr_find
程序获取输入,经过一些加密之后与字符串JACEJGCS进行比较,校验成功后打印Good Job.,这里将通过angr自己去寻找一条能通过校验的路径
import angr
#auto_load_libs=False 是否载入程序依赖的库,这里还不需要
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/00_angr_find',auto_load_libs=False)
init_state = io.factory.entry_state() #初始化状态为程序运行到程序入口点的状态
#factory负责将Project实例化
simgr = io.factory.simgr(init_state) #创建模拟管理器,将初始化后的state添加到SM中
target = 0x8048678 #目标地址
simgr.explore(find=target) #搜索路径,模拟管理器将尝试找到一个执行路径,使程序到达目标地址
if simgr.found: #找到满足条件的路径
so_state = simgr.found[0] #获取第一个满足条件的状态
print(so_state.posix.dumps(0)) #打印标准输入(文件描述符0)的内容==》程序运行过程中的输入内容
01_angr_avoid
在我们尝试在在IDA中查看main函数的伪代码时会提示函数过大,反编译main是比较困难了==》看看都有什么其他函数:
maybe_good:并查看交叉引用,可以看到被main调用了非常多次,怪不得会这么大
avoid_me:并查看交叉引用,可以看到函数也同样被调用多次,并注意到函数名是作者提供给我们的提示,avoid这个函数,并注意到这个函数只有一个作用,就是让should_succeed为0
而在maybe_good中则需要should_succeed为1才有通过校验的可能,所以我们只要经过了这个函数,程序将完全进入死胡同,不能获取我们想要的结果Good Job.==》恰好angr在explore中提供了avoid参数,用于避开这种路径分支
import angr
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/01_angr_avoid',auto_load_libs=False)
init_state = io.factory.entry_state()
simgr = io.factory.simgr(init_state)
target = 0x80485E0 #目标地址
un_target = 0x80485A8 #不想被执行的地址
#avoid=un_target为不想执行这里
simgr.explore(find=target,avoid=un_target)
if simgr.found:
so_state = simgr.found[0]
print(so_state.posix.dumps(0))
02_angr_find_condition
与之前的逻辑并没有什么不同,都是接收输入,将输入进行一些加密处理再进行校验
但是这题的作者是想告诉我们可以去动态的选择想要的state,而去使用explore设置一个固定的地址
import angr
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/02_angr_find_condition',auto_load_libs=False)
init_state = io.factory.entry_state()
simgr = io.factory.simgr(init_state)
#通过引入检测函数实现动态的选择想获取的state
def is_succ(state):
#将标准输出的内容存储到变量std_out中
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
#采用状态检测
simgr.explore(find=is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
print(so_state.posix.dumps(0))
03_angr_symbolic_registers
程序将接收我们3个十六进制值作为输入,存放于eax,ebx,edx
接下来将通过这几个寄存器进行传值,所以需要通过angr对这几个寄存器的值进行修改,所以,我们将需要跳过这个输入的函数,让程序开始的地址设置在输入之后(0x8048980),这样我们直接对寄存器赋值也起到了类似输入的功能,只是这样我们将更加方便
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/03_angr_symbolic_registers',auto_load_libs=False)
state_addr = 0x8048980
init_state = io.factory.blank_state(addr = state_addr) #从目标地址开始执行
passwd_size = 32 #符号向量大小
#通过claripy创建符号向量
passwd0 = claripy.BVS('passwd0',passwd_size)
passwd1 = claripy.BVS('passwd1',passwd_size)
passwd2 = claripy.BVS('passwd2',passwd_size)
#对寄存器进行赋值
init_state.regs.eax = passwd0
init_state.regs.ebx = passwd1
init_state.regs.edx = passwd2
simgr = io.factory.simgr(init_state)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr.explore(find=is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = hex(so_state.solver.eval(passwd0))
so1 = hex(so_state.solver.eval(passwd1))
so2 = hex(so_state.solver.eval(passwd2))
print(so0,so1,so2)
04_angr_symbolic_stack
程序通过scanf获取两个无符号整型存放在栈中(上一题是寄存器),为此,可以通过angr注入栈
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/04_angr_symbolic_stack',auto_load_libs=False)
state_addr = 0x8048694
init_state = io.factory.blank_state(addr = state_addr) #跳过输入
passwd_size = 32
passwd0 = claripy.BVS('passwd0',passwd_size)
passwd1 = claripy.BVS('passwd1',passwd_size)
esp_tmp = init_state.regs.esp #记录esp
init_state.regs.esp = init_state.regs.ebp-0x8 #抬高栈
init_state.stack_push(passwd0) #每一个push都将esp+4
init_state.stack_push(passwd1)
init_state.regs.esp = esp_tmp #复原esp
simgr = io.factory.simgr(init_state)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr.explore(find=is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.solver.eval(passwd0)
so1 = so_state.solver.eval(passwd1)
print(so0,so1)
05_angr_symbolic_memory
程序将获取4个值,并将它们存储到bss段上,我们也可以通过angr实现将符号向量注入bss段中
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/05_angr_symbolic_memory',auto_load_libs=False)
state_addr = 0x80485FE #跳过输入
init_state = io.factory.blank_state(addr = state_addr)
#%8s
passwd_size = 8*8 #8字节长
passwd0 = claripy.BVS('passwd0',passwd_size)
passwd1 = claripy.BVS('passwd1',passwd_size)
passwd2 = claripy.BVS('passwd2',passwd_size)
passwd3 = claripy.BVS('passwd3',passwd_size)
input_addr = 0xA1BA1C0 #获取输入存储的bss段地址
#通过memory写入内存
init_state.memory.store(input_addr,passwd0)
init_state.memory.store(input_addr+0x8,passwd1)
init_state.memory.store(input_addr+0x10,passwd2)
init_state.memory.store(input_addr+0x18,passwd3)
simgr = io.factory.simgr(init_state)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr.explore(find=is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
#%8s %8s %8s %8s
so0 = so_state.solver.eval(passwd0,cast_to=bytes)
so1 = so_state.solver.eval(passwd1,cast_to=bytes)
so2 = so_state.solver.eval(passwd2,cast_to=bytes)
so3 = so_state.solver.eval(passwd3,cast_to=bytes)
print(so0,so1,so2,so3)
06_angr_symbolic_dynamic_memory
程序通过malloc申请了2块9字节大小的缓冲区,将这两块缓冲区的地址保存在bss段上,也就是buffer0和buffer1中,之后就是向这两块缓冲区中写入内容,在经过complex_function函数处理后进行校验
由于我们需要跳过输入,所以所以需要通过angr去伪造两块缓冲区的地址,再往这两块缓冲区去写入符号向量
程序中存在很大的bss段,可以考虑在其中伪造缓冲区
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/06_angr_symbolic_dynamic_memory',auto_load_libs=False)
state_addr = 0x8048696
init_state = io.factory.blank_state(addr = state_addr)
passwd_size = 8*8
passwd0 = claripy.BVS('passwd0',passwd_size)
passwd1 = claripy.BVS('passwd1',passwd_size)
buffer0_addr = 0xABCC8A4
buffer1_addr = 0xABCC8AC
#伪造缓冲区
fake_chunk0_addr= 0x9999990
fake_chunk1_addr= 0x99999A0
#endness设置端序与程序相同
init_state.memory.store(buffer0_addr,fake_chunk0_addr,endness = io.arch.memory_endness)
init_state.memory.store(buffer1_addr,fake_chunk1_addr,endness = io.arch.memory_endness)
#写入伪造的地址,符号向量不是具体的值,不需要考虑端序
init_state.memory.store(fake_chunk0_addr,passwd0)
init_state.memory.store(fake_chunk1_addr,passwd1)
simgr = io.factory.simgr(init_state)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr.explore(find=is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.solver.eval(passwd0,cast_to=bytes)
so1 = so_state.solver.eval(passwd1,cast_to=bytes)
print(so0,so1)
07_angr_symbolic_file
程序将读取我们的输入通过ignore_me函数存储入OJKSQYDP.txt中,后续再通过从OJKSQYDP.txt中取出进行校验
我们可以通过angr跳过读入存储OJKSQYDP.txt的过程,转而去自己实现一个符号化文件
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/07_angr_symbolic_file',auto_load_libs=False)
state_addr = 0x80488E7
init_state = io.factory.blank_state(addr = state_addr)
passwd0 = claripy.BVS('passwd0',64*8)
file_name = 'OJKSQYDP.txt'
#%64s
#通过SimFile形成符号化文件
simfile = angr.storage.SimFile(name = file_name,content = passwd0,size = 64)
#将SimFile插入state文件系统
init_state.fs.insert(file_name,simfile)
simgr = io.factory.simgr(init_state)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr.explore(find=is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.solver.eval(passwd0,cast_to=bytes)
print(format(so0.decode('utf-8')))
08_angr_constraints
程序一样是获取输入,然后对输入进行加密处理,再由校验函数去校验
但是与之前直接使用strcmp校验不同,这里使用的是一个自定义的按位校验,并且由于输入是16位,就将会进行16次的循环,每次循环都将经历一次if判断==》将会产生2^16 == 65536个判断分支==》这么多的分支,就将会引发一个叫路径爆炸的问题,严重影响我们测试的效率==》为此,我们可以自己去实现一个校验约束,直接跳过或者也可以理解为hook掉这个按位校验函数,这样就不会产生路径爆炸了
你可能会有疑问,strcmp函数在底层实现也是按位比较,为什么在前面的题目中并没有提及路径爆炸问题==》原因是angr在对于strcmp这种标准库自己实现了一套hook,使用了angr实现的strcmp去替换掉了标准库中调用的strcmp函数,避免了路径爆炸,这在下文中也有提及
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/08_angr_constraints',auto_load_libs=False)
state_addr = 0x8048622
init_state = io.factory.blank_state(addr = state_addr)
#%16s
passwd0 = claripy.BVS('passwd0',16*8)
buffer_addr = 0x804A050
init_state.memory.store(buffer_addr,passwd0)
simgr = io.factory.simgr(init_state)
#运行至调用check
check_addr = 0x8048565
simgr.explore(find=check_addr)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
if simgr.found:
so_state = simgr.found[0]
buffer_cu = so_state.memory.load(buffer_addr,16) #读出buffer处的数据
key = "AUPDNNPROEZRJWKB"
#添加约束条件,自己实现一个校验
so_state.solver.add(buffer_cu == key)
#eval将对约束进行求解,也就是获取符合条件的值
so0 = so_state.solver.eval(passwd0,cast_to=bytes)
print(format(so0.decode('utf-8')))
09_angr_hooks
程序将获取两次输入,第一次输入经过complex_function处理后,再通过check_equals_XYMKBKUHNIQYNQXE与password进行比较;第二次输入将与经过complex_function处理后的password进行比较;并且可以看到在check_equals_XYMKBKUHNIQYNQXE中使用按位比较==》将会出现路径爆炸问题
==》通过angr hook check_equals_XYMKBKUHNIQYNQXE函数(这与上一题的思想并不太相同,这里才是真正的hook,上一题只是执行到了check之前,并没有真正调用,而这一题则是真正调用了check函数,调用了我们hook之后的代码)
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/09_angr_hooks',auto_load_libs=False)
init_state = io.factory.entry_state()
check_addr = 0x80486B3 #call check指令地址
call_check_len = 5 #指令长度
#通过地址进行HOOK
@io.hook(check_addr,length=call_check_len)
def hook_check(state):
buffer_addr = 0x804A054
#%16s
buffer = state.memory.load(buffer_addr,16) #读取
key = "XYMKBKUHNIQYNQXE"
#返回值存储在eax
state.regs.eax = claripy.If(
buffer == key,
claripy.BVV(1,32), #32位寄存器
claripy.BVV(0,32) #32位寄存器
)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr = io.factory.simgr(init_state)
simgr.explore(find = is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.posix.dumps(0)
print(format(so0.decode('utf-8')))
10_angr_simprocedures
与上一题类似,但是本题的check函数被多次调用,已经是很难再使用地址去hook了==》使用函数名进行hook
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/10_angr_simprocedures',auto_load_libs=False)
init_state = io.factory.entry_state()
#继承SimProcedure
class Hook(angr.SimProcedure):
#参照函数原型进行hook
def run(self,a1,a2):
buffer_addr = a1 #原函数1参数
buffer_len = a2 #原函数2参数
#读取
buffer = self.state.memory.load(
buffer_addr,
buffer_len
)
key = "ORSDDWXHZURJRBDH"
#原函数有返回值
return claripy.If(
buffer == key,
claripy.BVV(1,32),
claripy.BVV(0,32)
)
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
check_sym = "check_equals_ORSDDWXHZURJRBDH" #符号表获取
io.hook_symbol(check_sym,Hook()) #angr会自己去找和函数符号有关联的地址
simgr = io.factory.simgr(init_state)
simgr.explore(find = is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.posix.dumps(0)
print(format(so0.decode('utf-8')))
11_angr_sim_scanf
程序通过__isoc99_scanf获取2个输入,之后进行分段的校验==》将通过angr对__isoc99_scanf进行hook
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/11_angr_sim_scanf',auto_load_libs=False)
init_state = io.factory.entry_state()
class Hook(angr.SimProcedure):
#参照函数原型进行hook
def run(self,format_string,buffer0_addr,buffer1_addr):
scanf0 = claripy.BVS('scanf0',32)
scanf1 = claripy.BVS('scanf1',32)
#原函数向buffer写入数据
#向地址内写入符号位向量
self.state.memory.store(
buffer0_addr,
scanf0,
endness = io.arch.memory_endness
)
self.state.memory.store(
buffer1_addr,
scanf1,
endness = io.arch.memory_endness
)
#保存符号位变量(局部变量)为全局变量,方便我们后续访问
self.state.globals['so0'] = scanf0
self.state.globals['so1'] = scanf1
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
scanf_sym = "__isoc99_scanf"
io.hook_symbol(scanf_sym,Hook())
simgr = io.factory.simgr(init_state)
simgr.explore(find = is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.globals['so0'] #访问全局变量,获取指定的位向量
so1 = so_state.globals['so1']
scanf0_so = so_state.solver.eval(so0) #求解
scanf1_so = so_state.solver.eval(so1)
print(format(scanf0_so))
print(format(scanf1_so))
12_angr_veritesting
程序将进行一个按位的加密,这里将会出现路径爆炸==》angr提供了veritesting去避免路径爆炸,只需启用即可
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/12_angr_veritesting',auto_load_libs=False)
init_state = io.factory.entry_state()
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
simgr = io.factory.simgr(init_state,veritesting = True) #开启veritesting
simgr.explore(find = is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.posix.dumps(0)
print(format(so0.decode('utf-8')))
13_angr_static_binary
是一个静态编译的程序,通常,angr会自动使用速度快得多的simprocedure代替标准库==》就像本来strcmp的实现也是按位比较,但是在前面为什么不会在strcmp上发生路径爆炸,因为angr已经自动给这些函数hook了(上文中已经提到过)==》但是本体采用静态编译,angr没法自动hook,需要手动去hook在使用的标准库的C函数:
simprocedure:两层结构,一层包名,一层函数名
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/13_angr_static_binary',auto_load_libs=False)
init_state = io.factory.entry_state()
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
#手动hook
io.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']())
io.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']())
io.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']())
io.hook(0x8048280, angr.SIM_PROCEDURES['libc']['strcmp']())
io.hook_symbol('__libc_start_main',angr.SIM_PROCEDURES['glibc']['__libc_start_main']())
simgr = io.factory.simgr(init_state)
simgr.explore(find = is_succ,avoid=is_fail)
if simgr.found:
so_state = simgr.found[0]
so0 = so_state.posix.dumps(0)
print(format(so0.decode('utf-8')))
14_angr_shared_library
发现这个校验来自动态链接库lib14_angr_shared_library.so
==》基本逻辑和上文的题目大差不差,区别就是本体需要对lib14_angr_shared_library.so进行符号执行
==》checksec可以方便获取基地址
import angr
import claripy
import sys
def is_succ(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.' in std_out:
return True
else:
return False
def is_fail(state):
std_out = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in std_out:
return True
else:
return False
libc_so = '/home/closure/Desktop/CTF/angr_ctf/dist/lib14_angr_shared_library.so'
libc_base = 0x8048000 #基地址
io = angr.Project(libc_so,load_options={
'main_opts':{
'custom_base_addr':libc_base #基地址
}
})
validate_addr = libc_base+0x6D7 # + 偏移 = 目标函数地址
#int为4字节==》4*8=32
buffer_pointer = claripy.BVV(0x3000000, 32) #位向量,0x3000000为缓冲区地址,只要不影响程序执行即可
init_state = io.factory.call_state(validate_addr, buffer_pointer, claripy.BVV(8, 32))
password = claripy.BVS('password', 8*8)
init_state.memory.store(buffer_pointer, password)
simgr = io.factory.simgr(init_state)
success_address = libc_base + 0x783
simgr.explore(find=success_address)
if simgr.found:
so_state = simgr.found[0]
so_state.add_constraints(so_state.regs.eax == 1)
so0 = so_state.solver.eval(password,cast_to=bytes)
print(format(so0.decode('utf-8')))
15_angr_arbitrary_read
程序通过scanf获取输入,第一个key经过校验后将使用puts输出,但是都为try_again,但是一个s(try_again)在栈上,而我们的第二个输入也将写入栈上,并且输入长度并没有限制,也就是说可以覆盖s为Good Job.==》
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/15_angr_arbitrary_read',auto_load_libs=False)
init_state = io.factory.entry_state()
class Hook(angr.SimProcedure):
#hook scanf
def run(self,format_string,key_addr,password_addr):
#无符号整数
key_bvs = claripy.BVS('key_bvs',32)
#padding 20个字符 * 8 = 160比特
password_addr_bvs = claripy.BVS('password_addr_bvs',20*8)
#确保字符串中的每个字符都是可打印的,安装8比特1字符分组
for char in password_addr_bvs.chop(bits=8):
self.state.add_constraints(char >= 'A', char <= 'Z')
self.state.memory.store(
key_addr,
key_bvs,
endness = io.arch.memory_endness
)
self.state.memory.store(
password_addr,
password_addr_bvs
)
self.state.globals['solutions'] = (key_bvs, password_addr_bvs)
scanf_sym = "__isoc99_scanf"
io.hook_symbol(scanf_sym,Hook())
simgr = io.factory.simgr(init_state)
#判断是否为正确状态
def success(state):
#根据调用puts判断
jmp_puts_addr = 0x8048370
if state.addr != jmp_puts_addr:return False
goodjob_addr = 0x484F4A47
#提取数据
puts_param = state.memory.load(state.regs.esp+4,
4,
endness = io.arch.memory_endness)
if state.se.symbolic(puts_param):
#拷贝状态,不对原状态产生影响
cp_state = state.copy()
#判断提取出来的数据是否是为目标字符串所在地址
cp_state.add_constraints(puts_param == goodjob_addr)
if cp_state.satisfiable():
#正确则返回
state.add_constraints(puts_param == goodjob_addr)
return True
else:
return False
else:
return False
simgr.explore(find = success)
if simgr.found:
so_state = simgr.found[0]
(key_so,password_so) = so_state.globals['solutions']
so0 = so_state.solver.eval(key_so)
so1 = so_state.solver.eval(password_so,cast_to=bytes)
print(so0,so1)
16_angr_arbitrary_write
程序获取输入,首先是第一个判断key,如果校验成功将s指向的内容写入dest指向的数组,然后就是校验password_buffer,但是你可以发现password_buffer已经写入了PASSWORD,并不能通过后续条件==》
可以看到s是在dest上方的==》通过s覆盖dest就将能实现一个任意地址写入操作,也就能将password_buffer写为符合条件的值了
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/16_angr_arbitrary_write',auto_load_libs=False)
init_state = io.factory.entry_state()
class Hook(angr.SimProcedure):
#hook scanf
def run(self,format_string,key_addr,password_addr):
key_bvs = claripy.BVS('key_bvs',32)
password_addr_bvs = claripy.BVS('password_addr_bvs',20*8)
for char in password_addr_bvs.chop(bits=8):
self.state.add_constraints(char >= 'A', char <= 'Z')
self.state.memory.store(
key_addr,
key_bvs,
endness = io.arch.memory_endness
)
self.state.memory.store(
password_addr,
password_addr_bvs
)
self.state.globals['solutions'] = (key_bvs, password_addr_bvs)
def success(state):
#调用strncpy
strncpy_addr = 0x8048410
if state.addr == strncpy_addr:
return check_strncpy(state)
else:
return False
#从上一题检查puts变为检查strncpy
def check_strncpy(state):
#3个参数获取
strncpy_dest = state.memory.load(state.regs.esp+4,
4,
endness = io.arch.memory_endness)
#注意这边获取的是地址
strncpy_src_addr = state.memory.load(state.regs.esp+8,
4,
endness = io.arch.memory_endness)
strncpy_len = state.memory.load(state.regs.esp+12,
4,
endness = io.arch.memory_endness)
#这里获取的才是数据,请注意参考函数原型
src_contents = state.memory.load(strncpy_src_addr,strncpy_len)
if state.solver.symbolic(src_contents) and state.solver.symbolic(strncpy_dest):
password_str = "NDYNWEUJ"
buffer_addr = 0x57584344
#两项约束条件
#小端序获取字符串,验证src是否为目标字符串
check_content_password = src_contents[-1:-64] == password_str
#验证dest是否为password_buffer
check_dest_buffer_addr = strncpy_dest == buffer_addr
if state.satisfiable(extra_constraints = (check_content_password,check_dest_buffer_addr)):
state.add_constraints(check_content_password,check_dest_buffer_addr)
#条件通过
return True
else:
return False
else:
return False
scanf_sym = "__isoc99_scanf"
io.hook_symbol(scanf_sym,Hook())
simgr = io.factory.simgr(init_state)
simgr.explore(find = success)
if simgr.found:
so_state = simgr.found[0]
(key_so,password_so) = so_state.globals['solutions']
so0 = so_state.solver.eval(key_so)
so1 = so_state.solver.eval(password_so,cast_to=bytes)
print(so0,so1)
17_angr_arbitrary_jump
程序将获取输入,然后就没有其他功能了,没有调用我们的目标函数,考虑到scanf中存在溢出问题==》劫持程序执行流实现任意地址跳转
在符号执行中由于程序的输入是一个符号变量,并不是一个具体的值==》指令指针也将变为符号指针
在符号执行中,“无约束状态”指的是一种程序执行状态,其中程序计数器(指令指针)或其他关键寄存器被设置为符号变量。这意味着符号执行引擎无法确定下一条指令的具体位置,因为它可以是任何值,由输入的符号变量控制。
import angr
import claripy
import sys
io = angr.Project('/home/closure/Desktop/CTF/angr_ctf/dist/17_angr_arbitrary_jump',auto_load_libs=False)
init_state = io.factory.entry_state()
class Hook(angr.SimProcedure):
#hook scanf 与上文都类似,不再做过多解释
def run(self,format_string,scanf_input):
scanf_input_bvs = claripy.BVS('scanf_input',200*8)
for char in scanf_input_bvs.chop(bits=8):
self.state.add_constraints(char >= 'A', char <= 'Z')
self.state.memory.store(
scanf_input,
scanf_input_bvs
)
self.state.globals['scanf_input_bvs'] = scanf_input_bvs
scanf_sym = "__isoc99_scanf"
io.hook_symbol(scanf_sym,Hook())
#更改模拟引擎设置,使其不抛出无约束状态
simgr = io.factory.simgr(init_state,
save_unconstrained=True,
stashes={
'active': [init_state], #程序能进一步执行
'unconstrained': [], #无约束状态
'found': [], #找到目标路径的状态
})
#active为可以进一步搜索的所有状态的列表
#unconstrained为无约束状态
#found为已经找到目标路径的状态
while (simgr.active or simgr.unconstrained) and (not simgr.found):
#遍历所有无约束状态
for unconstrained_state in simgr.unconstrained:
#返回找到的无约束状态
def should_move(s):
return s is unconstrained_state
#将无约束状态移动至found中
simgr.move(from_stash='unconstrained',
to_stash='found',
filter_func=should_move)
simgr.step()
if simgr.found:
#取出第一个找到的状态
so_state = simgr.found[0]
print_goodjob = 0x42585249
#添加约束,验证eip是否指向目标地址
so_state.add_constraints(so_state.regs.eip == print_goodjob)
scanf_input_bvs_so = so_state.globals['scanf_input_bvs']
so0 = so_state.solver.eval(scanf_input_bvs_so,cast_to=bytes).decode() #求解
print(so0)