main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
signed int i; // [esp+Ch] [ebp-Ch]
buffer0 = (char *)malloc(9u);
buffer1 = (char *)malloc(9u);
memset(buffer0, 0, 9u);
memset(buffer1, 0, 9u);
printf("Enter the password: ");
__isoc99_scanf("%8s %8s", buffer0, buffer1);
for ( i = 0; i <= 7; ++i )
{
v3 = complex_function(buffer0[i], i);
buffer0[i] = v3;
buffer1[i] = complex_function(buffer1[i], i + 32);
}
if ( !strncmp(buffer0, "OSIWHBXI", 8u) && !strncmp(buffer1, "FOQVSBZB", 8u) )
puts("Good Job.");
else
puts("Try again.");
free(buffer0);
free(buffer1);
return 0;
}
.text:080486A1 push eax
.text:080486A2 push offset a8s8s ; "%8s %8s"
.text:080486A7 call ___isoc99_scanf
.text:080486AC add esp, 10h
.text:080486AF mov [ebp+var_C], 0
.text:080486B6 jmp short loc_8048722
scanf 호출 이후 주소를 시작 주소로 준다. (0x080486AF)
state = p.factory.blank_state(
addr=0x080486AF,
add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
)
input0 = state.solver.BVS('input0', 8*8)
input1 = state.solver.BVS('input1', 8*8)
동적 할당의 경우 전역변수 buffer0(0x0A2DEF74), buffer1(0x0A2DEF7C)에는 동적 할당된 영역을 가리키는 주소가 적힌다. 따라서 여기에 적히는 주소를 구해줘야 값을 원하는 공간에 넣을 수 있다.
근데 heap 주소를 알지 못해도 사용되지 않는 영역 주소로 fake heap 주소를 지정해서 buffer0, buffer1에 fake 주소들을 적어주고, fake 주소에 input0, input1 값을 저장해줘서 입력을 설정할 수 있다.
>>> import angr
>>> p = angr.Project('06_angr_symbolic_dynamic_memory')
>>> state = p.factory.blank_state(
... addr=0x080486AF,
... add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
... angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
... )
>>>
>>> p.arch
<Arch X86 (LE)>
>>> p.arch.memory_endness
'Iend_LE'
이때 angr는 디폴트로 endian이 big endian인데 바이너리 아키텍처에서 메모리에 값을 넣을 때는 little endian으로 넣어주므로 endianness를 바이너리에 맞게 바꿔준다. 또한 x86 32bit에서 pointer 크기가 4바이트이므로 사이즈로 4로 지정해준다.
# fake heap address
fake_heap_addr1 = 0x5160000
fake_heap_addr2 = 0x5160008
# set buffer0, buffer1 to fake heap address
state.memory.store(0x0A2DEF74, fake_heap_addr1, endness=p.arch.memory_endness, size=4)
state.memory.store(0x0A2DEF7C, fake_heap_addr2, endness=p.arch.memory_endness, size=4)
# store fake heap space with input0, input1
state.memory.store(fake_heap_addr0, input0)
state.memory.store(fake_heap_addr1, input1)
# explore possible path to find solution
simgr.explore(find=lambda s: 'Good'.encode() in s.posix.dumps(sys.stdout.fileno()), avoid=lambda s: 'Try'.encode() in s.posix.dumps(sys.stdout.fileno()))
# if found stash is not empty
if simgr.found:
solution_state = simgr.found[0]
print('solution state:', end=' ')
print(solution_state)
solution0 = solution_state.solver.eval(input0).decode()
solution1 = solution_state.solver.eval(input1).decode()
print("%s %s" % (solution0, solution1))
else:
raise Exception('could no find the solution')
#!/usr/bin/python
import angr, sys
# create angr project
p = angr.Project('./06_angr_symbolic_dynamic_memory')
# set start state
state = p.factory.blank_state(
addr=0x080486AF,
add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
)
# create bitvectors
input0 = state.solver.BVS('input0', 8*8)
input1 = state.solver.BVS('input1', 8*8)
# fake heap address
fake_heap_addr0 = 0x5160000
fake_heap_addr1 = 0x5160008
# set buffer0, buffer1 to fake heap address
state.memory.store(0x0A2DEF74, fake_heap_addr0, endness=p.arch.memory_endness, size=4)
state.memory.store(0x0A2DEF7C, fake_heap_addr1, endness=p.arch.memory_endness, size=4)
# store fake heap space with input0, input1
state.memory.store(fake_heap_addr0, input0)
state.memory.store(fake_heap_addr1, input1)
# create simulation managers
simgr = p.factory.simgr(state)
# explore possible path to find solution
simgr.explore(find=lambda s: 'Good'.encode() in s.posix.dumps(sys.stdout.fileno()), avoid=lambda s: 'Try'.encode() in s.posix.dumps(sys.stdout.fileno()))
# if found stash is not empty
if simgr.found:
solution_state = simgr.found[0]
print('solution state:', end=' ')
print(solution_state)
solution0 = solution_state.solver.eval(input0, cast_to=bytes).decode()
solution1 = solution_state.solver.eval(input1, cast_to=bytes).decode()
print("%s %s" % (solution0, solution1))
else:
raise Exception('could no find the solution')
이후 구해준 solution은 int가 아닌 bytes 값이므로 casting 후 decode해서 구해준다.
[angr_ctf] 08_angr_constraints (0) | 2022.05.15 |
---|---|
[angr_ctf] 07_angr_symbolic_file (0) | 2022.05.14 |
[angr_ctf] 05_angr_symbolic_memory (0) | 2022.05.14 |
[angr_ctf] 04_angr_symbolic_stack (0) | 2022.05.14 |
[angr_ctf] 03_angr_symbolic_registers (0) | 2022.05.14 |