Angr can't solve the googlectf beginner problem

938 views Asked by At

I am a student studying angr, first time.

I'm watching the code in this url.

https://github.com/Dvd848/CTFs/blob/master/2020_GoogleCTF/Beginner.md

import angr
import claripy

FLAG_LEN = 15
STDIN_FD = 0

base_addr = 0x100000 # To match addresses to Ghidra

proj = angr.Project("./a.out", main_opts={'base_addr': base_addr}) 

flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(FLAG_LEN)]
flag = claripy.Concat( *flag_chars + [claripy.BVV(b'\n')]) # Add \n for scanf() to accept the input

state = proj.factory.full_init_state(
        args=['./a.out'],
        add_options=angr.options.unicorn,
        stdin=flag,
)

# Add constraints that all characters are printable
for k in flag_chars:
    state.solver.add(k >= ord('!'))
    state.solver.add(k <= ord('~'))

simgr = proj.factory.simulation_manager(state)
find_addr  = 0x101124 # SUCCESS
avoid_addr = 0x10110d # FAILURE
simgr.explore(find=find_addr, avoid=avoid_addr)

if (len(simgr.found) > 0):
    for found in simgr.found:
        print(found.posix.dumps(STDIN_FD))

https://github.com/google/google-ctf/tree/master/2020/quals/reversing-beginner/attachments

Which is the answer of googlectf beginner.

But, the above code does not work. It doesn't give me the answer.

I want to know why the code is not working.

When I execute this code, the output was empty.

I run the code with python3 in Ubuntu 20.04 in wsl2

Thank you.

1

There are 1 answers

0
sharkmoos On

I believe this script isn't printing anything because angr fails to find a solution and then exits. You can prove this by appending the following to your script:

else:
    raise Exception('Could not find the solution')

If the exception raises, a valid solution was not found.

In terms of why it doesn't work, this code looks like copy & paste from a few different sources, and so it's fairly convoluted.

For example, the way the flag symbol is passed to stdin is not ideal. By default, stdin is a SimPackets, so it's best to keep it that way.

The following script solves the challenge, I have commented it to help you understand. You will notice that changing stdin=angr.SimPackets(name='stdin', content=[(flag, 15)]) to stdin=flag will cause the script to fail, due to the reason mentioned above.

import angr
import claripy

base = 0x400000 # Default angr base

project = angr.Project("./a.out") 

flag = claripy.BVS("flag", 15 * 8) # length is expected in bits here

initial_state = project.factory.full_init_state(
    stdin=angr.SimPackets(name='stdin', content=[(flag, 15)]), # provide symbol and length (in bytes)
    add_options ={
        angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS
    }
)

# constrain flag to common alphanumeric / punctuation characters
[initial_state.solver.add(byte >= 0x20, byte <= 0x7f) for byte in flag.chop(8)]

sim = project.factory.simgr(initial_state)

sim.explore(
    find=lambda s: b"SUCCESS" in s.posix.dumps(1), # search for a state with this result
    avoid=lambda s: b"FAILURE" in s.posix.dumps(1) # states that meet this constraint will be added to the avoid stash
    )

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

    print(f"[+] Success! Solution is: {solution_state.posix.dumps(0)}") # dump whatever was sent to stdin to reach this state

else:
    raise Exception('Could not find the solution') # Tell us if angr failed to find a solution state

A bit of Trivia - there are actually multiple 'solutions' that the program would accept, I guess the CTF flag server only accepts one though.

❯ echo -ne 'CTF{\x00\xe0MD\x17\xd1\x93\x1b\x00n)' | ./a.out   
Flag: SUCCESS