Welcome back on exploitnetworking! After some months I’m returned! This week I’ve partecipate to Houseplant CTF.
This blogpost is related to a nice Android reverse engineering challenge: RTCP TRIVIA.
The challenge description was: “We now have our very own trivia app! Solve 1000 questions and win a flag!”. Ok, try to do it 😉.
Decompile the apk
The first step, after download the client.apk, I was try to decompile it. I’ve used the utility apkx, that you can find in his official repository, that permit you to obtain the java code of the apk.
The code of the application it’s saved on client directory. Inspecting the code, it was possible to see some relevant classes inside client/wtf/riceteacatpanda/quiz directory:
In MainActivity.java class, there were some interesting lines that suggest such communication with a server using the websockets, like this line:
The next step, then, was run the application in an emulator or an Android device, setting up a proxy for check eventuals websocket communications.
Running and proxy
The main activity was a form login with an input text for the username and a login button:
I have tried to insert an username and press the login button and a websocket request was sent to a server, how I’ve seen in my BurpSuite proxy:
After the login there was the LoggedIn activity with only a play button:
Pressing the button, a websocket request happened with a reply by the server:
In the response, there was the first question that I had to answer correctly, but it was all encrypted, then I had to reverse the code of the apk for see how the answer was encrypted and decrypted.
Get the encryption keys
Looking at the code of the Game activity, I’ve seen that some lines are used to create a new cipher object:
The line with the following code: cipher = Cipher.getInstance((String)”AES/CBC/PKCS7Padding”); suggested an AES cipher, then the next step was search what key and iv was used for the encryption.
The iv was initialized with the following code: object2 = new IvParameterSpec((byte)cipher); where the cipher variable was created with cipher = nx.b(jSONObject.getString(“requestIdentifier”));, then I had to going into the nx class to see what b function does. This class was in the client/nx.java file, and the implementation of b function was:
Reverse this function was complex, then I’ve created a frida script for hooking at runtime the b function and see his input and output😆:
Then I’ve started the frida server on my Android device:
and run my frida script with the following command:
The results was printed after that the play button was pressed. The value 435db0475dae503ff9f8f2e829bc1eff was the input of the b function and [67,93,-80,71,93,-82,80,63,-7,-8,-14,-24,41,-68,30,-1] was the output; this array was simple a byte array of the md5, and the hash was the value requestIdentifier of the websocket response how it was possible see on BurpSuite:
After obtaining the iv of the AES cipher, I’ve tried to get the key. Returning in the Game activity, I’ve looked to this line:
The second parameter was an object Key, that was the passkey of the cipher, and it was initialized in the following line in the same class:
Where object was created by the following snippet of code:
Essentially, it was the output of a function of the nx class and his input was a string construction. Below there is the code of a function:
It seemed to create a sha256 hash and return it’s bytes. For confirm this, I’ve added the following code to my frida script to print the input and the output:
Running the script I’ve obtained the following result:
where in the input of a function there was the following string:
that was a contatenation of two string: a sha256 hash and an id value. The id value was obtained from the response, in the websocket, exactly as requestIdentifier for the iv:
But the sha256 hash? It seemed to be computed by nx class from another implementation of a function, below reported:
After some investigation, I’ve found that the value returned by this method was computed from the hash that the app initially communicates to the server (inside the userToken value provided in the first websocket request), in this way both, client and server, can compute the hash used for the encryption key.
- The key was a sha256 computed by a concatenation of two string: a sha256 value computed using userToken providing during the login, and the id provided by the server.
- The iv was a value returned by the server in requestIdentifier.
After obtaining the keys used for decrypt the response, I was able to create a script to respond correctly at 1000 question😆.
Finally, run it and:
If you’re interest to Android mobile pentest, I suggest you these nice books: