Angr CTF(一)
0x0 前言
最近打了一个ctf,几个Re题尝试用angr来做,但写的脚本跑了一天都没跑出结果,太菜了,还是不太会用angr。
遂找了一套angr ctf题来练练:https://github.com/jakespringer/angr_ctf
参考:https://github.com/ZERO-A-ONE/AngrCTF_FITM
00_angr_find——基础用法
这是一道最基本的入门题,运行会提示要求输入密码:
$ ./00_angr_find
Enter the password: asdf;kl
Try again.
反编译伪代码如下,密码变换处理后最后与”JACEJGCS”比较,正确输出”Good Job.”,错误输出”Try again.”:
local_c = (undefined *)¶m_1;
local_14 = *(int *)(in_GS_OFFSET + 0x14);
printf("Enter the password: ");
__isoc99_scanf(&DAT_08048753,local_1d);
for (local_24 = 0; local_24 < 8; local_24 = local_24 + 1) {
cVar1 = complex_function((int)local_1d[local_24],local_24);
local_1d[local_24] = cVar1;
}
iVar2 = strcmp(local_1d,"JACEJGCS");
if (iVar2 == 0) {
puts("Good Job.");
}
else {
puts("Try again.");
}
使用angr求解,主要需要找到输出”Good Job.”的代码位置:08048678
LAB_08048675 XREF[1]: 08048661(j)
08048675 83 ec 0c SUB ESP,0xc
08048678 68 60 PUSH s_Good_Job._08048760 = "Good Job."
87 04 08
0804867d e8 7e CALL <EXTERNAL>::puts int puts(char * __s)
fd ff ff
08048682 83 c4 10 ADD ESP,0x10
LAB_08048685 XREF[1]: 08048673(j)
08048685 b8 00 MOV EAX,0x0
00 00 00
0804868a 8b 4d f4 MOV ECX,dword ptr [EBP + local_14]
0804868d 65 33 XOR ECX,dword ptr GS:[0x14]
0d 14
00 00 00
08048694 74 05 JZ LAB_0804869b
08048696 e8 55 CALL <EXTERNAL>::__stack_chk_fail undefined
__stack_chk_fai
fd ff ff
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
修改angr脚本,angr ctf给每个题都写好了模板,只需要修改几个关键信息即可:
# 加载angr模块
import angr
import sys
def main(argv):
# 加载目标文件,创建project
path_to_binary = "./00_angr_find" # :string
project = angr.Project(path_to_binary)
# 设置state
# state代表程序的一个实例镜像,模拟执行某个时刻的状态,就类似于快照。保存运行状态的上下文信息,如内存/寄存器等,这里使用project.factory.entry_state()告诉符号执行引擎从程序的入口点开始符号执行
initial_state = project.factory.entry_state()
# 设置 Simulation Managers
# SimState 对象包含程序运行时信息,如内存/寄存器/文件系统数据等。SM(Simulation Managers)是angr中最重要的控制接口,它使你能够同时控制一组状态(state)的符号执行,应用搜索策略来探索程序的状态空间。
simulation = project.factory.simgr(initial_state)
# 运行,探索满足路径需要的值
# 符号执行最普遍的操作是找到能够到达某个地址的状态,同时丢弃其他不能到达这个地址的状态。SM为使用这种执行模式提供了.explore()方法
# 当使用find参数启动.explore()方法时,程序将会一直执行,直到发现了一个和find参数指定的条件相匹配的状态。find参数的内容可以是想要执行到的某个地址、或者想要执行到的地址列表、或者一个获取state作为参数并判断这个state是否满足某些条件的函数。当activestash中的任意状态和find中的条件匹配的时候,它们就会被放到found stash中,执行随即停止。之后你可以探索找到的状态,或者决定丢弃它,转而探索其它状态。
print_good_address = 0x08048678 # :integer (probably in hexadecimal)
simulation.explore(find=print_good_address)
# 获取执行结果
if simulation.found:
# 此时相关的状态已经保存在了simgr当中,可以通过simgr.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)
运行angr脚本,获取密码,验证通过
$ python scaffold00.py
WARNING | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-12-24 15:41:36,064 | 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-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-12-24 15:41:36,064 | 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-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-12-24 15:41:36,065 | 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-12-24 15:41:36,066 | angr.storage.memory_mixi ns.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in 00_angr_find (0x80486b3))
WARNING | 2023-12-24 15:41:37,355 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
b'JXWVXRKX'
$ ./00_angr_find
Enter the password: JXWVXRKX
Good Job.
01_angr_avoid——条件收敛
这题跟上面一样
$ ./01_angr_avoid
Enter the password: asdflk
Try again.
找到正确输出位置,定位到”Good Job.”
void maybe_good(char *param_1,char *param_2)
{
int iVar1;
if ((should_succeed != '\0') && (iVar1 = strncmp(param_1,param_2,8), iVar1 == 0)) {
puts("Good Job.");
return;
}
puts("Try again.");
return;
}
确定”Good Job.”代码位置:080485e0
080485d6 83 c4 10 ADD ESP,0x10
080485d9 85 c0 TEST EAX,EAX
080485db 75 12 JNZ LAB_080485ef
080485dd 83 ec 0c SUB ESP,0xc
080485e0 68 1e PUSH s_Good_Job._080d461e = "Good Job."
46 0d 08
080485e5 e8 e6 CALL <EXTERNAL>::puts int puts(char * __s)
fd ff ff
080485ea 83 c4 10 ADD ESP,0x10
080485ed eb 10 JMP LAB_080485ff
LAB_080485ef XREF[2]: 080485c4(j), 080485db(j)
080485ef 83 ec 0c SUB ESP,0xc
080485f2 68 13 PUSH s_Try_again._080d4613 = "Try again."
46 0d 08
080485f7 e8 d4 CALL <EXTERNAL>::puts int puts(char * __s)
fd ff ff
080485fc 83 c4 10 ADD ESP,0x10
完善angr脚本,不过这次不只查找达到print_good_address的状态,还要查找没有达到will_not_succeed_address的状态。
通常二进制文件都非常庞大,为了节省时间,添加一些收敛条件可以减少计算量。
import angr
import sys
def main(argv):
path_to_binary = "./01_angr_avoid"
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state)
print_good_address = 0x080485e0
will_not_succeed_address = 0x080485a8
simulation.explore(find=print_good_address, avoid=will_not_succeed_address)
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)
本次will_not_succeed_address选择多处调用的avoid_me
void avoid_me(void)
{
should_succeed = 0;
return;
}
运行angr脚本,获取密码,验证通过
$ python scaffold01.py
WARNING | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-12-24 16:09:32,082 | 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-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-12-24 16:09:32,082 | 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-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-12-24 16:09:32,083 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80d4591 (__libc_csu_init+0x1 in 01_angr_avoid (0x80d4591))
WARNING | 2023-12-24 16:09:32,084 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80d4593 (__libc_csu_init+0x3 in 01_angr_avoid (0x80d4593))
WARNING | 2023-12-24 16:09:35,100 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff3d with 11 unconstrained bytes referenced from 0x819a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING | 2023-12-24 16:09:35,100 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x819a410 (strncmp+0x0 in libc.so.6 (0x9a410))
b'HUJOZMYS'
$ ./01_angr_avoid
Enter the password: HUJOZMYS
Good Job.
02_angr_find_condition——动态获取state
这题主要想教会如何动态获取state,即不用每次都分析具体的代码位置,而是通过检索字符串自动获取
$ ./02_angr_find_condition
Enter the password: df;l
Try again.
代码逻辑依然是正确输出”Good Job.”,错误输出”Try again.”
local_c = (undefined *)¶m_1;
local_14 = *(int *)(in_GS_OFFSET + 0x14);
for (local_48 = 0; local_48 < 0x14; local_48 = local_48 + 1) {
*(undefined *)((int)&local_28 + local_48) = 0;
}
local_28 = 0x52525856;
local_24 = 0x5255454a;
printf("Enter the password: ");
__isoc99_scanf(&DAT_0804d333,local_3c);
for (local_44 = 0; local_44 < 8; local_44 = local_44 + 1) {
cVar1 = complex_function((int)local_3c[local_44],local_44 + 8);
local_3c[local_44] = cVar1;
}
iVar2 = strcmp(local_3c,(char *)&local_28);
if (iVar2 == 0) {
puts("Good Job.");
}
else {
puts("Try again.");
}
if (local_14 == *(int *)(in_GS_OFFSET + 0x14)) {
return 0;
}
由于使用动态获取state,直接修改脚本,检索字符串,但这里实际是字节对象,所以必须使用b’Good Job.’而不是仅”Good Job.”
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):
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]
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
运行angr脚本,获取密码,验证通过
$ python scaffold02.py
WARNING | 2023-12-24 16:40:09,183 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-12-24 16:40:09,184 | 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-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-12-24 16:40:09,184 | 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-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x804d291 (__libc_csu_init+0x1 in 02_angr_find_condition (0x804d291))
WARNING | 2023-12-24 16:40:09,185 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x804d293 (__libc_csu_init+0x3 in 02_angr_find_condition (0x804d293))
WARNING | 2023-12-24 16:40:10,374 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff2d with 11 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
WARNING | 2023-12-24 16:40:10,374 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff50 with 4 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
b'HETOBRCU'
$ ./02_angr_find_condition
Enter the password: HETOBRCU
Good Job.
03_angr_symbolic_registers——寄存器
$ ./03_angr_symbolic_registers
Enter the password: asdfa
Try again.
这次的输入是在get_user_input函数里,调用的scanf获取格式化字符串,”%x %x %x”说明使用三个十六进制值作为输入
undefined4 main(void)
{
int iVar1;
int iVar2;
int iVar3;
undefined4 unaff_EBX;
undefined8 uVar4;
printf("Enter the password: ");
uVar4 = get_user_input();
iVar1 = complex_function_1((int)uVar4);
iVar2 = complex_function_2(unaff_EBX);
iVar3 = complex_function_3((int)((ulonglong)uVar4 >> 0x20));
if (((iVar1 == 0) && (iVar2 == 0)) && (iVar3 == 0)) {
puts("Good Job.");
}
else {
puts("Try again.");
}
return 0;
}
undefined8 get_user_input(void)
{
int in_GS_OFFSET;
undefined4 local_1c;
undefined local_18 [4];
undefined4 local_14;
int local_10;
local_10 = *(int *)(in_GS_OFFSET + 0x14);
__isoc99_scanf("%x %x %x",&local_1c,local_18,&local_14);
if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return CONCAT44(local_14,local_1c);
}
查看汇编代码,这里使用EAX、EBX、EDX三个寄存器来存储
08048919 89 4d f4 MOV dword ptr [EBP + local_10],ECX 0804891c 31 c9 XOR ECX,ECX 0804891e 8d 4d f0 LEA ECX=>local_14,[EBP + -0x10] 08048921 51 PUSH ECX 08048922 8d 4d ec LEA ECX=>local_18,[EBP + -0x14] 08048925 51 PUSH ECX 08048926 8d 4d e8 LEA ECX=>local_1c,[EBP + -0x18]
08048929 51 PUSH ECX 0804892a 68 93 PUSH s_%x_%x_%x_08048a93 = "%x %x %x" 8a 04 0 0804892f e8 9c CALL <EXTERNAL>::__isoc99_scanf undefined __isoc99_scanf() fa ff ff 08048934 83 c4 10 ADD ESP,0x10 08048937 8b 4d e8 MOV ECX,dword ptr [EBP + local_1c] 0804893a 89 c8 MOV EAX,ECX 0804893c 8b 4d ec MOV ECX,dword ptr [EBP + local_18] 0804893f 89 cb MOV EBX,ECX 08048941 8b 4d f0 MOV ECX,dword ptr [EBP + local_14] 08048944 89 ca MOV EDX,ECX 08048946 90 NOP 08048947 8b 4d f4 MOV ECX,dword ptr [EBP + local_10] 0804894a 65 33 XOR ECX,dword ptr GS:[0x14] 0d 14 00 00 00 08048951 74 05 JZ LAB_08048958 08048953 e8 48 CALL <EXTERNAL>::__stack_chk_fail undefined __stack_chk_fai fa ff ff -- Flow Override: CALL_RETURN (CALL_TERMINATOR)
那么 start_address选择get_user_input返回后的地址:08048980
0804896e 68 9c PUSH s_Enter_the_password:_08048a9c = "Enter the password: "
8a 04 08
08048973 e8 18 CALL <EXTERNAL>::printf int printf(char * __forma
fa ff ff
08048978 83 c4 10 ADD ESP,0x10
0804897b e8 8c CALL get_user_input undefined get_user_input()
ff ff ff
08048980 89 45 ec MOV dword ptr [EBP + local_1c],EAX
08048983 89 5d f0 MOV dword ptr [EBP + local_18],EBX
08048986 89 55 f4 MOV dword ptr [EBP + local_14],EDX
08048989 83 ec 0c SUB ESP,0xc
0804898c ff 75 ec PUSH dword ptr [EBP + local_1c]
0804898f e8 75 CALL complex_function_1 undefined complex_functio
fb ff ff
08048994 83 c4 10 ADD ESP,0x10
08048997 89 c1 MOV ECX,EAX
08048999 89 4d ec MOV dword ptr [EBP + local_1c],ECX
0804899c 83 ec 0c SUB ESP,0xc
0804899f ff 75 f0 PUSH dword ptr [EBP + local_18]
080489a2 e8 b3 CALL complex_function_2 undefined complex_functio
fc ff ff
080489a7 83 c4 10 ADD ESP,0x10
080489aa 89 c1 MOV ECX,EAX
080489ac 89 4d f0 MOV dword ptr [EBP + local_18],ECX
080489af 83 ec 0c SUB ESP,0xc
080489b2 ff 75 f4 PUSH dword ptr [EBP + local_14]
080489b5 e8 23 CALL complex_function_3 undefined complex_functio
fe ff ff
修改angr脚本,这里需要计算3个寄存器的值
import angr
import claripy
import sys
def main(argv):
path_to_binary = "./03_angr_symbolic_registers"
project = angr.Project(path_to_binary)
start_address = 0x08048980 # :intege (probably hexadecimal)
initial_state = project.factory.blank_state(addr=start_address)
passwd_size_in_bits = 32
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
initial_state.regs.eax = passwd0
initial_state.regs.ebx = passwd1
initial_state.regs.edx = passwd2
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:
for i in simulation.found:
solution_state = i
solution0 = format(solution_state.solver.eval(passwd0), 'x')
solution1 = format(solution_state.solver.eval(passwd1), 'x')
solution2 = format(solution_state.solver.eval(passwd2), 'x')
solution = solution0 + " " + solution1 + " " + solution2
print(solution)
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
运行angr脚本,获取密码,验证通过
$ python scaffold03.py
WARNING | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-12-24 23:12:29,365 | 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-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-12-24 23:12:29,365 | 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-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048980 (main+0x26 in 03_angr_symbolic_registers (0x8048980))
b9ffd04e ccf63fe8 8fd4d959
$ ./03_angr_symbolic_registers
Enter the password: b9ffd04e
ccf63fe8
8fd4d959
Good Job.
0 评论:
发表评论