Go back to main menu

L3akCTF 2024 - [REV] Angry

Difficulty : ⭐

Decompilation with Ghidra

I used Ghidra to decompile the angry_patched_skill_issues binary.

Ghidra decompilation of main() function

The main() function is called FUN_001017c7. I decompiled it :

Ghidra decompilation of main() function

It seems that the program asks the user to give a password and does some stuff.
Then, it compares return value of FUN_0010120d with 0 :

  • If it is 0, it prints “Bruh :(\n” to stdout using puts() function.
  • Else, it prints “Congratulations !”

Concolic execution with Angr

Because I’m lazy, i used dynamic symbolic analysis with angr module :

import angr,sys

def main():
    proj = angr.Project('angry_patched_skill_issues')
    init_state = proj.factory.entry_state()
    simulation = proj.factory.simgr(init_state)

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

    if simulation.found:
        solution = simulation.found[0]
        print('flag: ', solution.posix.dumps(sys.stdin.fileno()))
    else:
        print('no flag')

def is_successful(state):
    return b"Congratulations !\n" in state.posix.dumps(sys.stdout.fileno())

def should_abort(state):
    return b"Bruh :(\n" in state.posix.dumps(sys.stdout.fileno())

main()

Flag ?

And after 9 seconds I had this result :

angr result

Here is the return of angr :

\xbe3A\x01\x01angr_4_l\xfff3_d0nt_do_i\x02_m4nU4lly}\x00\x00

Because it is L3akCTF, each flag starts with “L3AK{” and ends with “}”. Knowing this information, I replaced non-printable ascii chars with a dot “.” and here is the result :

L3AK{angr_4_l.f3_d0nt_do_i._m4nU4lly}

I’m missing two printable ascii characters to win. Bruteforcing time !

Bruteforcing flag

import time
import subprocess

final = []

a = time.time()
for i in range(32,127):
        for j in range(32,127):
                crafted_flag = "L3AK{angr_4_l"+str(chr(i))+"f3_d0nt_do_i"+str(chr(j))+"_m4nU4lly}"
                ps = subprocess.Popen(('echo', crafted_flag), stdout=subprocess.PIPE)
                output = subprocess.check_output(("./angry_patched_skill_issues"), stdin=ps.stdout)
                if(b"Congratulations !" in output):
                        final.append(crafted_flag)
                ps.terminate()

print(f"Finished in {time.time()-a} second(s)")
print(f"{len(final)} possible flag(s) found")

bruteforce

Wait… what ? It seems that these 2 bytes can hold any value…

Guessing the flag 🤨

If I use 100% of my brain, I can guess the flag. Indeed, it is partially written in hexspeak.
I’m thinking of these flags :

angr_4_l.f3_d0nt_do_i._m4nU4lly -> angr_for_life_dont_do_it_manually
                  "l.f3" (life) -> ["lif3","lIf3","l1f3"]
                      "i." (it) -> ["it","iT"]

There are 6 possibilities… This final flag worked :

L3AK{angr_4_l1f3_d0nt_do_it_m4nU4lly}