|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 |
|
| 3 | +import angr |
| 4 | +import claripy |
| 5 | +import logging |
| 6 | +logging.getLogger('angr').setLevel(logging.ERROR) |
3 | 7 | import sys |
4 | | - |
5 | | -# Fallback for environments without angr (e.g., CI) |
6 | | -try: |
7 | | - import angr |
8 | | - import claripy |
9 | | - HAS_ANGR = True |
10 | | -except ModuleNotFoundError: |
11 | | - HAS_ANGR = False |
| 8 | +import os |
12 | 9 |
|
13 | 10 | def main(): |
14 | | - if not HAS_ANGR: |
15 | | - # Fallback: Output known good 8-byte binary key |
16 | | - fallback_key = bytes([0x15, 0x40, 0x5d, 0x6b, 0xf2, 0xd6, 0xfc, 0xfb]) |
17 | | - sys.stdout.buffer.write(fallback_key) |
18 | | - sys.exit(0) |
19 | | - |
20 | | - # Load target binary without external library loading |
| 11 | + # Check if ./chal exists |
| 12 | + if not os.path.isfile('./chal'): |
| 13 | + print("Error: './chal' binary not found. Run 'make' to compile it.", file=sys.stderr) |
| 14 | + sys.exit(1) |
| 15 | + |
| 16 | + # Load the binary |
21 | 17 | try: |
22 | | - proj = angr.Project("./chal", auto_load_libs=False) |
| 18 | + proj = angr.Project('./chal', auto_load_libs=False) |
23 | 19 | except Exception as e: |
24 | | - print(f"Error loading binary: {e}. Run 'make' to compile it.", file=sys.stderr) |
| 20 | + print(f"Error loading binary: {e}", file=sys.stderr) |
25 | 21 | sys.exit(1) |
26 | | - |
27 | | - # Declare symbolic variables (8 bytes) |
28 | | - sym_len = 8 |
29 | | - sym_chars = [claripy.BVS(f'sym_{i}', 8) for i in range(sym_len)] |
30 | | - sym_input = claripy.Concat(*sym_chars) # 8 bytes, no \0 |
31 | | - |
32 | | - # Prepare initial program state with symbolic input |
33 | | - init_state = proj.factory.entry_state( |
34 | | - stdin=sym_input, |
35 | | - add_options={angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY} |
36 | | - ) |
37 | | - |
38 | | - # Start symbolic exploration |
39 | | - sim_mgr = proj.factory.simgr(init_state) |
40 | | - sim_mgr.explore( |
41 | | - find=lambda s: b"flag is:" in s.posix.dumps(1), |
42 | | - avoid=lambda s: b"Wrong key!" in s.posix.dumps(1) |
43 | | - ) |
44 | | - |
45 | | - # Extract and print result if a successful state is found |
46 | | - if sim_mgr.found: |
47 | | - result = sim_mgr.found[0].solver.eval(sym_input, cast_to=bytes) |
48 | | - sys.stdout.buffer.write(result[:sym_len]) |
| 22 | + |
| 23 | + # Create symbolic input (8 bytes) |
| 24 | + input_chars = [claripy.BVS(f'char_{i}', 8) for i in range(8)] |
| 25 | + |
| 26 | + # Create initial state with symbolic input on stdin |
| 27 | + state = proj.factory.entry_state(stdin=claripy.Concat(*input_chars)) |
| 28 | + |
| 29 | + # Constrain input to printable ASCII (32-126) |
| 30 | + for c in input_chars: |
| 31 | + state.solver.add(c >= 32) |
| 32 | + state.solver.add(c <= 126) |
| 33 | + |
| 34 | + # Create simulation manager |
| 35 | + simgr = proj.factory.simulation_manager(state) |
| 36 | + |
| 37 | + # Explore to find the path that prints the flag |
| 38 | + def is_successful(state): |
| 39 | + stdout_content = state.posix.dumps(1) # Check stdout |
| 40 | + return b"Correct!" in stdout_content |
| 41 | + |
| 42 | + def is_failed(state): |
| 43 | + stdout_content = state.posix.dumps(1) |
| 44 | + return b"Wrong key!" in stdout_content |
| 45 | + |
| 46 | + simgr.explore(find=is_successful, avoid=is_failed) |
| 47 | + |
| 48 | + # Check if a successful state was found |
| 49 | + if simgr.found: |
| 50 | + found_state = simgr.found[0] |
| 51 | + # Extract concrete values for the input |
| 52 | + secret_key = b"" |
| 53 | + for c in input_chars: |
| 54 | + val = found_state.solver.eval(c) |
| 55 | + secret_key += bytes([val]) |
| 56 | + |
| 57 | + # Output the secret key to stdout |
| 58 | + sys.stdout.buffer.write(secret_key) |
49 | 59 | else: |
50 | 60 | print("No solution found!", file=sys.stderr) |
51 | 61 | sys.exit(1) |
|
0 commit comments