OWASP-Kategorie: MASVS-PLATFORM: Plattforminteraktion
Übersicht
Laut der Dokumentation ist ContentResolver
eine „Klasse, die Anwendungen Zugriff auf das Inhaltsmodell gewährt“. ContentResolver stellen Methoden bereit, um mit Inhalten zu interagieren, sie abzurufen oder zu ändern, die aus folgenden Quellen stammen:
- Installierte Apps (
content://
-URI-Schema) - Dateisysteme (
file://
-URI-Schema) - Unterstützung von APIs, die von Android bereitgestellt werden (
android.resource://
URI-Schema).
Zusammenfassend gehören Sicherheitslücken im Zusammenhang mit ContentResolver
zur Klasse Confused Deputy, da der Angreifer die Berechtigungen einer angreifbaren Anwendung nutzen kann, um auf geschützte Inhalte zuzugreifen.
Risiko: Missbrauch aufgrund nicht vertrauenswürdiger file://-URIs
Beim Missbrauch von ContentResolver
mit der file://
-URI-Sicherheitslücke wird die Fähigkeit von ContentResolver
ausgenutzt, vom URI beschriebene Dateideskriptoren zurückzugeben. Diese Sicherheitslücke betrifft Funktionen wie openFile()
, openFileDescriptor()
, openInputStream()
, openOutputStream()
oder openAssetFileDescriptor()
der ContentResolver
API. Die Sicherheitslücke kann mit einem vollständig oder teilweise vom Angreifer kontrollierten file://
-URI missbraucht werden, um die Anwendung dazu zu zwingen, auf Dateien zuzugreifen, auf die kein Zugriff vorgesehen ist, z. B. auf interne Datenbanken oder freigegebene Einstellungen.
Eines der möglichen Angriffsszenarien besteht darin, eine schädliche Galerie oder Dateiauswahl zu erstellen, die bei Verwendung durch eine anfällige App einen schädlichen URI zurückgibt.
Es gibt einige Varianten dieses Angriffs:
- Vollständig vom Angreifer kontrollierter
file://
-URI, der auf die internen Dateien einer App verweist - Ein Teil des
file://
-URIs wird vom Angreifer gesteuert, was zu Pfadüberprüfungen führen kann file://
URI, der auf einen von Angreifern kontrollierten symbolischen Link (Symlink) verweist, der auf die internen Dateien der App verweist- Ähnlich wie bei der vorherigen Variante, tauscht der Angreifer hier jedoch wiederholt das Symlink-Ziel von einem legitimen Ziel in die internen Dateien einer App aus. Ziel ist es, eine Race-Bedingung zwischen einer potenziellen Sicherheitsprüfung und der Verwendung des Dateipfads auszunutzen.
Positiv beeinflussen
Die Auswirkungen der Ausnutzung dieser Sicherheitslücke variieren je nachdem, wofür der ContentResolver verwendet wird. In vielen Fällen kann dies dazu führen, dass geschützte Daten einer App von unbefugten Dritten abgerufen oder geändert werden.
Abhilfemaßnahmen
Um diese Sicherheitslücke zu minimieren, verwenden Sie den folgenden Algorithmus, um den Dateideskriptor zu validieren. Nach der Validierung kann der Dateideskriptor sicher verwendet werden.
Kotlin
fun isValidFile(ctx: Context, pfd: ParcelFileDescriptor, fileUri: Uri): Boolean {
// Canonicalize to resolve symlinks and path traversals.
val fdCanonical = File(fileUri.path!!).canonicalPath
val pfdStat: StructStat = Os.fstat(pfd.fileDescriptor)
// Lstat doesn't follow the symlink.
val canonicalFileStat: StructStat = Os.lstat(fdCanonical)
// Since we canonicalized (followed the links) the path already,
// the path shouldn't point to symlink unless it was changed in the
// meantime.
if (OsConstants.S_ISLNK(canonicalFileStat.st_mode)) {
return false
}
val sameFile =
pfdStat.st_dev == canonicalFileStat.st_dev &&
pfdStat.st_ino == canonicalFileStat.st_ino
if (!sameFile) {
return false
}
return !isBlockedPath(ctx, fdCanonical)
}
fun isBlockedPath(ctx: Context, fdCanonical: String): Boolean {
// Paths that should rarely be exposed
if (fdCanonical.startsWith("/proc/") ||
fdCanonical.startsWith("/data/misc/")) {
return true
}
// Implement logic to block desired directories. For example, specify
// the entire app data/ directory to block all access.
}
Java
boolean isValidFile(Context ctx, ParcelFileDescriptor pfd, Uri fileUri) {
// Canonicalize to resolve symlinks and path traversals
String fdCanonical = new File(fileUri.getPath()).getCanonicalPath();
StructStat pfdStat = Os.fstat(pfd.getFileDescriptor());
// Lstat doesn't follow the symlink.
StructStat canonicalFileStat = Os.lstat(fdCanonical);
// Since we canonicalized (followed the links) the path already,
// the path shouldn't point to symlink unless it was changed in the meantime
if (OsConstants.S_ISLNK(canonicalFileStat.st_mode)) {
return false;
}
boolean sameFile =
pfdStat.stDev == canonicalFileStat.stDev && pfdStat.stIno == canonicalFileStat.stIno;
if (!sameFile) {
return false;
}
return !isBlockedPath(ctx, fdCanonical);
}
boolean isBlockedPath(Context ctx, String fdCanonical) {
// Paths that should rarely be exposed
if (fdCanonical.startsWith("/proc/") || fdCanonical.startsWith("/data/misc/")) {
return true;
}
// Implement logic to block desired directories. For example, specify
// the entire app data/ directory to block all access.
}
Risiko: Missbrauch aufgrund eines nicht vertrauenswürdigen content://-URI
Der Missbrauch einer ContentResolver
mit einer content://
-URI-Sicherheitslücke tritt auf, wenn ein vollständig oder teilweise von Angreifern kontrollierter URI an ContentResolver
-APIs übergeben wird, um auf Inhalte zuzugreifen, die nicht für den Zugriff vorgesehen sind.
Es gibt zwei Hauptszenarien für diesen Angriff:
- Die App verwendet eigene interne Inhalte. Beispiel: Nachdem die Mail-App einen URI von einem Angreifer erhalten hat, hängt sie Daten von ihrem eigenen internen Inhaltsanbieter anstelle eines externen Fotos an.
- Die App fungiert als Proxy und greift dann für den Angreifer auf die Daten einer anderen Anwendung zu. Beispiel: Die E-Mail-Anwendung hängt Daten aus App X an, die durch eine Berechtigung geschützt sind, die dem Angreifer normalerweise den Zugriff auf diesen bestimmten Anhang verwehrt. Er ist für die Anwendung verfügbar, die den Anhang erstellt, aber nicht anfangs, sodass dieser Inhalt an den Angreifer weitergeleitet wird.
Ein mögliches Angriffsszenario besteht darin, eine schädliche Galerie oder Dateiauswahl zu erstellen, die bei Verwendung durch eine anfällige App einen schädlichen URI zurückgibt.
Positiv beeinflussen
Die Auswirkungen der Ausnutzung dieser Sicherheitslücke variieren je nach Kontext, der mit dem ContentResolver verknüpft ist. Dies kann dazu führen, dass geschützte Daten einer App ausgeschleust oder von nicht autorisierten Personen geändert werden.
Abhilfemaßnahmen
Allgemein
Prüfe eingehende URIs. Es gilt beispielsweise als Best Practice, eine Zulassungsliste mit erwarteten Behörden zu verwenden.
URI richtet sich an nicht exportierten oder berechtigungsgeschützten Contentanbieter, der zu einer angreifbaren App gehört
Prüfen Sie, ob der URI auf Ihre App verweist:
Kotlin
fun belongsToCurrentApplication(ctx: Context, uri: Uri): Boolean {
val authority: String = uri.authority.toString()
val info: ProviderInfo =
ctx.packageManager.resolveContentProvider(authority, 0)!!
return ctx.packageName.equals(info.packageName)
}
Java
boolean belongsToCurrentApplication(Context ctx, Uri uri){
String authority = uri.getAuthority();
ProviderInfo info = ctx.getPackageManager().resolveContentProvider(authority, 0);
return ctx.getPackageName().equals(info.packageName);
}
Wenn der ausgewählte Anbieter exportiert wird:
Kotlin
fun isExported(ctx: Context, uri: Uri): Boolean {
val authority = uri.authority.toString()
val info: ProviderInfo =
ctx.packageManager.resolveContentProvider(authority, 0)!!
return info.exported
}
Java
boolean isExported(Context ctx, Uri uri){
String authority = uri.getAuthority();
ProviderInfo info = ctx.getPackageManager().resolveContentProvider(authority, 0);
return info.exported;
}
Oder wenn eine explizite Berechtigung für den URI erteilt wurde. Diese Prüfung basiert auf der Annahme, dass der URI nicht bösartig ist, wenn eine explizite Berechtigung für den Zugriff auf die Daten erteilt wurde:
Kotlin
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
fun wasGrantedPermission(ctx: Context, uri: Uri?, grantFlag: Int): Boolean {
val pid: Int = Process.myPid()
val uid: Int = Process.myUid()
return ctx.checkUriPermission(uri, pid, uid, grantFlag) ==
PackageManager.PERMISSION_GRANTED
}
Java
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
boolean wasGrantedPermission(Context ctx, Uri uri, int grantFlag){
int pid = Process.myPid();
int uid = Process.myUid();
return ctx.checkUriPermission(uri, pid, uid, grantFlag) == PackageManager.PERMISSION_GRANTED;
}
Der URI ist auf einen berechtigungsgeschützten ContentProvider ausgerichtet, der zu einer anderen App gehört, die der angreifbaren App vertraut.
Dieser Angriff ist in folgenden Situationen relevant:
- Anwendungsumgebungen, in denen Apps benutzerdefinierte Berechtigungen oder andere Authentifizierungsmechanismen definieren und verwenden.
- Berechtigungs-Proxy-Angriffe, bei denen ein Angreifer eine angreifbare App mit einer Laufzeitberechtigung wie READ_CONTACTS missbraucht, um Daten von einem Systemanbieter abzurufen.
Prüfen Sie, ob die URI-Berechtigung erteilt wurde:
Kotlin
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
fun wasGrantedPermission(ctx: Context, uri: Uri?, grantFlag: Int): Boolean {
val pid: Int = Process.myPid()
val uid: Int = Process.myUid()
return ctx.checkUriPermission(uri, pid, uid, grantFlag) ==
PackageManager.PERMISSION_GRANTED
}
Java
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
boolean wasGrantedPermission(Context ctx, Uri uri, int grantFlag){
int pid = Process.myPid();
int uid = Process.myUid();
return ctx.checkUriPermission(uri, pid, uid, grantFlag) == PackageManager.PERMISSION_GRANTED;
}
Wenn für die Nutzung anderer Inhaltsanbieter keine Berechtigungsgrants erforderlich sind, z. B. wenn die App allen Apps aus dem Ökosystem den Zugriff auf alle Daten erlaubt, müssen Sie die Verwendung dieser Autoritäten ausdrücklich verbieten.