Verwendung impliziter Intents für sensible Kommunikation
Beschreibung
Verwendung impliziter Intents für sensible Kommunikation ist eine Android-spezifische Schwachstelle, bei der Anwendungen implizite Intents verwenden, um sensible Daten zu übertragen. Im Gegensatz zu expliziten Intents, die die genaue Zielkomponente angeben, deklarieren implizite Intents nur die auszuführende Aktion, wodurch jede Anwendung, die für diese Aktion registriert ist, den Intent empfangen kann. Wenn sensible Informationen wie Anmeldedaten, persönliche Daten oder Authentifizierungs-Tokens über implizite Intents gesendet werden, kann jede installierte Anwendung mit einem passenden Intent-Filter diese Daten abfangen, was möglicherweise zu Datendiebstahl oder -manipulation führt.
Risiko
Diese Schwachstelle setzt sensible Daten allen Anwendungen auf dem Gerät aus. Bösartige Anwendungen können Intent-Filter registrieren, die dem impliziten Intent entsprechen, und sensible Kommunikationen abfangen. Angreifer können Anmeldedaten, Session-Tokens oder persönliche Informationen stehlen, die durch implizite Intents übertragen werden. Für Intents, die eine Antwort erwarten, können Angreifer bösartige Daten zurückgeben, die die anfällige Anwendung ohne Verifizierung verarbeitet. PendingIntents, die aus impliziten Intents erstellt werden, können von bösartigen Anwendungen gekapert werden. Der Angriff ist besonders effektiv, weil Benutzer möglicherweise unwissentlich bösartige Anwendungen installiert haben, die stillschweigend Kommunikationen abfangen.
Lösung
Verwenden Sie explizite Intents für alle sensiblen Kommunikationen, indem Sie die genaue Zielkomponente angeben. Setzen Sie den Paketnamen mit intent.setPackage() für implizite Intents, die verwendet werden müssen. Für Antworten auf implizite Intents validieren Sie die Quelle vor der Verarbeitung. Verwenden Sie Berechtigungen auf Signaturebene für sensible Interkomponenten-Kommunikation. Erwägen Sie die Verwendung von LocalBroadcastManager für interne Anwendungs-Broadcasts. Beim Erstellen von PendingIntents verwenden Sie FLAG_IMMUTABLE, um Modifikationen zu verhindern. Validieren Sie alle von Intent-Antworten empfangenen Daten. Vermeiden Sie es, sensible Daten in Intents zu platzieren, wenn möglich - verwenden Sie sicheren Speicher und übergeben Sie Referenzen stattdessen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Anwendungsdaten lesen - Unautorisierte Anwendungen können sensible Daten abfangen und lesen, die durch implizite Intents übertragen werden. |
| Integrität | Bereich: Integrität Anwendungsdaten modifizieren - Anwendungen können Antworten von nicht vertrauenswürdigen Quellen verarbeiten, was zu unerwarteten oder unautorisierten Aktionen führt. |
Beispielcode
Anfälliger Code
// Anfällig: Anmeldedaten über impliziten Intent senden
public class VulnerableLoginActivity extends Activity {
public void sendLoginRequest(String username, String password) {
// Anfällig: Impliziter Intent mit sensiblen Daten
Intent intent = new Intent("com.example.USER_LOGIN");
intent.putExtra("username", username);
intent.putExtra("password", password); // Passwort im Intent!
sendBroadcast(intent);
// Jede App mit passendem Intent-Filter empfängt dies!
}
public void createUser(String username, String password) {
// Anfällig: Broadcast mit Anmeldedaten
Intent intent = new Intent();
intent.setAction("com.example.CreateUser");
intent.putExtra("Username", username);
intent.putExtra("Password", password);
sendBroadcast(intent);
}
}
// Bösartige App fängt den anfälligen Broadcast ab
public class MaliciousReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Fängt Anmeldedaten ab!
String username = intent.getStringExtra("Username");
String password = intent.getStringExtra("Password");
// Gestohlene Anmeldedaten an Server des Angreifers senden
sendToServer(username, password);
}
}
// Bösartiges AndroidManifest.xml
// <receiver android:name=".MaliciousReceiver">
// <intent-filter>
// <action android:name="com.example.CreateUser"/>
// </intent-filter>
// </receiver>
// Anfällig: Impliziter Intent für sensible Aktion
public class VulnerablePaymentActivity extends Activity {
public void processPayment(String creditCard, double amount) {
// Anfällig: Zahlungsdaten über impliziten Intent senden
Intent intent = new Intent("com.example.PROCESS_PAYMENT");
intent.putExtra("card_number", creditCard);
intent.putExtra("amount", amount);
startActivityForResult(intent, PAYMENT_REQUEST);
// Jede Zahlungsverarbeitungs-App kann diesen Intent empfangen
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PAYMENT_REQUEST && resultCode == RESULT_OK) {
// Anfällig: Antwort von unbekannter Quelle vertrauen
String transactionId = data.getStringExtra("transaction_id");
markPaymentComplete(transactionId);
}
}
}
// Anfällig: Mutablen PendingIntent aus implizitem Intent erstellen
public class VulnerablePendingIntent {
public void createNotificationWithAction(Context context) {
// Anfällig: Impliziter Intent für PendingIntent
Intent intent = new Intent("com.example.CONFIRM_ACTION");
intent.putExtra("user_token", getUserToken());
// Anfällig: Mutabler PendingIntent
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Bösartige App könnte den PendingIntent vor dem Auslösen modifizieren
}
}
Korrigierter Code
// Korrigiert: Verwendung expliziter Intents für sensible Kommunikation
public class FixedLoginActivity extends Activity {
public void sendLoginRequest(String username, String password) {
// Korrigiert: Expliziten Intent mit spezifischem Ziel verwenden
Intent intent = new Intent();
intent.setComponent(new ComponentName(
"com.example.authservice",
"com.example.authservice.LoginReceiver"
));
intent.putExtra("username", username);
// Korrigiert: Passwort nicht im Intent senden - sicheren Kanal verwenden
// Stattdessen temporär in sicherem Speicher ablegen und Token übergeben
String tempToken = SecureStorage.storeTemporarily(password);
intent.putExtra("auth_token", tempToken);
sendBroadcast(intent, "com.example.permission.AUTH_SERVICE");
}
}
// Korrigiert: Verwendung von LocalBroadcastManager für interne Kommunikation
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class FixedInternalBroadcast {
private LocalBroadcastManager localBroadcastManager;
public void setup(Context context) {
localBroadcastManager = LocalBroadcastManager.getInstance(context);
}
public void createUser(String username, String password) {
// Korrigiert: Lokaler Broadcast erreicht nur Komponenten unserer App
Intent intent = new Intent("com.example.CreateUser");
intent.putExtra("Username", username);
intent.putExtra("Password", password);
localBroadcastManager.sendBroadcast(intent);
// Kann nicht von anderen Apps abgefangen werden
}
}
// Korrigiert: Expliziter Intent für sensible Aktionen mit Validierung
public class FixedPaymentActivity extends Activity {
private static final String TRUSTED_PAYMENT_PACKAGE = "com.trusted.payment";
public void processPayment(String creditCard, double amount) {
// Korrigiert: Expliziter Intent an vertrauenswürdigen Zahlungsverarbeiter
Intent intent = new Intent();
intent.setPackage(TRUSTED_PAYMENT_PACKAGE);
intent.setAction("com.trusted.payment.PROCESS");
// Korrigiert: Verifizieren, dass Paket installiert und korrekt ist
if (!isPackageValid(TRUSTED_PAYMENT_PACKAGE)) {
showError("Zahlungsverarbeiter nicht verfügbar");
return;
}
intent.putExtra("amount", amount);
// Korrigiert: Karte tokenisieren statt rohe Nummer senden
String cardToken = tokenizeCard(creditCard);
intent.putExtra("card_token", cardToken);
startActivityForResult(intent, PAYMENT_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PAYMENT_REQUEST && resultCode == RESULT_OK) {
// Korrigiert: Verifizieren, dass Antwort von vertrauenswürdiger Quelle kam
String callingPackage = getCallingPackage();
if (!TRUSTED_PAYMENT_PACKAGE.equals(callingPackage)) {
Log.w(TAG, "Antwort von nicht vertrauenswürdigem Paket: " + callingPackage);
return;
}
String transactionId = data.getStringExtra("transaction_id");
// Korrigiert: Transaktion mit Backend verifizieren, bevor als abgeschlossen markiert
verifyAndCompletePayment(transactionId);
}
}
private boolean isPackageValid(String packageName) {
try {
PackageInfo info = getPackageManager().getPackageInfo(packageName, 0);
// Optional Signatur verifizieren
return verifyPackageSignature(info);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}
// Korrigiert: Immutabler PendingIntent mit explizitem Ziel
public class FixedPendingIntent {
public void createNotificationWithAction(Context context) {
// Korrigiert: Expliziter Intent mit spezifischer Komponente
Intent intent = new Intent(context, ConfirmActionReceiver.class);
intent.setPackage(context.getPackageName());
// Sensible Tokens nicht im Intent platzieren
intent.putExtra("action_id", "confirm_action");
// Korrigiert: FLAG_IMMUTABLE verwenden
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, intent,
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
// Benachrichtigung mit pendingIntent bauen
}
}
// Korrigiert: Verwendung von gebundenem Service für sensible IPC
public class FixedServiceCommunication {
private ISecureService secureService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
secureService = ISecureService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
secureService = null;
}
};
public void bindToSecureService(Context context) {
// Korrigiert: Explizite Bindung an unseren eigenen Service
Intent intent = new Intent(context, SecureDataService.class);
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
public void sendSensitiveData(String data) throws RemoteException {
if (secureService != null) {
// Kommunikation nur mit unserem gebundenen Service
secureService.processSensitiveData(data);
}
}
}
CVE-Beispiele
- CVE-2022-4903: Android-Anwendung verwendet FLAG_IMMUTABLE nicht beim Erstellen eines PendingIntent, was Intent-Hijacking ermöglicht.
Verwandte CWEs
- CWE-285: Improper Authorization (Eltern)
- CWE-668: Exposure of Resource to Wrong Sphere (Eltern)
- CWE-925: Improper Verification of Intent by Broadcast Receiver (verwandt)
- CWE-926: Improper Export of Android Application Components (verwandt)
Referenzen
- MITRE Corporation. "CWE-927: Use of Implicit Intent for Sensitive Communication." https://cwe.mitre.org/data/definitions/927.html
- Android Developers. "Intents and Intent Filters." https://developer.android.com/guide/components/intents-filters
- Android Developers. "Security tips." https://developer.android.com/training/articles/security-tips#IPC