Android App Pentesting — Hardcoded Encryption Key & No Rate Limiting Lead to Account Takeover
So while pentesting an Android application, I stumbled upon an unlimited login attempt trial — which was only consist of a phone number and a six digit PIN number..
At first I thought that it was a really “juicy” target to bruteforce, but that thought immediately disappeared once I see the request.
The request was a JSON object consists of fcmToken, phoneNumber, and a PIN number which somehow looks completely different from what I sent from the login screen.
My first guess was that the PIN parameter is encoded in Base64 (notice the “==” at the end), but as I decoding it into plain text, it turns out to be gibberish bytes-like text.
>>> import base64
>>> base64.b64decode("JTuPDh122ItijR3aw989lg==")
b'%;\x8f\x0e\x1dv\xd8\x8bb\x8d\x1d\xda\xc3\xdf=\x96'
My next step was to guess how the PIN parameter is getting encrypted. Since the app wasn’t obfuscated by either Proguard/Dexguard and I was able to decompile and gain a pretty clear view of the source code, I begin by inspecting the LoginActivity.java— which of course responsible for handling the login activity.
final String str2 = "0" + this.phoneNumber.getText( ).toString();
final String obj = this.pin.getText().toString();
LoginRequest loginRequest = new LoginRequest(str2, AES.AES256Encrypt(obj), str);
It turns out that the PIN number is passed into AES256Encrypt method, and — you guess it from the title — the key is hardcoded into the method itself.
AES256Encrypt.java
public static String AES256Encrypt(String str) {
String str2;
try {
str2 = Base64.encodeToString(encrypt("<REDACTED>".getBytes("UTF-8"), "<REDACTED>".getBytes("UTF-8"), str.getBytes("UTF-8")), 0);
} catch (Exception e) {
PrintStream printStream = System.err;
printStream.println("Encrypted Err: " + e.getMessage());
str2 = e.getMessage();
}
return str2.replaceAll("\\n", "");
}
As you see, there are two <REDACTED> parameter in the code above, the first is Initialization Vector, and the other is Encryption/Secret Key itself.
Referring to the code above, I was able to make a script that encrypts the number in range of 100000–999999 to be then used as wordlist for bruteforcing.
Lastly, by using Burpsuite’s Turbo Intruder and the wordlist generated from the script, I was able to “forcefully” gain access to my own account in relatively short amount of time.
For example, if the key is implemented over-the-air, you can obtain it by simply hooking the method using Frida, or if its hardcoded in the app, you can just refer to this post to decrypt it.
See ya 😀