Secure Safe is a 100 point Mobile challenge in the NahamCon CTF. The challenge provides the
secure_safe.apk
file and the description This app says it secures my stuff! It's so cool!
The challenge is actually somewhat complicated, as the flag cannot be obtained by simply decompiling the apk. Its actually an ‘encrypted’ string resource
This probably means that the encryption method will have to be reversed, but there is a twist. Running the APK in an emulator shows that a 4 digit pin code forms part of the process
The max length of the pin is only 4 digits at least, so there are only 10’000 possible combinations which can be brute forced in a few seconds.
Going through the bytecode of the
MainActivity
class shows that the encrypted flag, stored in p1
are passed along with the 4 digit pin stored in v0
to the a
method in MainActivity$
The
onClick
method in turn calls the a
method from the a
class, passing along the flag and pin code againThe
a
method is actually a straight forward xor operationSo my assumption is that the pin forms a part of the key somehow. There is a method below
a
called doInBackground
which is basically an android method that will complete a task in the background thread once its been invoked (TL;DR Async method for computation). This is the method where the hash computation and decryption actually takes place.JEB provides an excellent decompiler, which translates the two methods into the following
public final byte[] a(byte[] arg5, byte[] arg6) { byte[] v0 = new byte[arg5.length]; int v1; for(v1 = 0; v1 < arg5.length; ++v1) { v0[v1] = (byte)(arg5[v1] ^ arg6[v1 % arg6.length]); } return v0; } public Object doInBackground(Object[] arg7) { String[][] v7 = (String[][])arg7; String v2 = v7[0][0]; String v7_1 = v7[0][1]; try { MessageDigest v4 = MessageDigest.getInstance("SHA-1"); v4.update("5up3r_53cur3_53cr37".getBytes("UTF-8")); v4.update(v7_1.getBytes("UTF-8")); String v7_3 = new BigInteger(1, v4.digest()).toString(16); return new String(this.a(Base64.decode(v2, 0), v7_3.getBytes())); } catch(NoSuchAlgorithmException | UnsupportedEncodingException v7_2) { v7_2.printStackTrace(); return "Error decrypting"; } }
From that its really straight forward to put together a quick java app that will loop 1 to 10000 and brute force the encrypted flag. See comments in the code below for explanation.
import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class C3 { // XoR method public final static byte[] a(byte[] arg5, byte[] arg6) throws Exception { byte[] v0 = new byte[arg5.length]; int v1; for(v1 = 0; v1 < arg5.length; ++v1) { v0[v1] = (byte)(arg5[v1] ^ arg6[v1 % arg6.length]); } return v0; } public static void main(String args[]) throws Exception { // byte array of the encrypted flag byte[] v2 = "UFhYVUt2VmdqEFALbiNXRkZvVQtTQxwSTVABe0U=".getBytes(); // Loop from 1000 to 10000 as pin has to be 4 digits long for (int i = 1000; i <= 10000; i++) { // Print the pin code (i) String pin = Integer.toString(i); System.out.println(pin); // Initialize the method digest MessageDigest v4 = MessageDigest.getInstance("SHA-1"); // Updates the digest using the specified array of bytes v4.update("5up3r_53cur3_53cr37".getBytes("UTF-8")); // Further updates the digest using the pin code v4.update(pin.getBytes("UTF-8")); // Create the key String v7_3 = new BigInteger(1, v4.digest()).toString(16); // Decrypt using the a method and print the result System.out.println(new String(a(Base64.getDecoder().decode(v2), v7_3.getBytes()))); } } }
There will only be a single correct flag, so its possible to just grep flag
the stdout.
javac C3.java && java C3 | grep flag