Skip to content

Commit

Permalink
Fix solutions, add run-all script for solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
jakespringer committed Aug 9, 2017
1 parent e306e37 commit 489da52
Show file tree
Hide file tree
Showing 51 changed files with 1,625 additions and 27 deletions.
6 changes: 2 additions & 4 deletions 10_angr_simprocedures/10_angr_simprocedures.c.templite
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def recursive_if_else(variable, value, end_statement, depth):

char msg[] = "${ description }$";

char password[16];
char password[17];

void print_msg() {
printf("%s", msg);
Expand All @@ -61,11 +61,9 @@ int check_equals_${ userdef }$(char* to_check, size_t length) {
}

int main(int argc, char* argv[]) {
char password[20];
int keep_going = 1;
unsigned int x = 0xDEADBEEF;
char buffer[17];
int equals;
int equals = 17;

print_msg();

Expand Down
Binary file modified solutions/00_angr_find/00_angr_find
Binary file not shown.
73 changes: 73 additions & 0 deletions solutions/00_angr_find/scaffold00.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Before you begin, here are a few notes about these capture-the-flag
# challenges.
#
# Each binary, when run, will ask for a password, which can be entered via stdin
# (typing it into the console.) Many of the levels will accept many different
# passwords. Your goal is to find a single password that works for each binary.
#
# If you enter an incorrect password, the program will print "Try again." If you
# enter a correct password, the program will print "Good Job."
#
# Each challenge will be accompanied by a file like this one, named
# "scaffoldXX.py". It will offer guidance as well as the skeleton of a possible
# solution. You will have to edit each file. In some cases, you will have to
# edit it significantly. While use of these files is recommended, you can write
# a solution without them, if you find that they are too restrictive.
#
# Places in the scaffoldXX.py that require a simple substitution will be marked
# with three question marks (???). Places that require more code will be marked
# with an ellipsis (...). Comments will document any new concepts, but will be
# omitted for concepts that have already been covered (you will need to use
# previous scaffoldXX.py files as a reference to solve the challenges.) If a
# comment documents a part of the code that needs to be changed, it will be
# marked with an exclamation point at the end, on a separate line (!).

import angr
import sys

def main(argv):
# Create an Angr project.
# If you want to be able to point to the binary from the command line, you can
# use argv[1] as the parameter. Then, you can run the script from the command
# line as follows:
# python ./scaffold00.py [binary]
# (!)
path_to_binary = ??? # :string
project = angr.Project(path_to_binary)

# Tell Angr where to start executing (should it start from the main()
# function or somewhere else?) For now, use the entry_state function
# to instruct Angr to start from the main() function.
initial_state = project.factory.entry_state()

# Create a simulation manager initialized with the starting state. It provides
# a number of useful tools to search and execute the binary.
simulation = project.factory.simgr(initial_state)

# Explore the binary to attempt to find the address that prints "Good Job."
# You will have to find the address you want to find and insert it here.
# This function will keep executing until it either finds a solution or it
# has explored every possible path through the executable.
# (!)
print_good_address = ??? # :integer (probably in hexadecimal)
simulation.explore(find=print_good_address)

# Check that we have found a solution. The simulation.explore() method will
# set simulation.found to a list of the states that it could find that reach
# the instruction we asked it to search for. Remember, in Python, if a list
# is empty, it will be evaluated as false, otherwise true.
if simulation.found:
# The explore method stops after it finds a single state that arrives at the
# target address.
solution_state = simulation.found[0]

# Print the string that Angr wrote to stdin to follow solution_state. This
# is our solution.
print solution_state.posix.dumps(sys.stdin.fileno())
else:
# If Angr could not find a path that reaches print_good_address, throw an
# error. Perhaps you mistyped the print_good_address?
raise Exception('Could not find the solution')

if __name__ == '__main__':
main(sys.argv)
2 changes: 1 addition & 1 deletion solutions/00_angr_find/solve00.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def main(argv):
# This function will keep executing until it either finds a solution or it
# has explored every possible path through the executable.
# (!)
print_good_address = 0x8048670 # :integer (probably in hexadecimal)
print_good_address = 0x8048682 # :integer (probably in hexadecimal)
simulation.explore(find=print_good_address)

# Check that we have found a solution. The simulation.explore() method will
Expand Down
Binary file modified solutions/01_angr_avoid/01_angr_avoid
Binary file not shown.
27 changes: 27 additions & 0 deletions solutions/01_angr_avoid/scaffold01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import angr
import sys

def main(argv):
path_to_binary = ???
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state)

# Explore the binary, but this time, instead of only looking for a state that
# reaches the print_good_address, also find a state that does not reach
# will_not_succeed_address. The binary is pretty large, to save you some time,
# everything you will need to look at is near the beginning of the address
# space.
# (!)
print_good_address = ???
will_not_succeed_address = ???
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)
4 changes: 2 additions & 2 deletions solutions/01_angr_avoid/solve01.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def main(argv):
# everything you will need to look at is near the beginning of the address
# space.
# (!)
print_good_address = 0x80485f7
will_not_succeed_address = 0x80485a8
print_good_address = 0x080485e5
will_not_succeed_address = 0x080485a8
simulation.explore(find=print_good_address, avoid=will_not_succeed_address)

if simulation.found:
Expand Down
Binary file modified solutions/02_angr_find_condition/02_angr_find_condition
Binary file not shown.
51 changes: 51 additions & 0 deletions solutions/02_angr_find_condition/scaffold02.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# It is very useful to be able to search for a state that reaches a certain
# instruction. However, in some cases, you may not know the address of the
# specific instruction you want to reach (or perhaps there is no single
# instruction goal.) In this challenge, you don't know which instruction
# grants you success. Instead, you just know that you want to find a state where
# the binary prints "Good Job."
#
# Angr is powerful in that it allows you to search for a states that meets an
# arbitrary condition that you specify in Python, using a predicate you define
# as a function that takes a state and returns True if you have found what you
# are looking for, and False otherwise.

import angr
import sys

def main(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state)

# Define a function that checks if you have found the state you are looking
# for.
def is_successful(state):
# Dump whatever has been printed out by the binary so far into a string.
stdout_output = state.posix.dumps(sys.stdout.fileno())

# Return whether 'Good Job.' has been printed yet.
# (!)
return ??? # :boolean

# Same as above, but this time check if the state should abort. If you return
# False, Angr will continue to step the state. In this specific challenge, the
# only time at which you will know you should abort is when the program prints
# "Try again."
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return ??? # :boolean

# Tell Angr to explore the binary and find any state that is_successful identfies
# as a successful state by returning True.
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)
Binary file modified solutions/03_angr_symbolic_registers/03_angr_symbolic_registers
Binary file not shown.
78 changes: 78 additions & 0 deletions solutions/03_angr_symbolic_registers/scaffold03.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Angr doesn't currently support reading multiple things with scanf (Ex:
# scanf("%u %u).) You will have to tell the simulation engine to begin the
# program after scanf is called and manually inject the symbols into registers.

import angr
import claripy
import sys

def main(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary)

# Sometimes, you want to specify where the program should start. The variable
# start_address will specify where the symbolic execution engine should begin.
# Note that we are using blank_state, not entry_state.
# (!)
start_address = ??? # :integer (probably hexadecimal)
initial_state = project.factory.blank_state(addr=start_address)

# Create a symbolic bitvector (the datatype Angr uses to inject symbolic
# values into the binary.) The first parameter is just a name Angr uses
# to reference it.
# You will have to construct multiple bitvectors. Copy the two lines below
# and change the variable names. To figure out how many (and of what size)
# you need, dissassemble the binary and determine the format parameter passed
# to scanf.
# (!)
password0_size_in_bits = ??? # :integer
password0 = claripy.BVS('password0', password0_size_in_bits)
...

# Set a register to a symbolic value. This is one way to inject symbols into
# the program.
# initial_state.regs stores a number of convenient attributes that reference
# registers by name. For example, to set eax to password0, use:
#
# initial_state.regs.eax = password0
#
# You will have to set multiple registers to distinct bitvectors. Copy and
# paste the line below and change the register. To determine which registers
# to inject which symbol, dissassemble the binary and look at the instructions
# immediately following the call to scanf.
# (!)
initial_state.regs.??? = password0
...

simulation = project.factory.simgr(initial_state)

def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return ???

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return ???

simulation.explore(find=is_successful, avoid=should_abort)

if simulation.found:
solution_state = simulation.found[0]

# Solve for the symbolic values. If there are multiple solutions, we only
# care about one, so we can use any_int, which returns any (but only one)
# solution. Pass any_int the bitvector you want to solve for.
# (!)
solution0 = solution_state.se.any_int(password0)
...

# Aggregate and format the solutions you computed above, and then print
# the full string. Pay attention to the order of the integers, and the
# expected base (decimal, octal, hexadecimal, etc).
solution = ??? # :string
print solution
else:
raise Exception('Could not find the solution')

if __name__ == '__main__':
main(sys.argv)
4 changes: 2 additions & 2 deletions solutions/03_angr_symbolic_registers/solve03.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def main(argv):
# start_address will specify where the symbolic execution engine should begin.
# Note that we are using blank_state, not entry_state.
# (!)
start_address = 0x8048864 # :integer (probably hexadecimal)
start_address = 0x80488d1 # :integer (probably hexadecimal)
initial_state = project.factory.blank_state(addr=start_address)

# Create a symbolic bitvector (the datatype Angr uses to inject symbolic
Expand Down Expand Up @@ -76,7 +76,7 @@ def should_abort(state):
# Aggregate and format the solutions you computed above, and then print
# the full string. Pay attention to the order of the integers, and the
# expected base (decimal, octal, hexadecimal, etc).
solution = ' '.join(map(str, [ solution0, solution1, solution2 ])) # :string
solution = ' '.join(map('{:x}'.format, [ solution0, solution1, solution2 ])) # :string
print solution
else:
raise Exception('Could not find the solution')
Expand Down
Binary file modified solutions/04_angr_symbolic_stack/04_angr_symbolic_stack
Binary file not shown.
Loading

0 comments on commit 489da52

Please sign in to comment.