The ASIS 2018 Finals had some awesome challenges. One of them is the Android reversing and web challenge Gunshop. I enjoyed the mix between Android reversing and web vulnerabilities. Also, it was a lot of fun to use ARTist, an open-source Andorid instrumentation framework, for the first time in a CTF Challenge. The instrumentation performed by ARTist allowed us to leak the AES key, intercept https traffic and bypass custom certificate pinning.
Gunshop 1
Assets
The APK contained two suspicious files in the assets folder, configKey and configURL. Both contained some encrypted data as base64 encoded string. By analyzing the APK in the jadx-gui decompiler, we see that the assets are encrpted using AES/ECB/PKCS5Padding and the key is derived using a SHA-256 hash:
Obtaining the Encryption Key
At this point, I decided to not compute the AES key statically, but to instrument the application dynamically. For this purpose I used the ARTist, the Android RunTime Instrumentation and Security Toolkit. ARTist modules alter the application logic by modifying the App’s intermediate representation before the native code is generated during the compilation on the Android device. For more detailed information about ARTist, check out the project website or the BlackHat slides. The Gitter chat is the right place to ask questions.
The full source code of the ARTist module written for this challenge is available on Github.
Back to the challenge: To obtain the key dynamically, we implemented an ARTist module which extends the method public static String a(Context context) in a way that it will also print the generated key. First, we implemented a logging method, which is part of the CodeLib, an ARTist module’s apk containing additional helper methods:
To add this method into the application, we have to create an Instrumentation Pass defining how the method should be modified. The relevant part is the following:
Setting-up https Traffic Interception with ARTist
In another part of the application’s code, an https connection is used:
First, we want to replace the call to URL.openConnection() with a CodeLib method that opens a new connection too, but without certificate checks and with a proxy set up as man-in-the-middle:
To replace the call to URL.openConnection with an own implementation, the instrumentation pass has to be extended again:
Instrumenting the application with the current implementation of the module would leak the key, but would not allow intercepting the traffic. For each request, the Exception SSL Pin Error would be thrown because the App implements a custom certificate pinning:
The easiest way is to replace all possible return values with true. This requires no additional CodeLib methods and only a few lines of C++ for the instrumentation pass:
Now, the ARTist module is finally completed and we can transfer it to the Android device and let ARTist GUI recompile the Gunshop App with the modifications. Opening Burp, we can now intercept and modify the App’s https traffic. Also, the App greets us in the logcat with the encryption key for the assets:
ASIS-codelib-log: E4555D32B56E3D90
Decrypting the assets
Now, that the constant encryption key is known, we can build a python script to decrypt the assets:
Both, the URL and the key, are later important for the traffic interception.
Login as admin
After starting the app, there is only a login screen visible. As there are no credentials provided and there were no credentials in the application’s code, we try logging in with a random user name and password. After a failed login attempt, a toast message pops up: username not found in public/users_gunshop_admins.csv. It seems there is a csv file on the remote server, which contains the credentials of all admin accounts. Luckily, there is following method in the Application’s code which gives a hint on an remote endpoint that could be used to obtain files:
As expected, we can use the decrypted url and the path from the error message to retrieve a csv file with admin credentials:
Since the ARTist instrumentation allows to intercept the traffic, we intercepted the traffic during the login with the admin credentials. Although we can see the plain https traffic, we don’t see our credentials in the request yet:
It seems that the user_data is encrypted itself too. Since there is a configKey in the assets folder, we tried decrypting the user_data with this configKey:
The decryption seemed to work. Decrypting the response body with the same key, reveals a json object:
Besides an interesting new key and some shop data, it also gives us the first flag ASIS{d0Nt_KI11_M3_G4NgsteR}!
Gunshop 2
There we are, logged in as admin alfredo, seeing a list of weapons in the Gunshop. Let’s select the Tiny Killer. After confirming the choice, a request is sent to the server. The request contains, once again, an encrypted parameter, and the repsonse an encrypted body. Decrypting with the new key from the login-response reveals:
As the challenge description says Login to the City Center Shop. A weapon is there, waiting for a worthy warrior to take it!, we seem to be close to the goal. Sadly, accessing the URL via a GET request gives us a Method not allowed and an empty response via POST. It seems we are not yet authorized to access this url. But in the App, a submit button is shown on the current order page. After hitting the button, a new encrypted request is sent to the server:
Sending an url to the server looks like a possible SSRF vulnerablility. To check for SSRF, we logged in and hit the submit button again, but replaced the user_data with the encryption of a postb.in url:
The server returns the same string, but we can observe a request to our postb.in:
By decoding the authorization header, we get: bigbrother:4Qj3rc4ZhNQKv7Rz.
Let’s use these credentials to post to the shop url:
This gives us the second flag for Gunshop: ASIS{0Ld_B16_br0Th3r_H4d_a_F4rm}