Angr CTF(二)
04_angr_symbolic_stack——栈
$ ./04_angr_symbolic_stack
Enter the password: asdf
Try again.
这题跟上一题有点不一样,scanf获取数据后保存在栈上,不是寄存器
void handle_user(void)
{
int local_14;
int local_10 [3];
__isoc99_scanf("%u %u",local_10,&local_14);
local_10[0] = complex_function0(local_10[0]);
local_14 = complex_function1(local_14);
if ((local_10[0] == 0x5019fa83) && (local_14 == -0x13d421a4)) {
puts("Good Job.");
}
else {
puts("Try again.");
}
return;
}
查看汇编代码,两个参数分别保存在[ebp+var_10]和[ebp+var_C],scanf返回后的第一行代码“add esp, 10h”,是用于“ call ___isoc99_scanf”后平衡堆栈的,那么start_address选择后面第二行代码“.text:08048697 mov eax, [ebp+var_C]”
.text:08048679 var_10 = dword ptr -10h
.text:08048679 var_C = dword ptr -0Ch
.text:08048679
.text:08048679 ; __unwind {
.text:08048679 push ebp
.text:0804867A mov ebp, esp
.text:0804867C sub esp, 18h
.text:0804867F sub esp, 4
.text:08048682 lea eax, [ebp+var_10]
.text:08048685 push eax
.text:08048686 lea eax, [ebp+var_C]
.text:08048689 push eax
.text:0804868A push offset aUU ; "%u %u"
.text:0804868F call ___isoc99_scanf
.text:08048694 add esp, 10h;平衡堆栈
.text:08048697 mov eax, [ebp+var_C];start_address
.text:0804869A sub esp, 0Ch
.text:0804869D push eax
.text:0804869E call complex_function0
.text:080486A3 add esp, 10h
.text:080486A6 mov [ebp+var_C], eax
.text:080486A9 mov eax, [ebp+var_10]
.text:080486AC sub esp, 0Ch
.text:080486AF push eax
.text:080486B0 call complex_function1
.text:080486B5 add esp, 10h
.text:080486B8 mov [ebp+var_10], eax
.text:080486BB mov eax, [ebp+var_C]
.text:080486BE cmp eax, 773024D1h
.text:080486C3 jnz short loc_80486CF
.text:080486C5 mov eax, [ebp+var_10]
.text:080486C8 cmp eax, 0BC4311CFh
.text:080486CD jz short loc_80486E1
.text:080486CF
.text:080486CF loc_80486CF: ; CODE XREF: handle_user+4A↑j
.text:080486CF sub esp, 0Ch
.text:080486D2 push offset s ; "Try again."
.text:080486D7 call _puts
.text:080486DC add esp, 10h
.text:080486DF jmp short loc_80486F1
.text:080486E1 ; ---------------------------------------------------------------------------
.text:080486E1
.text:080486E1 loc_80486E1: ; CODE XREF: handle_user+54↑j
.text:080486E1 sub esp, 0Ch
.text:080486E4 push offset aGoodJob ; "Good Job."
.text:080486E9 call _puts
.text:080486EE add esp, 10h
.text:080486F1
.text:080486F1 loc_80486F1: ; CODE XREF: handle_user+66↑j
.text:080486F1 nop
.text:080486F2 leave
.text:080486F3 retn
.text:080486F3 ; } // starts at 8048679
修改angr脚本,需要注意实际栈空间
import angr
import claripy
import sys
def main(argv):
path_to_binary = "./04_angr_symbolic_stack"
project = angr.Project(path_to_binary)
# start_address选择scanf返回后的位置
start_address = 0x8048697
initial_state = project.factory.blank_state(addr=start_address)
# 将ESP指针恢复到和EBP指针一致,恢复栈帧初始状态,方便计算
initial_state.regs.ebp = initial_state.regs.esp
# 程序需要两个无符号的整数值(%u %u,即unsigned int),因此符号位向量的大小将为32位
passwd_size_in_bits = 32
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
# 两个参数都是32bits,也就是4字节,所以总共需要8字节,也就是ESP - 0x8
padding_length_in_bytes = 0x8 # :integer
initial_state.regs.esp -= padding_length_in_bytes
initial_state.stack_push(passwd0)
initial_state.stack_push(passwd1)
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:
for i in simulation.found:
solution_state = i
solution0 = (solution_state.solver.eval(passwd0))
solution1 = (solution_state.solver.eval(passwd1))
print("[+] Success! Solution is: {0} {1}".format(solution0, solution1))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
运行angr脚本,获取密码,验证通过
$ python3 scaffold04.py
[+] Success! Solution is: 1704280884 2382341151
$ ./04_angr_symbolic_stack
Enter the password: 1704280884
2382341151
Good Job.
05_angr_symbolic_memory——内存
$ ./05_angr_symbolic_memory
Enter the password: asdfa
asdf
dfs
adg
Try again.
程序将四个8字节长的字符串作为输入,字符串分别位于以下地址[user_input=0xA1BA1C0, 0xA1BA1C8, 0xA1BA1D0, 0xA1BA1D8],输入的字符串循环输入complex_function()函数进行变换,循环变换后的字符串与 “NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN”比较前0x20个字符
undefined4 main(void)
{
undefined uVar1;
int iVar2;
int local_14;
memset(user_input,0,0x21);
printf("Enter the password: ");
__isoc99_scanf("%8s %8s %8s %8s",user_input,0xa623108,0xa623110,0xa623118);
for (local_14 = 0; local_14 < 0x20; local_14 = local_14 + 1) {
uVar1 = complex_function((int)(char)user_input[local_14],local_14);
user_input[local_14] = uVar1;
}
iVar2 = strncmp(user_input,"GLCOREPCFIMXROLPXJNEXMLKSEIMFROR",0x20);
if (iVar2 == 0) {
puts("Good Job.");
}
else {
puts("Try again.");
}
return 0;
}
start_address照样选择scanf返回后的地址:08048601
080485e0 68 d8 PUSH user_input[24]
a1 1b 0a
080485e5 68 d0 PUSH user_input[16]
a1 1b 0a
080485ea 68 c8 PUSH user_input[8]
a1 1b 0a
080485ef 68 c0 PUSH user_input = ??
a1 1b 0a
080485f4 68 33 PUSH s_%8s_%8s_%8s_%8s_08048733 = "%8s %8s %8s %8s"
87 04 08
080485f9 e8 02 CALL <EXTERNAL>::__isoc99_scanf undefined __isoc99_scanf()
fe ff ff
080485fe 83 c4 20 ADD ESP,0x20
08048601 c7 45 MOV dword ptr [EBP + local_14],0x0
f4 00
00 00 00
08048608 eb 2d JMP LAB_08048637
LAB_0804860a XREF[1]: 0804863b(j)
0804860a 8b 45 f4 MOV EAX,dword ptr [EBP + local_14]
0804860d 05 c0 ADD EAX,user_input = ??
a1 1b 0a
08048612 0f b6 00 MOVZX EAX,byte ptr [EAX]=>user_input = ??
08048615 0f be c0 MOVSX EAX,AL
08048618 83 ec 08 SUB ESP,0x8
0804861b ff 75 f4 PUSH dword ptr [EBP + local_14]
0804861e 50 PUSH EAX
0804861f e8 25 CALL complex_function undefined complex_functio
ff ff ff
修改angr脚本,这里有四个内存值都需要计算
import angr
import claripy
import sys
def main(argv):
path_to_binary = "./05_angr_symbolic_memory"
project = angr.Project(path_to_binary)
start_address = 0x08048601
initial_state = project.factory.blank_state(addr=start_address)
# The binary is calling scanf("%8s %8s %8s %8s").
# 每个值长度为8字节,即8*8=64bits
passwd_size_in_bits = 64
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
passwd3 = claripy.BVS('passwd3', passwd_size_in_bits)
passwd0_address = 0xA1BA1C0
#passwd1_address = 0xA1BA1C8
#passwd2_address = 0xA1BA1D0
#passwd3_address = 0xA1BA1D8
initial_state.memory.store(passwd0_address, passwd0)
initial_state.memory.store(passwd0_address + 0x8, passwd1)
initial_state.memory.store(passwd0_address + 0x10, passwd2)
initial_state.memory.store(passwd0_address + 0x18, passwd3)
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:
for i in simulation.found:
solution_state = i
solution0 = solution_state.solver.eval(passwd0,cast_to=bytes)
solution1 = solution_state.solver.eval(passwd1,cast_to=bytes)
solution2 = solution_state.solver.eval(passwd2,cast_to=bytes)
solution3 = solution_state.solver.eval(passwd3,cast_to=bytes)
solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3
print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
运行angr脚本,获取密码,验证通过
$ python3 scaffold05.py
WARNING | 2023-12-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-12-25 21:39:32,058 | 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-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-12-25 21:39:32,059 | 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-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-12-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048601 (main+0x59 in 05_angr_symbolic_memory (0x8048601))
[+] Success! Solution is: NAXTHGNR JVSFTPWE LMGAUHWC XMDCPALU
$ ./05_angr_symbolic_memory
Enter the password: NAXTHGNR
JVSFTPWE
LMGAUHWC
XMDCPALU
Good Job.
06_angr_symbolic_dynamic_memory——动态内存
$ ./06_angr_symbolic_dynamic_memory
Enter the password: ASDF
AG
Try again.
跟上一题基本一致,不过字符串的内存是通过堆malloc(),而不是栈分配
undefined4 main(void)
{
char *pcVar1;
char cVar2;
int iVar3;
int local_14;
buffer0 = (char *)malloc(9);
buffer1 = (char *)malloc(9);
memset(buffer0,0,9);
memset(buffer1,0,9);
printf("Enter the password: ");
__isoc99_scanf("%8s %8s",buffer0,buffer1);
for (local_14 = 0; local_14 < 8; local_14 = local_14 + 1) {
pcVar1 = buffer0 + local_14;
cVar2 = complex_function((int)buffer0[local_14],local_14);
*pcVar1 = cVar2;
pcVar1 = buffer1 + local_14;
cVar2 = complex_function((int)buffer1[local_14],local_14 + 0x20);
*pcVar1 = cVar2;
}
iVar3 = strncmp(buffer0,"UODXLZBI",8);
if (iVar3 == 0) {
iVar3 = strncmp(buffer1,"UAORRAYF",8);
if (iVar3 == 0) {
puts("Good Job.");
goto LAB_08048766;
}
}
puts("Try again.");
LAB_08048766:
free(buffer0);
free(buffer1);
return 0;
}
[buffer1]=0abcc8ac; [buffer0]=0abcc8a4; start_address=08048699;
0804867c 8b 15 MOV EDX,dword ptr [buffer1] = ??
ac c8
bc 0a
08048682 a1 a4 MOV EAX,[buffer0] = ??
c8 bc 0a
08048687 83 ec 04 SUB ESP,0x4
0804868a 52 PUSH EDX
0804868b 50 PUSH EAX
0804868c 68 43 PUSH s_%8s_%8s_08048843 = "%8s %8s"
88 04 08
08048691 e8 ca CALL <EXTERNAL>::__isoc99_scanf undefined __isoc99_scanf()
fd ff ff
08048696 83 c4 10 ADD ESP,0x10
08048699 c7 45 MOV dword ptr [EBP + local_14],0x0
f4 00
00 00 00
080486a0 eb 64 JMP LAB_08048706
根据上述信息修改配置脚本
import angr
import claripy
import sys
def main(argv):
path_to_binary = "./06_angr_symbolic_dynamic_memory"
project = angr.Project(path_to_binary)
start_address = 0x8048699
initial_state = project.factory.blank_state(addr=start_address)
passwd_size_in_bits = 64
passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
# fake_heap_address可以任意设置
fake_heap_address0 = 0xffffc8a4
pointer_to_malloc_memory_address0 = 0xabcc8a4
fake_heap_address1 = 0xffffc8ac
pointer_to_malloc_memory_address1 = 0xabcc8ac
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)
initial_state.memory.store(fake_heap_address0, passwd0)
initial_state.memory.store(fake_heap_address1, passwd1)
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:
for i in simulation.found:
solution_state = i
solution0 = solution_state.solver.eval(passwd0, cast_to=bytes)
solution1 = solution_state.solver.eval(passwd1, cast_to=bytes)
print("[+] Success! Solution is: {0} {1}".format(solution0.decode('utf-8'), solution1.decode('utf-8')))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
运行angr脚本,获取密码,验证通过
$ python3 scaffold06.py
WARNING | 2023-12-25 22:25:35,135 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xffffc93c. Default to arch.bits.
WARNING | 2023-12-25 22:25:35,136 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xffffc94c. Default to arch.bits.
WARNING | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING | 2023-12-25 22:25:35,141 | 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-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2023-12-25 22:25:35,141 | 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-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2023-12-25 22:25:35,142 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048699 (main+0x8d in 06_angr_symbolic_dynamic_memory (0x8048699))
WARNING | 2023-12-25 22:25:36,807 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc944 with 8 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING | 2023-12-25 22:25:36,807 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc954 with 105 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING | 2023-12-25 22:25:38,488 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc9bd with 16 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
[+] Success! Solution is: UBDKLMBV UNOERNYS
$ ./06_angr_symbolic_dynamic_memory
Enter the password: UBDKLMBV
UNOERNYS
Good Job.
0 评论:
发表评论