The timing for this challenge was just about perfect for me. A new colleague of mine just last week did a presentation on smali basics and debugging Android Applications using IDA. Before that I had only done some of the most basic patches in smali and no real dynamic stuff other than with
am or purpose-built apps.
My process for an APK is pretty simple: Unzip,
apktool -d. This yields a .jar file you can look at with JD-GUI and the smali code if you need to make changes. It also converts the AndroidManifest.xml back into a readable format.
Peeking at the manifest, we see that it defines two permissions, ctf.permission._MSG, which has the level ‘signature’, and ctf.permission._SEND. There are also a few activities defined: com.example.application.IsThisTheRealOne, …ThisIsTheRealOne, and …DefinitelyNotThisOne. Finally, it defines a receiver com.example.application.Send_to_Activity.
Popping the jar into JD-GUI, we first peek at the MainActivity. Super simple: Nothing on the UI but some text, and the Send_to_Activity receiver is registered, filtering on com.ctf.INCOMING_INTENT, and requiring the first custom ‘MSG’ permission.
onReceive message we see that it checks the received intent for a string extra named “msg”, and uses it to select which activity to send an intent to.
It’s in the three activities that are sent to that where things get interesting. All three look about the same, with small variations: Get some string, munge it up (using some native functions
definitelyNotThis from libhello-jni.so), and then send a broadcast intent with the result. Because it doesn’t take any input from us, we can assume that one of these must generate the flag, and it’s only a matter of ‘catching’ the output.
Let’s play around to make sure we have the correct understanding of what’s going on. Installing the apk on my phone, I opened each of the activities using
Although it took longer than I’d like to admit to get the syntax correct in
am, once I had the above commands, I could switch between the activites with ease. The last three activities render on screen with nothing but a big button. Tappind the button executes the code that computes and broadcasts the flag. Note that we never had to use Send_to_Activity. We could have used it just the same:
Now that we know how to control the app and have a pretty good idea of how the flag is generated, there are a few ways to approach this:
- We can write an app that has a receiver that filters on com.ctf.OUTGOING_INTENT and logs what it receives.
- We can debug the app and just set a breakpoint on each call to
Because I don’t feel like writing java (I never do), I’ll go with the latter. The former requires signing the new app with the same key (because of the ‘signature’ level on the permission), but we could get around that by simply re-signing the original app with our own key. The latter requires we change the AndroidManifest.xml to set
debuggable to true. This also requires re-signing the app (so we can reinstall it) but that’s trivial.
- Edit AndroidManifest.xml:
<application android:icon="@mipmap/ic_launcher" android:label="CTF Application" android:debuggable="true">
- Build a new apk:
- Create some bogus key:
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
- Sign the apk:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ./my-release-key.keystore illintentions.apk alias_name
- Uninstall the old app (from the UI).
- Install the new one:
adb install illintentions.apk
Now I load up the APK in IDA, navigate to the
onClick methods in each activity, set a breakpoint, follow the instructions here, and start the application in the debugger.
Once IDA has connected to the application (just skipping past the auto-set bps), and the app is running, I use
am again to send one of the intents, then tap on the button that shows up. IDA should break immediately. Now at this point we see that the output of
perhapsThis, which is fed directly to
putExtra is stored in
v6. Opening the ‘Watch’ view (Debugger -> Debugger windows -> Watch view), I simply created a new watch on
v6 that casts it to
(Yes, I got lucky and chose the correct activity on my first try).