x86_64: Phase 3

In [1]:
# Imports

import angr
import claripy

We push the input values to the stack and then pop them right before the function exits. Since no value is pushed onto the stack during the execution of the function, we can safely manipulate the stack in the way we planned.

Load the binary.

In [2]:
project = angr.Project('bomb64')

Define the address for the start of the execution path.

In [3]:
# The address where the symbolic execution shall begin. It is right after parsing of two numbers taken as input.
addr_start = 0x400F60

# The address of the return of phase_3.
# We end it there so as to dump the stack and retrieve values before the stack frame is discarded.
addr_target = 0x400FC9

# The address of the first instruction of the explode_bomb function, which is to be avoided.
addr_bomb = 0x40143A

Define a blank state for the simulation, a state with most of it's data uninitialized. Pass the address where the state is initialized, along with the user input to be given via standard input.

In [4]:
state = project.factory.blank_state(addr=addr_start)

Setup the stack.

In [5]:
# Push the null value,
# so as to setup the stack as depicted in the pictures. 
state.stack_push(0x0)

Push the symbolic values to be input on the stack.

In [6]:
# We push two symbolic values required as the pair-set values.
for i in range(2):
    state.stack_push(claripy.BVS("num_{}".format(i), 32))

Create a simulation manager with this blank state that would help us manage the symbolic execution.

In [7]:
simgr = project.factory.simulation_manager(state)

We call the explore method of the simulation manager, tasked with finding an execution path that reaches the target address and avoids the address which explodes the bomb.

In [8]:
simgr.explore(find=addr_target, avoid=addr_bomb, enable_veritesting=True)
WARNING | 2020-06-16 00:21:30,064 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2020-06-16 00:21:30,066 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2020-06-16 00:21:30,066 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
WARNING | 2020-06-16 00:21:30,067 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2020-06-16 00:21:30,070 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages.
WARNING | 2020-06-16 00:21:30,076 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:30,504 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,025 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,114 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,194 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,277 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,375 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,464 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:31,551 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
Out[8]:
<SimulationManager with 15 active, 1 found, 2 avoid>

We dereference the execution path "found" by the simulation manager and dump the stack.

In [9]:
found = simgr.found[0]

Remove the stack setup first.

In [10]:
# Pop the null value we pushed to stack first,
# as part of stack setup.
found.stack_pop()
WARNING | 2020-06-16 00:21:31,673 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
Out[10]:
<BV64 Reverse(Reverse(num_1_40_32) .. mem_7fffffffffeffe4_50_32{UNINITIALIZED})>
In [11]:
answer = []

curr = found.solver.eval(found.stack_pop())
print("Popped value: {0}".format(hex(curr)))

# Masking is done using bit-wise operators to split the values merged in the block.
lower_end = curr & 0xffffffff
print("Decimal number on the lower end: {0}".format(lower_end))
answer.append(str(lower_end))
higher_end = curr>>32 & 0xffffffff
answer.append(' ')
print("Decimal number on the higher end: {0}\n".format(higher_end))
answer.append(str(higher_end))
answer.append(' ')

# Removes the space in the end.
''.join(answer[:-1])
Popped value: 0x13700000001
Decimal number on the lower end: 1
Decimal number on the higher end: 311

Out[11]:
'1 311'

That's the two number combination we need to input to pass the third phase.

Let's add a constraint to specifically reach an end with values that satisfy each case in the jump table. Add the constraint on the first value since that is used to match a case in the table.

In the form: state.solver.add(num_1 == x), where x is the integer value of a case out of the jump table.

Let's put it in a loop to get the all the values.

In [12]:
for x in range(0, 8):
    # Load the binary.
    project = angr.Project('bomb64')

    # Init the addresses.
    addr_start = 0x400F60
    addr_target = 0x400FC9
    addr_bomb = 0x40143A

    # Init a blank state.
    state = project.factory.blank_state(addr=addr_start)

    # Setup the stack.
    state.stack_push(0x0)

    # Define the symbolic values.
    num_1 = claripy.BVS("num_1", 32)
    num_2 = claripy.BVS("num_2", 32)

    # Add the constraint.
    state.solver.add(num_1 == x)

    # Push the input to the stack.
    state.stack_push(num_1)
    state.stack_push(num_2)

    # Init the simulation manager.
    simgr = project.factory.simulation_manager(state)

    # Let Angr explore.
    simgr.explore(find=addr_target, avoid=addr_bomb, enable_veritesting=True)

    # Get the answer from the found state.
    found = simgr.found[0]
    found.stack_pop()

    answer = []

    curr = found.solver.eval(found.stack_pop())

    lower_end = curr & 0xffffffff
    answer.append(str(lower_end))
    higher_end = curr>>32 & 0xffffffff
    answer.append(' ')
    answer.append(str(higher_end))
    answer.append(' ')

    # Removes the space in the end.
    print(''.join(answer[:-1]))
WARNING | 2020-06-16 00:21:33,850 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:34,082 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:34,177 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
0 207
WARNING | 2020-06-16 00:21:36,298 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:36,525 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:36,608 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
1 311
WARNING | 2020-06-16 00:21:39,952 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:40,400 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:40,518 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
2 707
WARNING | 2020-06-16 00:21:43,575 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:43,807 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:43,881 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
3 256
WARNING | 2020-06-16 00:21:46,728 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:46,997 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:47,088 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
4 389
WARNING | 2020-06-16 00:21:50,308 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:50,638 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:50,735 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
5 206
WARNING | 2020-06-16 00:21:53,950 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:54,201 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:54,289 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
6 682
WARNING | 2020-06-16 00:21:56,242 | angr.state_plugins.symbolic_memory | Filling register rax with 8 unconstrained bytes referenced from 0x400f60 (phase_3+0x1d in bomb64 (0x400f60))
WARNING | 2020-06-16 00:21:56,619 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffec with 4 unconstrained bytes referenced from 0x400fbe (phase_3+0x7b in bomb64 (0x400fbe))
WARNING | 2020-06-16 00:21:56,755 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffeffe4 with 4 unconstrained bytes referenced from 0x400fc9 (phase_3+0x86 in bomb64 (0x400fc9))
7 327

And there we go. Values to satisfy all the cases in the jumptable.