반응형
이번 문제는 main함수에서 문자열을 입력받고, 해당 문자열을 특정 연산 후에 check_equals 함수에서 몇개의 문자가 맞는지 확인을 한다.
시작 주소는 scanf 이후로 잡고, buffer 변수를 만든다.
start_address = 0x08048622
initial_state = project.factory.blank_state(
addr=start_address,
add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
)
buffer = claripy.BVS('buffer', 16 * 8)
buffer_address = 0x0804A050
initial_state.memory.store(buffer_address, buffer)
check_constraint의 인자값을 넣기 시작하는 주소와 인자값의 주소, 사이즈를 입력해준다.
address_to_check_constraint = 0x0804866C
simulation.explore(find=address_to_check_constraint)
if simulation.found:
solution_state = simulation.found[0]
constrained_parameter_address = 0x0804A050
constrained_parameter_size_bytes = 16
constrained_parameter_bitvector = solution_state.memory.load(
constrained_parameter_address,
constrained_parameter_size_bytes
)
constrained_parameter_desired_value = 'AUPDNNPROEZRJWKB'
# The binary asks for a 16 character password to which is applies a complex
# function and then compares with a reference string with the function
# check_equals_[reference string]. (Decompile the binary and take a look at it!)
# The source code for this function is provided here. However, the reference
# string in your version will be different than AABBCCDDEEFFGGHH:
#
# #define REFERENCE_PASSWORD = "AABBCCDDEEFFGGHH";
# int check_equals_AABBCCDDEEFFGGHH(char* to_check, size_t length) {
# uint32_t num_correct = 0;
# for (int i=0; i<length; ++i) {
# if (to_check[i] == REFERENCE_PASSWORD[i]) {
# num_correct += 1;
# }
# }
# return num_correct == length;
# }
#
# ...
#
# char* input = user_input();
# char* encrypted_input = complex_function(input);
# if (check_equals_AABBCCDDEEFFGGHH(encrypted_input, 16)) {
# puts("Good Job.");
# } else {
# puts("Try again.");
# }
#
# The function checks if *to_check == "AABBCCDDEEFFGGHH". Verify this yourself.
# While you, as a human, can easily determine that this function is equivalent
# to simply comparing the strings, the computer cannot. Instead the computer
# would need to branch every time the if statement in the loop was called (16
# times), resulting in 2^16 = 65,536 branches, which will take too long of a
# time to evaluate for our needs.
#
# We do not know how the complex_function works, but we want to find an input
# that, when modified by complex_function, will produce the string:
# AABBCCDDEEFFGGHH.
#
# In this puzzle, your goal will be to stop the program before this function is
# called and manually constrain the to_check variable to be equal to the
# password you identify by decompiling the binary. Since, you, as a human, know
# that if the strings are equal, the program will print "Good Job.", you can
# be assured that if the program can solve for an input that makes them equal,
# the input will be the correct password.
import angr
import claripy
import sys
def main(argv):
path_to_binary = '08_angr_constraints'
project = angr.Project(path_to_binary)
start_address = 0x08048622
initial_state = project.factory.blank_state(
addr=start_address,
add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS}
)
buffer = claripy.BVS('buffer', 16 * 8)
buffer_address = 0x0804A050
initial_state.memory.store(buffer_address, buffer)
simulation = project.factory.simgr(initial_state)
# Angr will not be able to reach the point at which the binary prints out
# 'Good Job.'. We cannot use that as the target anymore.
# (!)
address_to_check_constraint = 0x0804866C
simulation.explore(find=address_to_check_constraint)
if simulation.found:
solution_state = simulation.found[0]
# Recall that we need to constrain the to_check parameter (see top) of the
# check_equals_ function. Determine the address that is being passed as the
# parameter and load it into a bitvector so that we can constrain it.
# (!)
constrained_parameter_address = 0x0804A050
constrained_parameter_size_bytes = 16
constrained_parameter_bitvector = solution_state.memory.load(
constrained_parameter_address,
constrained_parameter_size_bytes
)
# We want to constrain the system to find an input that will make
# constrained_parameter_bitvector equal the desired value.
# (!)
constrained_parameter_desired_value = 'AUPDNNPROEZRJWKB' # :string (encoded)
# Specify a claripy expression (using Pythonic syntax) that tests whether
# constrained_parameter_bitvector == constrained_parameter_desired_value.
# Add the constraint to the state to let z3 attempt to find an input that
# will make this expression true.
solution_state.add_constraints(constrained_parameter_bitvector == constrained_parameter_desired_value)
# Solve for the constrained_parameter_bitvector.
# (!)
solution = solution_state.solver.eval(buffer,cast_to=bytes).decode()
print(solution)
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
반응형
'CTF. > Angr Tutorial For CTF' 카테고리의 다른 글
07_angr_symbolic_file #Angr Tutorial For CTF (0) | 2022.08.26 |
---|---|
06_angr_symbolic_daynamic_memory #Angr Tutorial For CTF (0) | 2022.08.25 |
05_angr_symbolic_memory #Angr Tutorial For CTF (0) | 2022.08.24 |
04_angr_symbolic_stack #Angr Tutorial For CTF (0) | 2022.08.23 |
03_angr_symbolic_registers #Angr Tutorial For CTF (0) | 2022.08.22 |
댓글