BSides San Francisco CTF FlagReceiver writeup

Flag Receiver – 200
Here is a simple mobile application that will hand you the flag.. if you ask for it the right way.

*P.S, it is meant to have a blank landing activity  Use string starting with Flag:

flagstore.apk

First of all, I decompiled apk code with apktool and enjarify:

➜  apktool d --no-src flagstore.apk
➜  enjarify flagstore/classes.dex -o classes.jar

Then I browsed application code using JD-GUI and found that

  1. It registers BroadcastReceiver called Send_to_Activity for com.flagstore.ctf.INCOMING_INTENT action:
protected void onCreate(Bundle paramBundle)
{
    super.onCreate(paramBundle);
    TextView localTextView = new android/widget/TextView;
    Context localContext = getApplicationContext();
    localTextView.<init>(localContext);
    localTextView.setText("To-do: UI pending");
    setContentView(localTextView);
    IntentFilter localIntentFilter = new android/content/IntentFilter;
    localIntentFilter.<init>();
    localIntentFilter.addAction("com.flagstore.ctf.INCOMING_INTENT");
    Send_to_Activity localSend_to_Activity = new com/flagstore/ctf/flagstore/Send_to_Activity;
    localSend_to_Activity.<init>();
    registerReceiver(localSend_to_Activity, localIntentFilter, "ctf.permissions._MSG", null);
}
  1. After receiving com.flagstore.ctf.INCOMING_INTENT it checks that msg parameter of this intent is equal to OpenSesame and if it is true the application starts new activity called CTFReceiver:
public void onReceive(Context paramContext, Intent paramIntent)
{
    String str1 = paramIntent.getStringExtra("msg");
    Intent localIntent = null;
    Object localObject = "OpenSesame";
    int i = str1.equalsIgnoreCase((String)localObject);
    if (i != 0)
    {
      String str2 = "Intent";
      Log.d("Here", str2);
      localIntent = new android/content/Intent;
      localObject = CTFReceiver.class;
      localIntent.<init>(paramContext, (Class)localObject);
      paramContext.startActivity(localIntent);
    }
    for (;;)
    {
      return;
      String str3 = "Ah, ah, ah, you didn't say the magic word!";
      i = 1;
      localObject = Toast.makeText(paramContext, str3, i);
      ((Toast)localObject).show();
    }
}
  1. Then in CTFReceiver activity in OnClick event handler it does weird things with some strings using the code from native library (libnative-lib.so) and broadcasts the result as msg parameter of com.flagstore.ctf.OUTGOING_INTENT:
public void onClick(View paramView)
{
    Intent localIntent = new android/content/Intent;
    localIntent.<init>();
    localIntent.setAction("com.flagstore.ctf.OUTGOING_INTENT");
    StringBuilder localStringBuilder = new java/lang/StringBuilder;
    localStringBuilder.<init>();
    String str1 = this.this$0.getResources().getString(2131099686);
    String str2 = str1 + "fpcMpwfFurWGlWu`uDlUge";
    String str3 = Utilities.doBoth(this.this$0.getResources().getString(2131099683));
    String str4 = getClass().getName().split("\\.")[4];
    int i = str4.length() + -2;
    String str5 = Utilities.doBoth(str4.substring(0, i));
    String str6 = this.this$0.getPhrase(str2, str3, str5);
    localIntent.putExtra("msg", str6);
    this.this$0.sendBroadcast(localIntent);
}

So, to get flag we should:

  1. Send com.flagstore.ctf.INCOMING_INTENT with parameter msg="OpenSesame" to the app.
  2. Click the button in CTFReciever activity
  3. Get msg parameter of com.flagstore.ctf.INCOMING_INTENT

I decieded to try something new, i.e. not to use debugger and solve this task by using android emulator, adb and tool called drozer. Let's start!

First of all, run an android emulator and install flagstore.apk to it:

➜  emulator -avd 24_x86_64
➜  adb install flagstore.apk

Then, install drozer client and its dependencites to your computer:

➜  wget https://github.com/mwrlabs/drozer/releases/download/2.4.2/drozer-2.4.2-py2.7.egg
➜  easy_install drozer-2.4.2-py2.7.egg
➜  pip install twisted

After that, generate drozer agent apk and install it to the emulator:

➜  drozer agent build
...
Done: /var/folders/s4/hf3pw66928v6qdhftl_53lkr0000gn/T/tmpAKONaf/agent.apk
➜  adb install /var/folders/s4/hf3pw66928v6qdhftl_53lkr0000gn/T/tmpAKONaf/agent.apk

Run drozer agent on the emulator and click the On button:

On your computer forward drozer's port and connect to it using the client:

➜  adb forward tcp:31415 tcp:31415
➜  drozer console connect
Selecting 83e5e2881cdf4d59 (unknown Android SDK built for x86_64 7.0)

            ..                    ..:.
           ..o..                  .r..
            ..a..  . ....... .  ..nd
              ro..idsnemesisand..pr
              .otectorandroidsneme.
           .,sisandprotectorandroids+.
         ..nemesisandprotectorandroidsn:.
        .emesisandprotectorandroidsnemes..
      ..isandp,..,rotectorandro,..,idsnem.
      .isisandp..rotectorandroid..snemisis.
      ,andprotectorandroidsnemisisandprotec.
     .torandroidsnemesisandprotectorandroid.
     .snemisisandprotectorandroidsnemesisan:
     .dprotectorandroidsnemesisandprotector.

drozer Console (v2.4.2)
dz>

In drozer console start listening tocom.flagstore.ctf.OUTGOING_INTENT action:

dz> run app.broadcast.sniff --action "com.flagstore.ctf.OUTGOING_INTENT"
[*] Broadcast receiver registered to sniff matching intents
[*] Output is updated once a second. Press Control+C to exit.

Then, run the flagstore application on the emulator:

Send com.flagstore.ctf.INCOMING_INTENT with correct msg parameter to the app using adb:

➜  adb shell
generic_x86_64:/ $ su
generic_x86_64:/ $ am broadcast -a "com.flagstore.ctf.INCOMING_INTENT" --es msg "OpenSesame"

Click appeared Broadcast button in the emulator:

Finally, in the drozer console you see:

Action: com.flagstore.ctf.OUTGOING_INTENT
Raw: Intent { act=com.flagstore.ctf.OUTGOING_INTENT flg=0x10 (has extras) }
Extra: msg=CongratsGoodWorkYouFoundItIHopeYouUsedADBFlag:TheseIntentsAreFunAndEasyToUse (java.lang.String)