OWASP kategorisi: MASVS-PLATFORM: Platform Etkileşimi
Genel Bakış
Belgelere göre ContentResolver
, "uygulamalara içerik modeline erişim sağlayan bir sınıf"tır. ContentResolver'lar, aşağıdaki kaynaklardan sağlanan içerikle etkileşim kurma, içerik getirme veya içerik değiştirme yöntemlerini gösterir:
- Yüklü uygulamalar (
content://
URI şeması) - Dosya sistemleri (
file://
URI şeması) - Android tarafından sağlanan API'leri destekleme (
android.resource://
URI şeması).
Özetlemek gerekirse, saldırgan korumalı içeriğe erişmek için güvenlik açığı olan bir uygulamanın ayrıcalıklarını kullanabildiğinden, ContentResolver
ile ilgili güvenlik açıkları şaşırmış vekil sınıfına aittir.
Risk: Güvenilir olmayan file:// URI'sine dayalı kötüye kullanım
file://
URI güvenlik açığını kullanarak ContentResolver
'ün kötüye kullanılması, ContentResolver
'ün URI tarafından açıklanan dosya tanımlayıcılarını döndürme özelliğinden yararlanır. Bu güvenlik açığı, ContentResolver
API'deki openFile()
, openFileDescriptor()
, openInputStream()
, openOutputStream()
veya openAssetFileDescriptor()
gibi işlevleri etkiler. Bu güvenlik açığı, uygulamayı erişimi amaçlanmayan dosyalara (ör. dahili veritabanları veya paylaşılan tercihler) erişmeye zorlamak için saldırgan tarafından tamamen veya kısmen kontrol edilen bir file://
URI ile kötüye kullanılabilir.
Olası saldırı senaryolarından biri, güvenlik açığı olan bir uygulama tarafından kullanıldığında kötü amaçlı bir URI döndürecek kötü amaçlı bir galeri veya dosya seçici oluşturmaktır.
Bu saldırının birkaç varyantı vardır:
- Bir uygulamanın dahili dosyalarını işaret eden, tamamen saldırgan tarafından kontrol edilen
file://
URI file://
URI'sinin bir kısmı saldırgan tarafından kontrol edildiğinden, yol geçişlerine açıktırfile://
Uygulamanın dahili dosyalarına yönlendiren, saldırgan tarafından kontrol edilen bir sembolik bağlantıyı (sembolik bağlantı) hedefleyen URI- Önceki varyanta benzer ancak burada saldırgan, sembolik bağlantı hedefini meşru bir hedeften uygulamanın dahili dosyalarıyla tekrar tekrar değiştirir. Amaç, olası bir güvenlik kontrolü ile dosya yolu kullanımı arasındaki yarış koşulundan yararlanmaktır.
Etki
Bu güvenlik açığından yararlanmanın etkisi, ContentResolver'ın ne için kullanıldığına bağlı olarak değişir. Bu durum, çoğu durumda bir uygulamanın korunan verilerinin sızdırılmasına veya yetkisiz taraflar tarafından korunan verilerin değiştirilmesine neden olabilir.
Çözümler
Bu güvenlik açığının oluşturduğu riskleri azaltmak için dosya tanımlayıcısını doğrulamak üzere aşağıdaki algoritmayı kullanın. Doğrulama işlemini geçtikten sonra dosya tanımlayıcısı güvenli bir şekilde kullanılabilir.
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.
}
Risk: Güvenilir olmayan content:// URI'sine dayalı kötüye kullanım
content://
URI güvenlik açığı kullanılarak ContentResolver
'ün kötüye kullanılması, tamamen veya kısmen saldırgan tarafından kontrol edilen bir URI'nin, erişilebilir olması amaçlanmayan içerik üzerinde işlem yapması için ContentResolver
API'lerine iletilmesiyle gerçekleşir.
Bu saldırının iki temel senaryosu vardır:
- Uygulama, kendi dahili içeriğiyle çalışır. Örneğin: E-posta uygulaması, bir saldırgandan URI aldıktan sonra harici bir fotoğraf yerine kendi dahili içerik sağlayıcısından veri ekler.
- Uygulama, proxy görevi görür ve ardından saldırgan için başka bir uygulamanın verilerine erişir. Örneğin: Posta uygulaması, X uygulamasından normalde saldırganın söz konusu eki görmesine izin vermeyecek bir izinle korunan verileri ekler. Ekleme yapan uygulama tarafından kullanılabilir ancak başlangıçta bu içerik saldırgana iletilmez.
Olası bir saldırı senaryosu, güvenlik açığı olan bir uygulama tarafından kullanıldığında kötü amaçlı bir URI döndürecek kötü amaçlı bir galeri veya dosya seçici oluşturmaktır.
Etki
Bu güvenlik açığından yararlanmanın etkisi, ContentResolver ile ilişkili bağlama bağlı olarak değişir. Bu durum, bir uygulamanın korunan verilerinin sızdırılmasına veya yetkisiz tarafların korunan verilerde değişiklik yapmasına neden olabilir.
Çözümler
Genel
Gelen URI'leri doğrulayın. Örneğin, beklenen yetkililer için izin verilenler listesi kullanmak iyi bir uygulama olarak kabul edilir.
URI, güvenlik açığı olan uygulamaya ait dışa aktarılmamış veya izin korumalı içerik sağlayıcıyı hedefliyor
URI'nin uygulamanızı hedefleyip hedeflemediğini kontrol edin:
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);
}
Hedeflenen sağlayıcı dışa aktarıldıysa:
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;
}
Veya URI'ye açık izin verilmişse (bu kontrol, verilere erişmek için açık izin verildiğinde URI'nin kötü amaçlı olmadığı varsayımını temel alır):
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;
}
URI, güvenlik açığı olan uygulamaya güvenen başka bir uygulamaya ait izin korumalı bir ContentProvider'ı hedefliyor.
Bu saldırı aşağıdaki durumlarla ilgilidir:
- Uygulamaların özel izinleri veya diğer kimlik doğrulama mekanizmalarını tanımlayıp kullandığı uygulama ekosistemleri.
- Saldırganın, bir sistem sağlayıcıdan veri almak için READ_CONTACTS gibi bir çalışma zamanında izine sahip olan savunmasız bir uygulamayı kötüye kullandığı izin proxy saldırıları.
URI izninin verilip verilmediğini test edin:
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;
}
Diğer içerik sağlayıcıların kullanımı izin verilmesini gerektirmiyorsa (ör. uygulama, ekosistemdeki tüm uygulamaların tüm verilere erişmesine izin veriyorsa) bu yetkilerin kullanımını açıkça yasaklayın.