Unsachgemäße Verifizierung von Intents durch Broadcast Receiver

Beschreibung

Unsachgemäße Verifizierung von Intents durch Broadcast Receiver ist eine Android-spezifische Schwachstelle, bei der Anwendungen, die Broadcast Receiver verwenden, nicht verifizieren, dass empfangene Intents von autorisierten Quellen stammen. Während das Android-Betriebssystem bestimmte implizite System-Intents (wie ACTION_BOOT_COMPLETED) darauf beschränkt, nur vom Betriebssystem selbst gesendet zu werden, können bösartige Anwendungen explizite Intents direkt an den Broadcast Receiver einer anfälligen Anwendung senden. Selbst Receiver, die für implizite System-Intents registriert sind, erhalten auch explizite Intents, was möglicherweise unbeabsichtigtes Verhalten verursacht, wenn der Receiver die Quelle des Intents nicht validiert.

Risiko

Diese Schwachstelle ermöglicht es bösartigen Anwendungen, Aktionen in anfälligen Anwendungen auszulösen, indem sie manipulierte Intents senden. Angreifer können Denial-of-Service verursachen, indem sie Shutdown- oder Cleanup-Prozeduren auslösen. Sensible Operationen, die nur für System-Events bestimmt sind, können beliebig ausgelöst werden. Daten können exfiltriert werden, wenn Receiver bösartige Intents verarbeiten und darauf reagieren. Der Anwendungsstatus kann durch unerwartete Intent-Verarbeitung beschädigt werden. In schweren Fällen könnten Angreifer privilegierte Operationen auslösen, die nur als Reaktion auf legitime System-Events auftreten sollten.

Lösung

Verifizieren Sie immer die Quelle empfangener Intents in Broadcast Receivern. Für Receiver, die nur auf System-Intents reagieren sollten, prüfen Sie, ob der Intent implizit ist (keine explizite Komponente gesetzt). Verwenden Sie Berechtigungen auf Signaturebene, um einzuschränken, wer Intents an Ihre Receiver senden kann. Erwägen Sie die Verwendung von LocalBroadcastManager für interne Anwendungs-Broadcasts. Setzen Sie android:exported="false" für Receiver, die keine externen Intents empfangen müssen. Validieren Sie alle Intent-Extras vor der Verarbeitung. Für sensible Operationen implementieren Sie zusätzliche Autorisierungsprüfungen über das bloße Empfangen des Intents hinaus.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Privilegien erlangen oder Identität annehmen - Bösartige Anwendungen können das Betriebssystem imitieren und Aktionen auslösen, die nur für System-Events bestimmt sind.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz/Beenden/Neustart - Angreifer können Shutdown- oder Cleanup-Prozeduren auslösen und Denial-of-Service verursachen.
IntegritätBereich: Integrität

Anwendungsdaten modifizieren - Unerwartete Intent-Verarbeitung kann den Anwendungsstatus oder Daten beschädigen.

Beispielcode

Anfälliger Code

// Anfällig: BroadcastReceiver ohne Intent-Verifizierung
public class VulnerableShutdownReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        // Anfällig: Keine Verifizierung, dass dies vom System kommt
        if (Intent.ACTION_SHUTDOWN.equals(action)) {
            // Angreifer kann dies mit explizitem Intent auslösen!
            performShutdownCleanup(context);
            deleteTemporaryFiles(context);
            saveStateAndClose(context);
        }
    }

    private void performShutdownCleanup(Context context) {
        // Caches leeren, Daten speichern, etc.
    }
}

// AndroidManifest.xml
// <receiver android:name=".VulnerableShutdownReceiver"
//           android:exported="true">
//     <intent-filter>
//         <action android:name="android.intent.action.ACTION_SHUTDOWN"/>
//     </intent-filter>
// </receiver>
// Bösartige App des Angreifers
public class AttackerApp {

    public void triggerShutdown(Context context) {
        // Expliziten Intent an anfälligen Receiver senden
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SHUTDOWN);
        intent.setComponent(new ComponentName(
            "com.vulnerable.app",
            "com.vulnerable.app.VulnerableShutdownReceiver"
        ));
        context.sendBroadcast(intent);
        // Anfällige App führt Shutdown-Cleanup durch!
    }
}
// Anfällig: Batterie-Überwachungs-Receiver
public class VulnerableBatteryReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        // Anfällig: Batterie-Level von jedem Intent vertrauen
        if (Intent.ACTION_BATTERY_LOW.equals(action)) {
            // Angreifer kann Niedrigbatterie-Modus auslösen
            enablePowerSavingMode(context);
            disableBackgroundSync(context);
            notifyUser("Batterie niedrig!");
        }

        if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
            // Angreifer kann Energiesparmodus deaktivieren
            disablePowerSavingMode(context);
        }
    }
}

Korrigierter Code

// Korrigiert: BroadcastReceiver mit Intent-Quellenverifizierung
public class FixedShutdownReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // Korrigiert: Verifizieren, dass dies ein System-Broadcast ist, kein expliziter Intent
        if (!isSystemBroadcast(intent)) {
            Log.w(TAG, "Ablehnung expliziten Intents, der versucht Shutdown auszulösen");
            return;
        }

        String action = intent.getAction();

        if (Intent.ACTION_SHUTDOWN.equals(action)) {
            performShutdownCleanup(context);
            deleteTemporaryFiles(context);
            saveStateAndClose(context);
        }
    }

    private boolean isSystemBroadcast(Intent intent) {
        // Korrigiert: Prüfen, ob dies ein impliziter Broadcast ist (kein explizites Ziel)
        // Explizite Intents haben Component gesetzt
        return intent.getComponent() == null;
    }

    private void performShutdownCleanup(Context context) {
        // Sicher fortzufahren
    }
}

// AndroidManifest.xml - Berechtigungsanforderung hinzufügen
// <receiver android:name=".FixedShutdownReceiver"
//           android:exported="true"
//           android:permission="android.permission.SHUTDOWN">
//     <intent-filter>
//         <action android:name="android.intent.action.ACTION_SHUTDOWN"/>
//     </intent-filter>
// </receiver>
// Korrigiert: Verwendung von Signatur-Berechtigung für sensible Receiver
// In AndroidManifest.xml:
// <permission
//     android:name="com.myapp.permission.SENSITIVE_ACTION"
//     android:protectionLevel="signature"/>
//
// <receiver android:name=".SensitiveReceiver"
//           android:exported="true"
//           android:permission="com.myapp.permission.SENSITIVE_ACTION">

public class FixedSensitiveReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // Korrigiert: Selbst mit explizitem Intent können nur Apps, die mit
        // unserem Schlüssel signiert sind, diese Berechtigung haben

        // Zusätzliche Verifizierung für System-Intents
        if (isSystemAction(intent.getAction())) {
            if (!isFromSystem(context, intent)) {
                Log.w(TAG, "Ignoriere Nicht-System-Intent für System-Aktion");
                return;
            }
        }

        handleIntent(context, intent);
    }

    private boolean isSystemAction(String action) {
        return Intent.ACTION_SHUTDOWN.equals(action) ||
               Intent.ACTION_BOOT_COMPLETED.equals(action) ||
               Intent.ACTION_BATTERY_LOW.equals(action);
    }

    private boolean isFromSystem(Context context, Intent intent) {
        // Implizite Broadcasts haben keine Component gesetzt
        return intent.getComponent() == null;
    }
}
// Korrigiert: LocalBroadcastManager für interne Broadcasts verwenden
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

public class FixedInternalCommunication {

    private LocalBroadcastManager localBroadcastManager;
    private BroadcastReceiver receiver;

    public void setup(Context context) {
        // Korrigiert: Lokale Broadcasts können nicht von anderen Apps empfangen werden
        localBroadcastManager = LocalBroadcastManager.getInstance(context);

        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // Korrigiert: Empfängt nur Intents von innerhalb unserer App
                handleInternalMessage(intent);
            }
        };

        IntentFilter filter = new IntentFilter("com.myapp.INTERNAL_MESSAGE");
        localBroadcastManager.registerReceiver(receiver, filter);
    }

    public void sendInternalBroadcast(Intent intent) {
        // Korrigiert: Dieser Broadcast bleibt innerhalb der App
        localBroadcastManager.sendBroadcast(intent);
    }

    public void cleanup() {
        if (receiver != null) {
            localBroadcastManager.unregisterReceiver(receiver);
        }
    }
}
// Korrigiert: Umfassende Intent-Validierung
public class FixedBatteryReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // Korrigiert: Mehrere Validierungsprüfungen
        if (!validateIntent(context, intent)) {
            Log.w(TAG, "Ungültiger Intent abgelehnt");
            return;
        }

        String action = intent.getAction();

        if (Intent.ACTION_BATTERY_LOW.equals(action)) {
            // Zusätzliche Prüfung: Verifizieren, dass Batterie tatsächlich niedrig ist
            IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
            Intent batteryStatus = context.registerReceiver(null, filter);

            if (batteryStatus != null) {
                int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
                int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
                float batteryPct = level * 100 / (float) scale;

                // Korrigiert: Nur reagieren, wenn Batterie tatsächlich niedrig ist
                if (batteryPct < 15) {
                    enablePowerSavingMode(context);
                }
            }
        }
    }

    private boolean validateIntent(Context context, Intent intent) {
        // Prüfung 1: Muss impliziter Broadcast sein
        if (intent.getComponent() != null) {
            Log.w(TAG, "Ablehnung expliziten Intents");
            return false;
        }

        // Prüfung 2: Verifizieren, dass Aktion erwartet wird
        String action = intent.getAction();
        if (!Intent.ACTION_BATTERY_LOW.equals(action) &&
            !Intent.ACTION_BATTERY_OKAY.equals(action)) {
            return false;
        }

        return true;
    }
}

Verwandte CWEs

  • CWE-940: Improper Verification of Source of a Communication Channel (Eltern)
  • CWE-926: Improper Export of Android Application Components (verwandt)
  • CWE-927: Use of Implicit Intent for Sensitive Communication (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-925: Improper Verification of Intent by Broadcast Receiver." https://cwe.mitre.org/data/definitions/925.html
  2. Android Developers. "Broadcasts overview." https://developer.android.com/guide/components/broadcasts
  3. Android Developers. "Security tips." https://developer.android.com/training/articles/security-tips