CTF Writeup — You Shall Not Pass

BSides Canberra 2019 — Reverse Engineering

Tom
TSS - Trusted Security Services
6 min readMar 19, 2019

--

If you’d like to try the problem before reading the solution, the CTF challenges can be found here: https://gitlab.com/cybears/fall-of-cybeartron.

Today, I’m going to walk through my approach to solving the “You Shall Not Pass” reverse engineering challenge from BSides Canberra 2019. At first glance, I dismissed this challenge as “something to worry about later; Android isn’t a strength of mine and the initial apk file hinted that I’d most likely descend into seven hells of browser tabs before giving up and deciding to move on.

Instead, this challenge proved to be solvable, and it demonstrated an important lesson — always try.

Even if you think you don’t have a chance, try.

Yoda might bid us to “do or do not” but you’ll never know if you’re capable of doing something without trying. Anyway, I digress.

The challenge consists of a single apk: PasswordChecker.apk. You might like to run the app up in an emulator like Genymotion, in which case you’ll see the prompt shown below.

This is literally all the app does.

You might approach this by attempting to debug the live app but my Android debugging knowledge is pretty rudimentary so I just unzipped the apk to look at the contents.

Unzipped contents of PasswordChecker.apk

Inside, we have some standard Android-ish looking stuff. A few canonical metadata files (AndroidManifest.xml, resources.arsc) and compiled Java classes in classes.dex. Checking the lib folder, we find a number of shared objects.

Some sort of native library shared objects, they arranged by architecture.

Kind of interesting, but let’s start with the actual Android app. The “dex” in “Classes.dex” stands for “Dalvik Executable Format”, an Android native filetype. Rather than analyse dex directly, we can convert it to a Java JAR using dex2jar.

Running dex2jar to convert a Dalvik format executable into a Jar.

Browsing the codebase then becomes a simple matter of loading the JAR into a tool such as JD-GUI or decompiling it with Jad. I opted for the JD-GUI route and quickly discovered the function checkPassword in MainActivity.class. The rest of the application didn’t seem too interesting.

Decompiled Java application code, viewed here in JD-GUI.

Note that the code above is retrieving a string from the application GUI and passing the string to checkPassword3. This function doesn’t seem to be defined in the decompiled Java code, but note that the method is defined as native. This means that checkPassword3 is going to be run outside JVM (or … DVM??) Remember those shared objects in the “lib” folder? It seems like a good bet that those are going to be relevant to the “native” component of the application (I mean, they’re arranged by architecture, it seemed like a good bet.)

Now, because I’m writing this on March 18th, 2019 and Ghidra is literally “so hot rite now”, I’m going to analyse these libraries in Ghidra.

In a parallel universe I imagine I might have solved this challenge using Radare2. However in this universe, I can only offer the following comment:

RE in 2006 be like 👆

If you’ve never been on Twitter or if you’ve never worked for a FVEY signals intelligence agency you might not be familiar with Ghidra. It looks like this 👇

Ghidra GUI with the x86 version of the PasswordChecker native library loaded.

It’s an NSA developed reverse engineering suite with a functional API, rich architecture support, a pretty sweet decompiler and a GUI that’s s̶t̶r̶a̶i̶g̶h̶t̶ ̶o̶u̶t̶t̶a̶ ̶1̶9̶9̶3̶ a touch dated. The decompiler is perhaps slightly less functional than HexRays but is vastly more free.

I won’t go into the specifics of loading binaries in Ghidra but the process looks something like:

  • Open the software (requires Java 11 dependencies).
  • Create a new project.
  • Import your target binary.
  • Click through analysis prompts.
  • Profit.

With the x86 version of libnative-lib.so loaded, we can search for our target function, checkPassword3, under the list of functions in symbol tree 👇. The symbol tree is visible on the left hand side of the GUI, under the program tree.

Searching for a function name in the Ghidra symbols pane, much like you might search for a function name in the “functions” pane of IDA Pro.

In the decompilation window (right hand side of the Ghidra GUI) we see some pretty funky looking code (shown below). If we read this code carefully it looks like a string (note the char* at line 34) is having a series of constraints checked. If the constraints are satisfied, we end up returning one value (uVar6 set at line75). Otherwise we return some other value of uVar6. Seems vague? It is, but intuition suggests that one of these values represents a success case and one represents a failure case. Although rather than a boolean or integer value, the check password function is returning a string that is appended to “RESULT: ” with localStringBuilder. This suggests that the funky return values being assigned to uVal6 are pointers to strings; running a random password through the app will print “RESULT: FALSE” to the screen. The strings “FALSE” and “TRUE” also appear in the library shared objects so this seems like a valid assumption.

Some gnarly looking code — the meat and potatoes of the checkPassword3 function.

Discovering a password that satisfies these constrains and which makes the app print “RESULT: TRUE” could be approached in a couple of ways. Using a symbolic execution framework like Angr would be an excellent choice, although I’m not sure how it would play with the Java native interface calls. Instead of symbolic execution, I opted to model the constraints as a satisfiability problem using the Z3 theorem prover.

My Z3 solver is pretty basic:

  • Each byte in the input string is treated as an integer.
  • Numerical constraints are applied to each input byte using a solver object.
  • The solver object checks if the constraints can be satisfied (using math(s)).
  • If the constraints can be satisfied, print the result.

The final solution is shown below. This contains a bit of a hack to print the flag nicely. It’s not perfect but it works. On the initial run, my solver didn’t return a result in a reasonable time. Given that I was trying to solve this flag with 40 minutes of a 24 hour CTF remaining, I wasn’t about to make a cup of tea and wait for z3 to ponder its navel. Fortunately, adding the constraint that the flag ought to begin with “cybears” was sufficient and the solver returned a result right away.

One z3 solver == One Pray 🙏

Et Voila — we get the flag 🎉 🤙

Probably got points for this, idk.

The moral of this story is — try something before you decide you can’t do it.

Don’t let yourself feel too … constrained … ( ͡° ͜ʖ ͡°)

T.

Tom is a principal penetration tester at TSS specialising in red teaming.

TSS is a specialist cyber security company providing penetration testing, security assurance consulting and managed security services. More information is available at our website https://www.tsscyber.com.au.

--

--