UniWA 2022 – SeekNDestroy

Description: James Hetfield has applied a position in Squid Game 2022, but in order to take part into the game, he was asked to bypass the login screen of this app. Help him do this and he might find you a free ticket for the concert.

UniWA 2022 SeekNDestroy

Install the APK with ADB

				
					adb install -r Seek_N_Destroy.apk
				
			

We can see a login with username and password.
Let’s inspect the source code with jadx.

We have one activity, which is MainActivity.
And the java code is:

				
					public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;

    public native String stringFromJNI();

    static {
        System.loadLibrary("seek_n_destroy");
    }

    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
        this.binding = inflate;
        setContentView(inflate.getRoot());
        final EditText editText = (EditText) findViewById(C0511R.id.uname);
        final EditText editText2 = (EditText) findViewById(C0511R.id.pass);
        final TextView textView = (TextView) findViewById(C0511R.id.show);
        ((Button) findViewById(C0511R.id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.example.seek_n_destroy.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                if (editText.getText().toString().equals("mitroglou") && MainActivity.md5(editText2.getText().toString()).equals("15eca8868ab1ae1828fff6bb7cf4b")) {
                    textView.setText(MainActivity.this.stringFromJNI());
                } else {
                    Toast.makeText(MainActivity.this, "Wrong username or password!", 1).show();
                }
            }
        });
    }

    public static String md5(String str) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(str.getBytes());
            byte[] digest = messageDigest.digest();
            StringBuffer stringBuffer = new StringBuffer();
            for (byte b : digest) {
                stringBuffer.append(Integer.toHexString(b & 255));
            }
            return stringBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }
    }
}
				
			
  • The code load a native lib with the name: libseek_n_destroy.so
  • The Button “LOGIN” have the following logic:
				
					((Button) findViewById(C0511R.id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.example.seek_n_destroy.MainActivity.1
    @Override // android.view.View.OnClickListener
    public void onClick(View view) {
        if (editText.getText().toString().equals("mitroglou") && MainActivity.md5(editText2.getText().toString()).equals("15eca8868ab1ae1828fff6bb7cf4b")) {
            textView.setText(MainActivity.this.stringFromJNI());
        } else {
            Toast.makeText(MainActivity.this, "Wrong username or password!", 1).show();
        }
    }
});
				
			

Check if username = mitroglou and the password is the result of MD5 hash15eca8868ab1ae1828fff6bb7cf4b.
But this MD5 is malformed. Due that real MD5 has 32 chars, and this one have 28.
So we can’t crack it.

We can follow two paths in order for get the flag.
Call to stringFromJNI hooking with frida, or patch the APK changing the MD5 hash by another one that we can create.

We can do both.
In order to call stringFromJNI using frida, we can do it with this JavaScript code:

				
					Java.perform(() => {
    const MainActivity = Java.use('com.example.seek_n_destroy.MainActivity');

    // onclick hook (anonimate class$1)
    const LoginButtonClick = Java.use('com.example.seek_n_destroy.MainActivity$1');

    LoginButtonClick.onClick.implementation = function(view) {
        // execute
        this.onClick(view);

        // get instancia from activity
        const activity = this.this$0.value;

        // execute native method
        const flag = activity.stringFromJNI();
        console.log("[+] FLAG: " + flag);
    };
});
				
			

Run the app, start frida server on device and the in the terminal:

				
					frida -U "Seek_N_Destroy" -l hookLogin.js
				
			

We got the flag!
[Redmi Note 8::Seek_N_Destroy ]-> [+] FLAG desde JNI: UNIWA{!Se@rch_S33k_n_D3str0y!}

Flag: UNIWA{!Se@rch_S33k_n_D3str0y!}

Now let’s make it persistent changing the MD5 value for another.
Decompile the APK with apktool

				
					apktool d Seek_N_Destroy.apk
				
			

Inside of Seek_N_Destroy/smali/com/example/seek_n_destroy directory, we have the file with the hardcoded MD5 value.
In MainActivity$1.smali search for MD5 text.
const-string v0, "15eca8868ab1ae1828fff6bb7cf4b"

I create with CyberChef the following MD5:
hola -> 4d186321c1a7f0f354b297e8914ab240
Change the value and save the .smali file.

Let’s build the new APK!

				
					keytool -genkey -v -keystore name.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias alias
				
			

Now, zipalign tool in action:

				
					zipalign -v -p 4 Seek_N_Destroy.apk Seek_N_Destroy-aligned.apk
				
			

And now, sign the APK

				
					apksigner sign --ks name.keystore --ks-key-alias alias --ks-pass pass:lautaro --key-pass pass:lautaro --out Seek_N_Destroy-signed.apk Seek_N_Destroy-aligned.apk
				
			

Uninstall the original APK and install the signed version:

				
					adb install -r Seek_N_Destroy-signed.apk
				
			

Now login with the creds: mitroglou:hola and the flag will appear!

I hope you found it useful (:

Leave a Reply

Your email address will not be published. Required fields are marked *