I didn’t end up attempting this challenge during the competition, but
afterwards decided to give it a look. It turns out to be a pretty fun android
reversing challenge that revenge is helpful in solving.
The solution involved reversing the given apk, identifying the first input
checks, brute-forcing the correct input, identifying the first decryption
routine, manually decrypting to find a second stage dex file, and finally
reversing that stage to identify how the final flag needed to be decrypted.
Triage
The given file is an APK (android application file). To start with, spin up an
emulator and connect to it via revenge:
When we try entering “password” as the password, the app crashes. Clearly
something is amiss.
Main App Code
Time to dig into the code a little. The basics for looking at the code involve:
Looking at the MainActivity, we can find the handler for our password
validation:
So we should be expecting to see “Yes!” or “No…”. It’s first calling
check_password, so we should check there next.
So we can see that it’s breaking up our input into two character chunks,
converting them to an integer, then calling a function with the input and
checking if the output is the expected. If the total number of items that match
is 32, then we’re good.
Those c1c2 etc params are actually called from the shared library that
comes with this application. You can see it here:
Luckily for us, we don’t actually have to reverse those. Instead, so long as
they are deterministic in what they return, we can simply call them over and
over until we find the correct output.
Brute Force Stage 1
To test the ability to call those functions with revenge, you can
simply do:
What we just did there was to actually call the function directly with our
input. With this in mind, we can now brute force stage 1 to find the expected
input.
While we know that should be correct, when we input it, the program still
crashes… Need to take a look at the next section.
Code Analysis 2
According to the decompilation, if we pass step one we should hit the call
DynamicLoaderService.startActionLoad. To verify this, let’s quickly hook that
method so that revenge tells us if we hit it or not.
So we can confirm this is getting hit. Here’s some code for the function:
Following it down to handleActionFoo:
So our program is crashing because there are two methods that are not
implemented yet and are throwing exceptions.
Decrypting Payload 1
The application is grabbing some encrypted payload from a local resource. We
could use something like AndroidStudio to pull the encrypted
blob out directly. However, let’s use revenge to pull it out
dynamically instead.
To do this, all we need to do is implement the XORDecrypt method that gets
called with the array.
The blob is the first argument and the key (which we gave it) is the second.
Since this is apparently a basic xor chipher, we can just decrypt
it directly.
Our blob2 appears to be a dex file. The call after XORing this blob is to call
decrypt inside it. Time to open the new dex file up, same as the first ones.
Code Analysis 3
Opening up the new dex file in jdgui, we can find the decrypt method
being referenced:
Decrypting Payload 2
Now it’s simply a matter of re-writing the java decryption method in python: