angr-ctf
angr 的项目地址
https://github.com/jakespringer/angr_ctf
angr实战
00
拖到IDA

就是输入正确的指令才能通关
这次试一下用angr来解题
goahead@DESKTOP-8KORQ75:/mnt/d/CTF/angr/angr_ctf-master/dist$ workon angr
(angr) goahead@DESKTOP-8KORQ75:/mnt/d/CTF/angr/angr_ctf-master/dist$ python
Python 3.6.9 (default, Mar 10 2023, 16:46:00)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr
>>> p = angr.Project("./00_angr_find") #创建一个工程名
>>> init_state = p.factory.entry_state() #给它初始化状态为从入口点开始
>>> sm = p.factory.simulation_manager(init_state) #让angr执行
>>> sm.explore(find = 0x08048678) # 给它探索的目的地,也就是IDA中分析到的“good job”
WARNING | 2023-11-17 14:12:29,194 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-11-17 14:12:29,194 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2023-11-17 14:12:29,195 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-11-17 14:12:29,195 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2023-11-17 14:12:29,195 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-11-17 14:12:29,195 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80486b1 (__libc_csu_init+0x1 in 00_angr_find (0x80486b1))
WARNING | 2023-11-17 14:12:29,196 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in 00_angr_find (0x80486b3))
WARNING | 2023-11-17 14:12:30,087 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x817e690 (strcmp+0x0 in libc.so.6 (0x7e690))
<SimulationManager with 1 active, 16 deadended, 1 found>
>>> sm.found[0]
<SimState @ 0x8048678>
>>> found_state = sm.found[0]
>>> found_state.posix.dumps(0)
b'JXWVXRKX'
>>> found_state.posix.dumps(1)
b'Enter the password: '
## state.posix.dumps (0) 代表该状态程序的所有输入, state.posix.dumps (1) 代表该状态程序的所有输出。
所以输入JXWVXRKX就可以通关
goahead@DESKTOP-8KORQ75:/mnt/d/CTF/angr/angr_ctf-master/dist$ ./00_angr_find
Enter the password: JXWVXRKX
Good Job.
常规解法
letter_list = ['J', 'A', 'C', 'E', 'J', 'G', 'C', 'S']
def reverse_operation(value, i):
return chr((value - 65 - 3 * i) % 26 + 65)
# 反向计算得到字母数组 s[8]
s = [reverse_operation(ord(letter), i) for i, letter in enumerate(letter_list)]
print("原始字母数组:", letter_list)
print("通过反向运算得到的字母数组:", ''.join(s))
01
拖到IDA,看到一个函数maybe_good,打开

很显然,我们想要的就是0x080485E0
执行程序

发现和00类似
直接使用angr
(angr) goahead@DESKTOP-8KORQ75:/mnt/d/CTF/angr/angr_ctf-master/dist$ workon angr
(angr) goahead@DESKTOP-8KORQ75:/mnt/d/CTF/angr/angr_ctf-master/dist$ python
Python 3.6.9 (default, Mar 10 2023, 16:46:00)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr
>>> p = p.angr.Project("./01_angr_avoid")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'p' is not defined
>>> p = angr.Project("./01_angr_avoid")
>>> init_state=p.factory.entry_state()
>>> sm = p.factory.simulation_manager(init_state)
>>> sm.explore(find=0x080485E0,avoid=0x080485F2) #find的值就是要让angr到达的地址值,而avoid的值是不让angr到达的地址值
>>> found_state = sm.found[0]
>>> found_state.posix.dumps(0)
b'HUJOZMYS'
>>> found_state.posix.dumps(1)
b'Enter the password: '
故输入HUJOZMYS即可通关

02
观察有多个地方输出“Good job”
使用脚本:
# It is very useful to be able to search for a state that reaches a certain
# instruction. However, in some cases, you may not know the address of the
# specific instruction you want to reach (or perhaps there is no single
# instruction goal.) In this challenge, you don't know which instruction
# grants you success. Instead, you just know that you want to find a state where
# the binary prints "Good Job."
#
# Angr is powerful in that it allows you to search for a states that meets an
# arbitrary condition that you specify in Python, using a predicate you define
# as a function that takes a state and returns True if you have found what you
# are looking for, and False otherwise.
import angr
import sys
def main(argv):
path_to_binary = "./02_angr_find_condition"
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state)
def is_successful(state): #判断当前状态能否使程序输出 Good Job,然后返回 True or False,
stdout_output = state.posix.dumps(sys.stdout.fileno()) #把标准输出赋值给 stdout_output ,那不是字符串而是一个bytes 对象
if b'Good Job.' in stdout_output: # 要使用 b'Good Job.' 替代 Good Job. 检查是否输出了字符串 Good Job.
return True # (3)
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again.' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
输出结果为:b'HETOBRCU'
03 寄存器符号化
发现要输入多个参数(3个)

则我们直接跳过输入,让angr直接从0x8048980 处开始执行
start_address = 0x08048980 # :integer (probably hexadecimal)
initial_state = project.factory.blank_state(addr=start_address)
#这次使用 blank_state() 方法替代了 entry_state() 。通过把 addr=start_address 传递给 blank_state()
观察反汇编,使用eax,ebx,edx,进行传参,故位数为32位
用 claripy 通过 BVS() 方法生成三个位向量。这个方法需要两个参数:第一个参数表示符号名,第二个参数表示这个符号的长度 单位bit。因为符号值都保存在寄存器里,并且寄存器都是32位的,所以位向量的大小也需要是32位的。
password0_size_in_bits = 32 # :integer
password0 = claripy.BVS('password0', password0_size_in_bits)
password1 = claripy.BVS('password1', password0_size_in_bits)
password2 = claripy.BVS('password2', password0_size_in_bits)
现在我们已经创建了三个符号位向量,现在就把他们赋值给 eax,ebx,edx。我准备修改先前创建的状态 initial_state,并更新寄存器的内容,幸运的是,angr提供了一个非常智能的方法:
initial_state.regs.eax = password0
initial_state.regs.ebx = password1
initial_state.regs.edx = password2
现在我们准备跟以前一样定义 find , avoid 状态。
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Good Job.\n' in stdout_output:
return True
else: return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again.\n' in stdout_output:
return True
else: return False
simulation.explore(find=is_successful, avoid=should_abort)
下面就是打印解了。
if simulation.found:
solution_state = simulation.found[0]
# Solve for the symbolic values. If there are multiple solutions, we only
# care about one, so we can use eval, which returns any (but only one)
# solution. Pass eval the bitvector you want to solve for.
# (!) NOTE: state.se is deprecated, use state.solver (it's exactly the same).
solution0 = format(solution_state.solver.eval(password0), 'x')
# 我们根据注入的三个符号值调用求解引擎的 eval()方法; format() 方法格式化解并去掉16进制的 “0x”。
solution1 = format(solution_state.solver.eval(password1), 'x')
solution2 = format(solution_state.solver.eval(password2), 'x')
# Aggregate and format the solutions you computed above, and then print
# the full string. Pay attention to the order of the integers, and the
# expected base (decimal, octal, hexadecimal, etc).
#重组3个解,组合为一个字符串,然后打印出来。
solution = solution0 + " " + solution1 + " " + solution2 # (2)
print("[+] Success! Solution is: {}".format(solution))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
04 符号化栈
手搓一下
F5反汇编进入 handle_user()函数

发现只要将输入的两个数分别与另外两个数进行异或操作,再与两个数比较就能得到正确答案
因此,只需将最终要比较的数分别和那两个数异或就能得到输入(两次异或等于不操作)
使用angr

因为我们要跳过scanf,故call scanf 下面的add esp,10h(平衡堆栈)我们也不用执行
所以,从下一行 0x08048697开始执行
import angr
import claripy
import sys
def main(argv):
bin_path = "./04_angr_symbolic_stack"
p = angr.Project(bin_path)
start_addr = 0x08048697
init_state = p.factory.blank_state(addr = start_addr)
用 claripy 通过 BVS() 方法生成两个个位向量。
#ebp = esp
init_state.regs.esp = init_state.reg.esp
password0 = claripy.BVS('password0',32)
password1= claripy.BVS('password0',32)
#因为他push 两个参数为 [ebp-0Ch]和[ebp-10h],而栈的地址是向下增长的
#一个参数大小为4字节,所以padding 的位置为0x0C-4 = 0x08
padding_length_in_bytes = 0x08
#提升堆栈
initial_state.regs.esp -= padding_length_in_bytes
#push password0
initial_state.stack_push(password0)
#push password1
initial_state.stack_push(password1)
sm = p.factory.simgr(init_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Good Job' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(password0)
solution1 = solution_state.se.eval(password1)
print("Solution is:{} {}".format(solution0,solution1))
else:
raise Exception"Solution not found"
if __name__ == '__main__':
main(sys.argv)
答案输入:
1704280884 -1912626145
或者
1704280884 2382341151
都对!因为将-1912626145转化为无符号整型就是2382341151(python实现将有符号整型a转为无符号整型:print(a+2**32))
05 符号化内存
找到scanf附近,发现需要输入4个变量,并且这4个变量都是通过push内存直接传入的

因此,本次得到目标是符号化内存
首先,看一下从哪个地址开始执行吧,因为无需调用scanf函数,故他的堆栈平衡也不用操作,
所以直接从0x08048601开始执行
start_address = 0x08048601
init_state = project.factory.blank_state(addr = start_address)
用 claripy 通过 BVS() 方法生成四个位向量。由于scanf("%8s %8s %8s %8s"),所以每个参数64位
password0 = claripy.BVS('password0',64)
password1 = claripy.BVS('password1',64)
password2 = claripy.BVS('password2',64)
password3 = claripy.BVS('password3',64)
把内存单元的地址值和变量之间进行一定关系的绑定
password0_address = 0x0A1BA1C0
password1_address = 0x0A1BA1C8
password2_address = 0x0A1BA1D0
password3_address = 0x0A1BA1D8
initial_state.memory.store(password0_address, password0)
initial_state.memory.store(password1_address, password1)
initial_state.memory.store(password2_address, password2)
initial_state.memory.store(password3_address, password3)
执行
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Good Job' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(password0, cast_to=bytes)
solution1 = solution_state.se.eval(password1, cast_to=bytes)
solution2 = solution_state.se.eval(password2, cast_to=bytes)
solution3 = solution_state.se.eval(password3, cast_to=bytes)
#将b'NAXTHGNR' b'JVSFTPWE' b'LMGAUHWC' b'XMDCPALU' 转化为 NAXTHGNR JVSFTPWE LMGAUHWC XMDCPALU
print("Solution is: {} {} {} {}".format(solution0.decode('utf-8'),solution1.decode('utf-8'),solution2.decode('utf-8'),solution3.decode('utf-8')))
else:
raise Exception('Could not find the solution')
结果:NAXTHGNR JVSFTPWE LMGAUHWC XMDCPALU
06符号化堆
观察反汇编

程序使用了malloc动态分配内存,故本次我们要执行符号化堆
先看下从哪开始执行

不调用scanf,所以我们从0x08048699处开始执行
start_address = 0x08048699
initial_state = project.factory.blank_state(addr=start_address)
需要两个参数,所以用 claripy 通过 BVS() 方法生成两个位向量
password0 = claripy.BVS('password0', 64)
password1 = claripy.BVS('password1', 64)
因为malloc是随机分配地址的,所以我们直接指定地址
addr_esp = initial_state.regs.esp
fake_heap_address0 = addr_esp - 0x100
fake_heap_address1 = addr_esp - 0x200
将地址和变量进行绑定,默认情况下,Angr 在内存中存储整数时采用大字节。要使用参数 endness=project.arch.memory_endness。在 x86 架构上,这是小端序。
pointer_to_malloc_memory_address0 = 0x0ABCC8A4
pointer_to_malloc_memory_address1 = 0x0ABCC8AC
initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness)
initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness)
在我们的 fake_heap_address 处存储我们的符号值。查看二进制文件,确定 scanf 从 fake_heap_address 写入的偏移量。
initial_state.memory.store(fake_heap_address0, password0)
initial_state.memory.store(fake_heap_address1, password1)
执行
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Good Job' in stdout_output:
return True
else:
return False
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
if b'Try again' in stdout_output:
return True
else:
return False
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
solution0 = solution_state.se.eval(password0, cast_to=bytes)
solution1 = solution_state.se.eval(password1, cast_to=bytes)
#solution = ???
print("Solution is: {} {}".format(solution0.decode('utf-8'), solution1.decode('utf-8')))
# print(solution)
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
结果是:UBDKLMBV UNOERNYS
07 文件内容符号化
拖到IDA,F5一下

看到文件操作,因此本次目标符号化文件内容

起始地址我们需要在初始化文件之前(因为这里并没有符号化文件名),输入password之后,选择的是ignore_me之后,选的是0x80488D6
start_address = 0x080488D6
initial_state = project.factory.blank_state(addr=start_address)
给它文件名和大小
filename = "OJKSQYDP.txt"
file_size = 0x40
紧接着进行文件文本符号化
password = initial_state.solver.BVS("password",file_size*8)
sim_file = angr.storage.SimFile(filename,content=password,size=file_size)
然后进行相应的 插入操作:
initial_state.fs.insert(filename,sim_file)
最后进行文本内容的求解:
sm = project.factory.simulation_manager(initial_state)
结果为 AZOMMMZM

angr-ctf的更多相关文章
- 符号执行-基于python的二进制分析框架angr
转载:All Right 符号执行概述 在学习这个框架之前首先要知道符号执行.符号执行技术使用符号值代替数字值执行程序,得到的变量的值是由输入变 量的符号值和常量组成的表达式.符号执行技术首先由Kin ...
- 动若脱兔:深入浅出angr--初步理解符号执行以及angr架构
一:概论 angr作为符号执行的工具,集成了过去的许多分析方式,它不仅能进行动态符号执行,而且还能进行很多静态分析,他在分析二进制程序中能发挥很大的作用,下面为一些应用: 1:利用符号执行探究执行路径 ...
- 简单的尝试下angr
0x00:前言 之前接触到了符号执行,可以用于程序的自动化分析,感觉还是比较神奇,工业上的具体用法不是很清楚,不过在CTF中这个东西慢慢在流行...从defcon 2016就可以看出(有很多人解re用 ...
- CTF:从0到1 -> zero2one
本篇blog首发0xffff论坛(CTF:从0到1->zero2one - 0xFFFF),中间有各位大佬补充,搬到了个人博客CTF:从0到1 -> zero2one | c10udlnk ...
- angr原理与实践(一)——原理
1本文系原创,转载请说明出处 关注微信公众号 信安科研人,获取更多的原创安全资讯 编辑 网上已经有很多介绍angr的官方文档的博客,但是怎么去用angr做一次有意义且成就感满满的分析的教程很少 ...
- 个人CTF资源聚合
i春秋 幻泉 CTF入门课程笔记 视频地址 能力 思维能力 快速学习能力 技术能力 基础 编程基础 (c语言 汇编语言 脚本语言) 数学基础 (算法 密码学) 脑洞 (天马行空的想象推理) 体力耐力( ...
- 暑假CTF训练一
暑假CTF训练一 围在栅栏中的爱 题目: 最近一直在好奇一个问题,QWE到底等不等于ABC? -.- .. --.- .-.. .-- - ..-. -.-. --.- --. -. ... --- ...
- Sharif University CTF 2016 -- Login to System (PWN 200)
EN: It's easy to find out where is the bug : .text:0000000000400DE4 ; void *start_routine(void *).te ...
- 入CTF坑必不可少的地方-保持更新
0x00 前言 没有交易,没有买卖,没有排名,纯属分享:p 0x01 CTF介绍 CTF领域指南CTF介绍大全CTF赛事预告 0x02 CTF练习 BIN:reversingpwnableexploi ...
- v0lt CTF安全工具包
0×00 v0lt v0lt是一个我尝试重组每一个我使用过的/现在在使用的/将来要用的用python开发的安全领域CTF工具.实践任务可能会采用bash脚本来解决,但我认为Python更具有灵活性,这 ...
随机推荐
- Spring —— bean实例化
bean 实例化 bean本质上就是对象,创建bean使用构造方法完成(反射) 构造方法(常用) 静态工厂* 实例工厂* FactoryBean(实 ...
- 基于SqlAlchemy+Pydantic+FastApi的Python开发框架
随着大环境的跨平台需求越来越多,对与开发环境和实际运行环境都有跨平台的需求,Python开发和部署上都是跨平台的,本篇随笔介绍基于SqlAlchemy+Pydantic+FastApi的Python开 ...
- 配置 ZRAM,实现 Linux 下的内存压缩,零成本低开销获得成倍内存扩增
由于项目需求,笔者最近在一台 Linux 服务器上部署了 ElasticSearch 集群,却发现运行过程中经常出现查询速度突然降低的问题,登录服务器后发现是物理内存不足,导致机器频繁发生页面交换.由 ...
- 利用CSV路径文件和.png图像,生成3D原图。并展示部分分割图像
具体代码 ,请看的的github if __name__ == "__main__": df = pd.read_csv(r'D:/compation/kaggle/train.c ...
- 直播预告 | 字节跳动云原生大数据分析引擎 ByConity 与 ClickHouse 有何差异?
ByContiy 是字节跳动开源的一款云原生的大数据分析引擎,擅长交互式查询和即席查询,具有支持多表关联复杂查询.集群扩容无感.离线批数据和实时数据流统一汇总等特点. ByConity 从1月份发布开 ...
- 数据库周刊33丨腾讯Tbase新版本发布;“2020数据技术嘉年华”有奖话题遴选;阿里云技术面试题;APEX 实现数据库自动巡检;MYSQL OCP题库……
摘要:墨天轮数据库周刊第33期发布啦,每周1次推送本周数据库相关热门资讯.精选文章.干货文档. 热门资讯 1.中国移动国产OLTP数据库中标公告:南大金仓阿里,万里开源中兴 分获大单[摘要]近日,中国 ...
- JDBC后端实现登录的逻辑
// 包名 package com.zhulx.JDBC; // 导入实例类 import com.zhulx.pojo.Account; import java.sql.Connection; im ...
- ajax异步请求数据还没有返回,页面时空白的如何处理
使用骨架屏,给用户一种正在解析数据的感觉 : element-ui的骨架屏 :https://element.eleme.cn/#/zh-CN/component/skeleton
- 王树森Attention与Self-Attention学习笔记
目录 Seq2Seq + Attention Attention的原理 方法一(Used in the original paper) 方法二(more popular,the same to Tra ...
- 如何使用 VuePress 搭建博客网站并 Vercel 部署
先来看一下网站截图: 快速上手 1.创建并进入一个新目录 mkdir vuepress-starter && cd vuepress-starter 2.使用你喜欢的包管理器进行初始化 ...