Whoami

Source

https://cybertalents.com/challenges/mobile-security/whoami

whoami

This challenge has 2 distinct solutions actually,

  • the first one is by using Frida to bypass root detection, then swapping the two activities

  • the second is by extracting the JNI libraries and using them in our own app (this solution is kinda sneaky and I don't think this is how the challenge was intended to be solved)

So let's begin.

Gathering information

By using Jadx to reverse-engineer the APK after downloading it, we get its source code.

Reading AndroidManifest.xml

<activity android:name="com.cybertalents.ohmyroot.FLAGbox"/>  
<activity android:name="com.cybertalents.ohmyroot.MainActivity">  
	<intent-filter>  
		<action android:name="android.intent.action.MAIN"/>  
		<category android:name="android.intent.category.LAUNCHER"/>  
	</intent-filter>  
</activity>

It appears that we only have 2 activities with only one activity being exported as the MAIN activity, and the second one isn't exported at all, meaning that we cannot reach it from outside normally.

Reading the MainActivity, we are introduced to this code.

super.onCreate(bundle);  
	setContentView(R.layout.activity_main);  
	TextView textView = (TextView) findViewById(R.id.sample_text);  
	if (Boolean.valueOf(new IsRooted().IsDeviceRooted()).booleanValue()) {  
		textView.setText("Try Harder");  
	} else {  
		textView.setText(stringFromJNI());  
	}  
}

Reading the code we conclude that the activity is calling some class named IsRooted() and calling a method IsDeviceRooted(). By their names, it looks like a root-detection mechanism.

Reading the class content and focusing on the IsDeviceRooted() method, we notice that it returns a boolean

public class IsRooted {  
    // --- snip --- //
    public boolean IsDeviceRooted() {  
        return detectTestKeys() || checkForSuBinary() || checkSuExists() || checkForBusyBoxBinary();  
    }  
	// --- snip --- //  
}

This looks like an easy mechanism to bypass, so the first thing we do is fire up our trustworthy rooted emulator, then inject a frida agent inside our target application using objection patchapk -s whoami.apk, install it via adb, open the application, then start the frida client using frida -U oHmyRoot.

In my emulator the root detection didn't work properly, so the application thought my device wasn't rooted and blessed me with the result of the else branch. But worry not, my friend — I'll write the root detection bypass using frida in case the root detection is working in your case.

Java.perform(() => {
    let isRooted = Java.use("com.cybertalents.ohmyroot.IsRooted");
    isRooted.IsDeviceRooted.implementation = function() {
        return false;
    }
})

We load this code using frida -U oHmyRoot -l <filename.js>. In this code we import the IsRooted class and re-implement the IsDeviceRooted function to always return false so our device isn't considered rooted.

But as we know from the code review, the flag isn't here. So let's review the second activity FLAGbox; obviously from its name it is the one containing our flag XD.

private native String DoSomeMagic();  
  
    static {  
        System.loadLibrary("native-lib");  
    }  
  
    @Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity  
    protected void onCreate(Bundle bundle) {  
        super.onCreate(bundle);  
        setContentView(R.layout.activity_flagbox);  
        TextView textView = (TextView) findViewById(R.id.textView);  
        if (!Boolean.valueOf(new IsRooted().IsDeviceRooted()).booleanValue()) {  
            textView.setText(DoSomeMagic());  
        } else {  
            textView.setText("Sorry You Are you should get off your Rooted Device");  
        }  
    }

Viewing it we deduce the same thing: it checks if our device is rooted or not, then executes DoSomeMagic(), so we need to find some way to start this activity.

Using the next frida script,

Java.perform(() => {
    const MainActivity = Java.use("com.cybertalents.ohmyroot.MainActivity");
    const FlagActivity = Java.use("com.cybertalents.ohmyroot.FLAGbox")
    MainActivity.onResume.implementation = function () {
        console.log("onresume");
        let Intent = Java.use("android.content.Intent");
        var myIntent = Intent.$new(this.getApplicationContext(), FlagActivity.class);
        this.startActivity(myIntent);
    }
})

This code would start the flag activity when the main activity resumes. So we hook this code using frida after starting our application. You might notice that nothing happens immediately, but after going to the home screen and then entering the app again, the flag will appear.

Last updated