Content-Resolver

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.