This is a write-up for the Egypt challenge of r2con 2018 CTF.
Are you a master in lockpicking?
Try to open this lock bruteforcing the combination!
Put the flag inside r2con{flag} and check if you got the right flag.
binary: forceme_9f132ba529bfd562380d448f4904c76f
Open the binary with radare2 and list the functions
$ radare2 forceme_9f132ba529bfd562380d448f4904c76f
[0x004004c0]> aaa
[0x004004c0]> e asm.pseudo = 1
[0x004004c0]> e asm.bytes = 0
[0x004004c0]> afl
0x00400047 1 170 fcn.00400047
0x00400438 3 26 sym._init
0x00400470 1 6 sym.imp.putchar
0x00400480 1 6 sym.imp.puts
0x00400490 1 6 sym.imp.strlen
0x004004a0 1 6 sym.imp.__libc_start_main
0x004004b0 1 6 sub.__gmon_start_4b0
0x004004c0 1 41 entry0
0x004004f0 4 50 -> 41 sym.deregister_tm_clones
0x00400530 4 58 -> 55 sym.register_tm_clones
0x00400570 3 28 sym.__do_global_dtors_aux
0x00400590 4 38 -> 35 entry1.init
0x004005b6 1 53 sym.code
0x004005eb 8 62 sym.check
0x00400629 1 117 sym.banner
0x0040069e 1 117 sym.goodboy
0x00400713 9 543 main
0x00400940 4 101 sym.__libc_csu_init
0x004009b0 1 2 sym.__libc_csu_fini
0x004009b4 1 9 sym._fini
We quickly see that we want to end up in sym.goodboy
which is called from the main function and prints some ascii art.
[0x004004c0]> axt sym.goodboy
main 0x40091a [CALL] call sym.goodboy
[0x0040069e]> s main
[0x00400713]> pdf
Looking at the main function we see that the password need to be at least of length 9
0x00400762 rdi = rax ; const char *s
0x00400765 sym.imp.strlen () ; size_t strlen(const char *s)
0x0040076a var = rax - 9 ; 9
0x0040076e if (var) goto 0x40092b
And that there are two end paths
0x00400909 if (!var) goto 0x400921
0x0040090b edi = str.Success__You_Got_it ; 0x400b05 ; "Success! You Got it!" ; const char *s
0x00400910 sym.imp.puts () ; int puts(const char *s)
0x00400915 eax = 0
0x0040091a sym.goodboy ()
0x0040091f goto 0x40092b
0x00400921 edi = str.Try_harder ; 0x400b1a ; "Try harder" ; const char *s
0x00400926 sym.imp.puts () ; int puts(const char *s)
0x0040092b eax = 0
0x00400930
0x00400931 return 0
Address 0x0040091a
brings us to a success through the function sym.goodboy
while address 0x00400921
(or any below) brings us to failure with a
"Try harder".
This is a typical job for angr.
We know:
- the password needs to be at least 9 bytes long
- a success path is reachable at
0x0040091a
- a failure path is reachable at
0x00400921
Finding the password took less than 5s to angr
$ time ./forceme-angr.py
<SimulationManager with 1 found, 19 deadended, 1 avoid>
esilrulez
./forceme-angr.py 3.61s user 0.12s system 99% cpu 3.742 total
Below the angr script used
!/usr/bin/env python2
#
# Egypt - lockpicking - 100 pts
# type: flag
# category: easy
#
import angr
import claripy
path = './forceme_9f132ba529bfd562380d448f4904c76f'
p = angr.Project(path, load_options={'auto_load_libs':False})
state = p.factory.entry_state()
# construct the argument
argv = [p.filename]
size = 9 # password size
pwd = claripy.BVS('sym_arg', 8*size)
argv.append(pwd)
state = p.factory.entry_state(args=argv)
sm = p.factory.simulation_manager(state)
sm.explore(find = 0x0040091a, avoid = 0x00400921)
print sm
if len(sm.found) > 0:
# we have a valid path to success
found = sm.found[0]
result = found.solver.eval(argv[1], cast_to=str)
print result