Categoria OWASP: MASVS-PLATFORM: Interazione con la piattaforma
Panoramica
Secondo la documentazione, ContentResolver
è una "classe che fornisce alle applicazioni l'accesso al modello di contenuti". I ContentResolver espongono metodi per interagire, recuperare o modificare i contenuti forniti da quanto segue:
- App installate (schema URI
content://
) - File system (schema URI
file://
) - API supportate come fornite da Android (schema URI
android.resource://
).
In sintesi, le vulnerabilità relative a ContentResolver
appartengono alla classe rappresentante confuso, in quanto l'attaccante può utilizzare i privilegi di un'applicazione vulnerabile per accedere a contenuti protetti.
Rischio: abuso basato su URI file:// non attendibile
L'abuso di ContentResolver
che utilizza la vulnerabilità dell'URI file://
sfrutta la capacità di ContentResolver
di restituire i descrittori dei file descritti dall'URI. Questa vulnerabilità interessa funzioni come openFile()
, openFileDescriptor()
, openInputStream()
, openOutputStream()
o openAssetFileDescriptor()
dell'API ContentResolver
. La vulnerabilità può essere sfruttata con un URI file://
completamente o parzialmente controllato dall'attaccante per forzare l'applicazione ad accedere a file che non dovevano essere accessibili, come database interni o preferenze condivise.
Uno dei possibili scenari di attacco consisterebbe nel creare una galleria o un selettore di file dannoso che, se utilizzato da un'app vulnerabile, restituisse un URI dannoso.
Esistono alcune varianti di questo attacco:
- URI
file://
completamente controllato dall'attaccante che rimanda ai file interni di un'app - Parte dell'URI
file://
è controllata dall'utente malintenzionato, il che la rende soggetta a attraversamenti del percorso - URI
file://
che ha come target un link simbolico (symlink) controllato dall'utente malintenzionato che rimanda ai file interni dell'app - Simile alla variante precedente, ma in questo caso l'utente malintenzionato scambia ripetutamente il target del link simbolico da un target legittimo ai file interni di un'app. L'obiettivo è sfruttare una condizione di gara tra un potenziale controllo di sicurezza e l'utilizzo del percorso del file
Impatto
L'impatto dello sfruttamento di questa vulnerabilità varia a seconda dell'utilizzo di ContentResolver. In molti casi, può comportare l'esfiltrazione dei dati protetti di un'app o la loro modifica da parte di terze parti non autorizzate.
Mitigazioni
Per mitigare questa vulnerabilità, utilizza l'algoritmo riportato di seguito per convalidare il descrittore file. Dopo aver superato la convalida, il descrittore file può essere utilizzato in tutta sicurezza.
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.
}
Rischio: abuso basato su URI content:// non attendibile
L'abuso di un ContentResolver
che utilizza una vulnerabilità dell'URI content://
si verifica quando un URI completamente o parzialmente controllato dall'utente malintenzionato viene passato alle API ContentResolver
per operare su contenuti che non dovevano essere accessibili.
Esistono due scenari principali per questo attacco:
- L'app funziona con i suoi contenuti interni. Ad esempio, dopo aver ricevuto un URI da un malintenzionato, l'app Mail allega i dati del proprio fornitore di contenuti interno anziché di una foto esterna.
- L'app agisce da proxy e accede ai dati di un'altra applicazione per l'aggressore. Ad esempio, l'applicazione di posta allega dati dell'app X protetti da un'autorizzazione che normalmente non consentirebbe all'attaccante di vedere quell'allegato specifico. È disponibile per l'applicazione che esegue l'allegato, ma non inizialmente, quindi non inoltra questi contenuti all'attaccante.
Un possibile scenario di attacco è creare una galleria o un selettore di file dannoso che, se utilizzato da un'app vulnerabile, restituisce un URI dannoso.
Impatto
L'impatto dello sfruttamento di questa vulnerabilità varia a seconda del contesto associato a ContentResolver. Ciò potrebbe comportare l'esfiltrazione dei dati protetti di un'app o la loro modifica da parte di terze parti non autorizzate.
Mitigazioni
Generali
Convalida gli URI in entrata. Ad esempio, l'utilizzo di una lista consentita di autorità previste è considerata una buona prassi.
L'URI ha come target un fornitore di contenuti non esportato o protetto da autorizzazione che appartiene a un'app vulnerabile
Controlla se l'URI ha come target la tua app:
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);
}
In alternativa, se il fornitore scelto come target viene esportato:
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;
}
In alternativa, se è stata concessa l'autorizzazione esplicita all'URI, questo controllo si basa sul presupposto che, se è stata concessa l'autorizzazione esplicita per accedere ai dati, l'URI non è dannoso:
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;
}
L'URI ha come target un ContentProvider protetto da autorizzazioni che appartiene a un'altra app attendibile per l'app vulnerabile.
Questo attacco è pertinente alle seguenti situazioni:
- Ecosistemi di applicazioni in cui le app definiscono e utilizzano autorizzazioni personalizzate o altri meccanismi di autenticazione.
- Attacchi proxy delle autorizzazioni, in cui un malintenzionato abusa di un'app vulnerabile che detiene un'autorizzazione di runtime, ad esempio READ_CONTACTS, per recuperare dati da un provider di sistema.
Verifica se l'autorizzazione URI è stata concessa:
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;
}
Se l'utilizzo di altri fornitori di contenuti non richiede la concessione di un'autorizzazione, ad esempio quando l'app consente a tutte le app dell'ecosistema di accedere a tutti i dati, proibisci esplicitamente l'utilizzo di queste autorità.