通过Angr_CTF入门Angr
2024-07-28 00:59:30

Angr_CTF

https://github.com/jakespringer/angr_ctf/tree/master

PS:可执行程序存放于dist目录

00_angr_find

image-20240715150854363

程序获取输入,经过一些加密之后与字符串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)的内容==》程序运行过程中的输入内容

image-20240715150706189

image-20240717092157535

01_angr_avoid

在我们尝试在在IDA中查看main函数的伪代码时会提示函数过大,反编译main是比较困难了==》看看都有什么其他函数:

image-20240727235420064

maybe_good:并查看交叉引用,可以看到被main调用了非常多次,怪不得会这么大

image-20240715152008856

avoid_me:并查看交叉引用,可以看到函数也同样被调用多次,并注意到函数名是作者提供给我们的提示,avoid这个函数,并注意到这个函数只有一个作用,就是让should_succeed为0

image-20240727235647536

而在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))

image-20240715153651075

image-20240728000803111

02_angr_find_condition

image-20240715161736962

与之前的逻辑并没有什么不同,都是接收输入,将输入进行一些加密处理再进行校验

但是这题的作者是想告诉我们可以去动态的选择想要的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))

image-20240715163557125

image-20240717093116937

03_angr_symbolic_registers

image-20240715164421137

image-20240715165143199

image-20240716093642631

程序将接收我们3个十六进制值作为输入,存放于eax,ebx,edx

image-20240716094504440

接下来将通过这几个寄存器进行传值,所以需要通过angr对这几个寄存器的值进行修改,所以,我们将需要跳过这个输入的函数,让程序开始的地址设置在输入之后(0x8048980),这样我们直接对寄存器赋值也起到了类似输入的功能,只是这样我们将更加方便

image-20240715165159902

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)

image-20240716100110821

image-20240716100038468

04_angr_symbolic_stack

image-20240716100534661

image-20240716101810736

程序通过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)

image-20240716110707161

image-20240716110539281

05_angr_symbolic_memory

image-20240716110939158

image-20240716111944631

程序将获取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)

image-20240716141639564

image-20240716112742035

06_angr_symbolic_dynamic_memory

image-20240716141831373

image-20240716142653622

程序通过malloc申请了2块9字节大小的缓冲区,将这两块缓冲区的地址保存在bss段上,也就是buffer0和buffer1中,之后就是向这两块缓冲区中写入内容,在经过complex_function函数处理后进行校验

由于我们需要跳过输入,所以所以需要通过angr去伪造两块缓冲区的地址,再往这两块缓冲区去写入符号向量

程序中存在很大的bss段,可以考虑在其中伪造缓冲区

image-20240716143538975

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)

image-20240716144300234

image-20240716144239737

07_angr_symbolic_file

image-20240716144643038

image-20240716144840797

程序将读取我们的输入通过ignore_me函数存储入OJKSQYDP.txt中,后续再通过从OJKSQYDP.txt中取出进行校验

image-20240716150810517

我们可以通过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')))

image-20240716150505987

image-20240716150005925

08_angr_constraints

image-20240728002208961

image-20240716162853585

程序一样是获取输入,然后对输入进行加密处理,再由校验函数去校验

但是与之前直接使用strcmp校验不同,这里使用的是一个自定义的按位校验,并且由于输入是16位,就将会进行16次的循环,每次循环都将经历一次if判断==》将会产生2^16 == 65536个判断分支==》这么多的分支,就将会引发一个叫路径爆炸的问题,严重影响我们测试的效率==》为此,我们可以自己去实现一个校验约束,直接跳过或者也可以理解为hook掉这个按位校验函数,这样就不会产生路径爆炸了

你可能会有疑问,strcmp函数在底层实现也是按位比较,为什么在前面的题目中并没有提及路径爆炸问题==》原因是angr在对于strcmp这种标准库自己实现了一套hook,使用了angr实现的strcmp去替换掉了标准库中调用的strcmp函数,避免了路径爆炸,这在下文中也有提及

image-20240716163356055

image-20240716161755867

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')))

image-20240717091816543

image-20240716164510494

09_angr_hooks

image-20240717100302681

image-20240717100709498

image-20240717100745760

image-20240717100854792

程序将获取两次输入,第一次输入经过complex_function处理后,再通过check_equals_XYMKBKUHNIQYNQXE与password进行比较;第二次输入将与经过complex_function处理后的password进行比较;并且可以看到在check_equals_XYMKBKUHNIQYNQXE中使用按位比较==》将会出现路径爆炸问题

==》通过angr hook check_equals_XYMKBKUHNIQYNQXE函数(这与上一题的思想并不太相同,这里才是真正的hook,上一题只是执行到了check之前,并没有真正调用,而这一题则是真正调用了check函数,调用了我们hook之后的代码)

image-20240717102302005

image-20240717101418633

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')))

image-20240717104316691

image-20240717104302401

10_angr_simprocedures

image-20240717110120253

与上一题类似,但是本题的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')))

image-20240717111242588

image-20240717111305089

11_angr_sim_scanf

image-20240717111538945

程序通过__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))

image-20240717143711968

image-20240717143736617

12_angr_veritesting

image-20240717144730209

程序将进行一个按位的加密,这里将会出现路径爆炸==》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')))

image-20240717150248347

image-20240717150502419

13_angr_static_binary

image-20240717152347852

是一个静态编译的程序,通常,angr会自动使用速度快得多的simprocedure代替标准库==》就像本来strcmp的实现也是按位比较,但是在前面为什么不会在strcmp上发生路径爆炸,因为angr已经自动给这些函数hook了(上文中已经提到过)==》但是本体采用静态编译,angr没法自动hook,需要手动去hook在使用的标准库的C函数:

simprocedure:两层结构,一层包名,一层函数名

image-20240717152917404

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')))

image-20240717153619939

image-20240717153728855

14_angr_shared_library

image-20240717153809642

image-20240717153821284

发现这个校验来自动态链接库lib14_angr_shared_library.so

image-20240717154925116

==》基本逻辑和上文的题目大差不差,区别就是本体需要对lib14_angr_shared_library.so进行符号执行

==》checksec可以方便获取基地址

image-20240717154741435

image-20240717155623973

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')))

image-20240717161702716

image-20240717162023294

15_angr_arbitrary_read

image-20240717162642873

程序通过scanf获取输入,第一个key经过校验后将使用puts输出,但是都为try_again,但是一个s(try_again)在栈上,而我们的第二个输入也将写入栈上,并且输入长度并没有限制,也就是说可以覆盖s为Good Job.==》

image-20240717164217730

image-20240717165320347

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)

image-20240718103457583

image-20240718094617723

16_angr_arbitrary_write

image-20240718144751409

程序获取输入,首先是第一个判断key,如果校验成功将s指向的内容写入dest指向的数组,然后就是校验password_buffer,但是你可以发现password_buffer已经写入了PASSWORD,并不能通过后续条件==》

image-20240728011634331

可以看到s是在dest上方的==》通过s覆盖dest就将能实现一个任意地址写入操作,也就能将password_buffer写为符合条件的值了

image-20240718150531323

image-20240718151451712

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)

image-20240718151317883

image-20240718151256404

17_angr_arbitrary_jump

image-20240718151534886

image-20240718151552772

程序将获取输入,然后就没有其他功能了,没有调用我们的目标函数,考虑到scanf中存在溢出问题==》劫持程序执行流实现任意地址跳转

在符号执行中由于程序的输入是一个符号变量,并不是一个具体的值==》指令指针也将变为符号指针

在符号执行中,“无约束状态”指的是一种程序执行状态,其中程序计数器(指令指针)或其他关键寄存器被设置为符号变量。这意味着符号执行引擎无法确定下一条指令的具体位置,因为它可以是任何值,由输入的符号变量控制。

image-20240718154857434

image-20240718151614494

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)    

image-20240718165559088

image-20240718165625006