Potentially Untrusted External Storage File Access

Description

Files saved to the external storage prior to Android 4.1 are world-readable. Prior to Android 1, files saved to external storage are world-writable. From Android 1 to Android 4.3, only the WRITE_EXTERNAL_STORAGE permission is required for an app to write to any external storage file stored by any app. Starting with Android 4.4, groups and modes of files are created based on a directory structure, which allows an app permission to manage/read/write files within a directory structure based on its package name. Starting with Android 4.4, users (including apps as users) are isolated from primary external storage spaces of other apps controlled by the Android device.

Consequent to the lack of restrictions described above, files written to external storage can be modified or read by other apps installed on the device (for the Android versions which allow read/write) and by anyone with access to the files if stored on an off-device external storage device such as a PC (or if the in-device external storage media is removed and mounted elsewhere).

Source: CERT Secure Coding

Recommendation

It is recomended to set proper permissions on file stored on the external storage and encrypt sensitive data that may need to be stored on the external storage.

Technical details
[TAINT] String '/sdcard/DCIM/.thumbnails' ==>>> Sink '['Ljava/io/File;', '<init>', '(Ljava/lang/String;)V', '0', 'FILESYSTEM_ACCESS_SINK']' [[('Lcom/android/providers/media/MediaProvider;', 'pruneThumbnails', '()V'), ('Ljava/io/File;', '<init>', '(Ljava/lang/String;)V')]]

Call to filesystem access method using a string path to external storage, like /sdcard/

Method com.android.providers.media.MediaProvider.pruneThumbnails():


    private void pruneThumbnails()
    {
        android.util.Log.v(com.android.providers.media.MediaProvider.TAG, "pruneThumbnails ");
        android.database.sqlite.SQLiteDatabase v12 = this.getDatabaseForUri(android.provider.MediaStore$Images$Thumbnails.getContentUri("external")).getWritableDatabase();
        v12.execSQL("delete from thumbnails where image_id not in (select _id from images)");
        v12.execSQL("delete from videothumbnails where video_id not in (select _id from video)");
        java.util.HashSet v13 = new java.util.HashSet();
        try {
            String v4_2 = new java.io.File("/sdcard/DCIM/.thumbnails").getCanonicalFile();
            String v5_0 = v4_2.list();
        } catch (SecurityException v0) {
            return;
        }
        if (v5_0 == null) {
            String[] v6_0 = new String[0];
            v5_0 = v6_0;
        }
        String[] v6_1 = v4_2.getPath();
        int v7_0 = 0;
        while (v7_0 < v5_0.length) {
            if (v5_0[v7_0].endsWith(".jpg")) {
                int v8_5 = new StringBuilder();
                v8_5.append(v6_1);
                v8_5.append("/");
                v8_5.append(v5_0[v7_0]);
                v13.add(v8_5.toString());
            }
            v7_0++;
        }
        SecurityException v0_8 = new String[] {"thumbnails", "videothumbnails"});
        int v15 = v0_8.length;
        int v11_0 = 0;
        while (v11_0 < v15) {
            int v18 = v11_0;
            String v4_8 = v12.query(v0_8[v11_0], new String[] {"_data"}), 0, 0, 0, 0, 0);
            if ((v4_8 != null) && (v4_8.moveToFirst())) {
                do {
                    v13.remove(v4_8.getString(0));
                } while(v4_8.moveToNext());
            }
            libcore.io.IoUtils.closeQuietly(v4_8);
            v11_0 = (v18 + 1);
        }
        String v4_4 = v13.iterator();
        while (v4_4.hasNext()) {
            try {
                new java.io.File(((String) v4_4.next())).delete();
            } catch (SecurityException v0) {
            }
        }
        android.util.Log.v(com.android.providers.media.MediaProvider.TAG, "/pruneDeadThumbnailFiles... ");
        return;
    }

Method java.io.File.<init>() not found.

[TAINT] String '/sdcard/DCIM/.thumbnails' ==>>> Sink '['Ljava/io/File;', '<init>', '(Ljava/lang/String;)V', '0', 'FILESYSTEM_ACCESS_SINK']' [[('Lcom/android/providers/media/MediaProvider;', 'delete', '(Landroid/net/Uri; Ljava/lang/String; [Ljava/lang/String;)I'), ('Lcom/android/providers/media/MediaProvider;', 'pruneThumbnails', '()V'), ('Ljava/io/File;', '<init>', '(Ljava/lang/String;)V')]]

Call to filesystem access method using a string path to external storage, like /sdcard/

Method com.android.providers.media.MediaProvider.delete():


    public int delete(android.net.Uri p40, String p41, String[] p42)
    {
        void v1 = this;
        android.net.Uri v11 = this.safeUncanonicalize(p40);
        android.database.sqlite.SQLiteDatabase v12_2 = com.android.providers.media.MediaProvider.URI_MATCHER.match(v11);
        android.database.sqlite.SQLiteDatabase v13_3 = 0;
        String v14_9 = 1;
        if (v12_2 != 500) {
            Throwable v0_10;
            if (v12_2 != 301) {
                if (v12_2 != 705) {
                    String v15 = com.android.providers.media.MediaProvider.getVolumeName(v11);
                    boolean v16 = "external".equals(v15);
                    android.database.sqlite.SQLiteDatabase v9_2 = this.getDatabaseForUri(v11);
                    if (v9_2 == null) {
                        int v38 = v12_2;
                        android.net.Uri v2_16 = new StringBuilder();
                        v2_16.append("Unknown URI: ");
                        v2_16.append(v11);
                        v2_16.append(" match: ");
                        v2_16.append(v38);
                        throw new UnsupportedOperationException(v2_16.toString());
                    } else {
                        android.database.sqlite.SQLiteDatabase v13_0;
                        android.database.sqlite.SQLiteDatabase v9_0;
                        String v14_0;
                        v9_2.mNumDeletes = (v9_2.mNumDeletes + 1);
                        String v8_14 = v9_2.getWritableDatabase();
                        android.media.MiniThumbFile v6_2 = this.getTableAndWhere(v11, v12_2, p41);
                        if (!v6_2.table.equals("files")) {
                            v14_0 = v6_2;
                            v13_0 = v9_2;
                            v9_0 = v8_14;
                        } else {
                            String v5_12 = v11.getQueryParameter("deletedata");
                            if ((v5_12 != null) && (v5_12.equals("false"))) {
                                v14_0 = v6_2;
                                v13_0 = v9_2;
                                v9_0 = v8_14;
                            } else {
                                v9_2.mNumQueries = (v9_2.mNumQueries + 1);
                                String v26 = v6_2;
                                android.database.sqlite.SQLiteDatabase v27 = v8_14;
                                android.database.sqlite.SQLiteDatabase v28 = v9_2;
                                android.net.Uri v2_35 = v8_14.query(v6_2.table, com.android.providers.media.MediaProvider.sMediaTypeDataId, v6_2.where, p42, 0, 0, 0);
                                String v3_26 = new String[] {""});
                                long v4_3 = new String[] {"", ""});
                                android.media.MiniThumbFile v6_1 = 0;
                                String v5_8 = 0;
                                try {
                                    while (v2_35.moveToNext()) {
                                        try {
                                            android.database.sqlite.SQLiteDatabase v9_4;
                                            android.database.sqlite.SQLiteDatabase v13_1;
                                            int v7_2 = v2_35.getInt(v13_3);
                                            String v8_3 = v2_35.getString(v14_9);
                                            long v29 = v2_35.getLong(2);
                                        } catch (Throwable v0_0) {
                                            android.database.sqlite.SQLiteDatabase v13 = v28;
                                            String v8_0 = v5_8;
                                            libcore.io.IoUtils.closeQuietly(v2_35);
                                            if (v6_1 != null) {
                                                v6_1.deactivate();
                                            }
                                            if (v8_0 != null) {
                                                v8_0.deactivate();
                                            }
                                            throw v0_0;
                                        }
                                        if (v7_2 != v14_9) {
                                            long v31_0 = v4_3;
                                            String v32_2 = v5_8;
                                            v9_4 = v27;
                                            v13_1 = v28;
                                            long v4_2 = v29;
                                            if (v7_2 != 3) {
                                                if (v7_2 != 2) {
                                                    v4_3 = v31_0;
                                                    v5_8 = v32_2;
                                                    v27 = v9_4;
                                                    v28 = v13_1;
                                                    v13_3 = 0;
                                                    v14_9 = 1;
                                                } else {
                                                    try {
                                                        if (v13_1.mInternal) {
                                                            v4_3 = v31_0;
                                                        } else {
                                                            com.android.providers.media.MediaDocumentsProvider.onMediaStoreDelete(this.getContext(), v15, 2, v4_2);
                                                            v3_26[0] = String.valueOf(v4_2);
                                                            v13_1.mNumDeletes = (v13_1.mNumDeletes + 2);
                                                            v9_4.delete("audio_genres_map", "audio_id=?", v3_26);
                                                            Throwable v0_27 = v9_4.query("audio_playlists_map", com.android.providers.media.MediaProvider.sPlaylistIdPlayOrder, "audio_id=?", v3_26, 0, 0, 0);
                                                            while(true) {
                                                                String v8_10 = v0_27;
                                                                if (!v8_10.moveToNext()) {
                                                                    break;
                                                                }
                                                                Throwable v0_32 = new StringBuilder();
                                                                v0_32.append("");
                                                                long v36 = v4_2;
                                                                v0_32.append(v8_10.getLong(0));
                                                                v31_0[0] = v0_32.toString();
                                                                Throwable v0_35 = new StringBuilder();
                                                                v0_35.append("");
                                                                v0_35.append(v8_10.getInt(1));
                                                                v31_0[1] = v0_35.toString();
                                                                v13_1.mNumUpdates = (v13_1.mNumUpdates + 1);
                                                                long v4_7 = v31_0;
                                                                v9_4.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1 WHERE playlist_id=? AND play_order>?", v4_7);
                                                                v31_0 = v4_7;
                                                                v0_27 = v8_10;
                                                                v4_2 = v36;
                                                            }
                                                            v4_3 = v31_0;
                                                            v9_4.delete("audio_playlists_map", "audio_id=?", v3_26);
                                                            libcore.io.IoUtils.closeQuietly(v8_10);
                                                        }
                                                    } catch (Throwable v0_0) {
                                                        String v14 = v26;
                                                        v8_0 = v32_2;
                                                    }
                                                }
                                            } else {
                                                try {
                                                    v1 = v1.deleteIfAllowed(v11, v8_3);
                                                    com.android.providers.media.MediaDocumentsProvider.onMediaStoreDelete(this.getContext(), v15, 3, v4_2);
                                                    v3_26[0] = String.valueOf(v4_2);
                                                    v13_1.mNumQueries = (v13_1.mNumQueries + 1);
                                                    Throwable v0_44 = v9_4.query("videothumbnails", com.android.providers.media.MediaProvider.sDataOnlyColumn, "video_id=?", v3_26, 0, 0, 0);
                                                } catch (Throwable v0_0) {
                                                    long v4 = v31_0;
                                                }
                                                while(true) {
                                                    String v14_7 = v0_44;
                                                    if (!v14_7.moveToNext()) {
                                                        break;
                                                    }
                                                    String v35 = v8_3;
                                                    v1 = v1.deleteIfAllowed(v11, v14_7.getString(0));
                                                    v0_44 = v14_7;
                                                    v8_3 = v35;
                                                }
                                                v13_1.mNumDeletes = (v13_1.mNumDeletes + 1);
                                                v9_4.delete("videothumbnails", "video_id=?", v3_26);
                                                libcore.io.IoUtils.closeQuietly(v14_7);
                                                if (v16) {
                                                    if (v32_2 != null) {
                                                        v8_0 = v32_2;
                                                    } else {
                                                        v8_0 = android.media.MiniThumbFile.instance(android.provider.MediaStore$Video$Media.EXTERNAL_CONTENT_URI);
                                                    }
                                                    try {
                                                        v8_0.eraseMiniThumb(v4_2);
                                                        v32_2 = v8_0;
                                                    } catch (Throwable v0_0) {
                                                        v4 = v31_0;
                                                    }
                                                }
                                                v4_3 = v31_0;
                                            }
                                            v8_0 = v32_2;
                                        } else {
                                            try {
                                                v1 = v1.deleteIfAllowed(v11, v8_3);
                                                long v31_1 = v4_3;
                                                String v32_3 = v5_8;
                                                long v4_8 = v29;
                                                try {
                                                    com.android.providers.media.MediaDocumentsProvider.onMediaStoreDelete(this.getContext(), v15, v14_9, v4_8);
                                                    v3_26[v13_3] = String.valueOf(v4_8);
                                                    v9_4 = v28;
                                                    try {
                                                        v9_4.mNumQueries = (v9_4.mNumQueries + v14_9);
                                                        Throwable v0_57 = v27.query("thumbnails", com.android.providers.media.MediaProvider.sDataOnlyColumn, "image_id=?", v3_26, 0, 0, 0);
                                                    } catch (Throwable v0_0) {
                                                        v14 = v26;
                                                        v8_0 = v32_3;
                                                    }
                                                    while(true) {
                                                        String v14_8 = v0_57;
                                                        if (!v14_8.moveToNext()) {
                                                            break;
                                                        }
                                                        try {
                                                            v1 = v1.deleteIfAllowed(v11, v14_8.getString(v13_3));
                                                            v0_57 = v14_8;
                                                            v14_8 = 1;
                                                        } catch (Throwable v0_62) {
                                                            libcore.io.IoUtils.closeQuietly(v14_8);
                                                            throw v0_62;
                                                        }
                                                    }
                                                    v9_4.mNumDeletes = (v9_4.mNumDeletes + 1);
                                                    android.database.sqlite.SQLiteDatabase v34_1 = v9_4;
                                                    v9_4 = v27;
                                                    v9_4.delete("thumbnails", "image_id=?", v3_26);
                                                    libcore.io.IoUtils.closeQuietly(v14_8);
                                                    if (v16) {
                                                        if (v6_1 == null) {
                                                            v6_1 = android.media.MiniThumbFile.instance(android.provider.MediaStore$Images$Media.EXTERNAL_CONTENT_URI);
                                                        }
                                                        v6_1.eraseMiniThumb(v4_8);
                                                    }
                                                    v4_3 = v31_1;
                                                    v5_8 = v32_3;
                                                    v13_1 = v34_1;
                                                } catch (Throwable v0_0) {
                                                    v14 = v26;
                                                    v4 = v31_1;
                                                    v8_0 = v32_3;
                                                }
                                            } catch (Throwable v0_0) {
                                                v14 = v26;
                                                v8_0 = v5_8;
                                            }
                                        }
                                    }
                                } catch (Throwable v0_0) {
                                    v8_0 = v5_8;
                                    android.database.sqlite.SQLiteDatabase v9 = v27;
                                }
                                String v32_0 = v5_8;
                                v9_0 = v27;
                                v13_0 = v28;
                                libcore.io.IoUtils.closeQuietly(v2_35);
                                if (v6_1 != null) {
                                    v6_1.deactivate();
                                }
                                if (v32_0 != null) {
                                    v32_0.deactivate();
                                }
                                v14_0 = v26;
                                if (android.text.TextUtils.isEmpty(v14_0.where)) {
                                    v14_0.where = "_id NOT IN (SELECT parent FROM files)";
                                    switch (v12_2) {
                                        case 3:
                                        case 4:
                                        case 202:
                                        case 203:
                                            android.database.sqlite.SQLiteDatabase v12_0 = v9_0;
                                            android.net.Uri v2_8 = v9_0.query(v14_0.table, com.android.providers.media.MediaProvider.sDataOnlyColumn, v14_0.where, p42, 0, 0, 0);
                                            if (v2_8 != null) {
                                                try {
                                                    while (v2_8.moveToNext()) {
                                                        v1 = v1.deleteIfAllowed(v11, v2_8.getString(0));
                                                    }
                                                } catch (Throwable v0_16) {
                                                    libcore.io.IoUtils.closeQuietly(v2_8);
                                                    throw v0_16;
                                                }
                                                libcore.io.IoUtils.closeQuietly(v2_8);
                                            }
                                            v13_0.mNumDeletes = (v13_0.mNumDeletes + 1);
                                            v0_10 = v12_0.delete(v14_0.table, v14_0.where, p42);
                                            break;
                                        case 108:
                                            v13_0.mNumDeletes = (v13_0.mNumDeletes + 1);
                                            v0_10 = v9_0.delete("audio_genres_map", v14_0.where, p42);
                                            android.database.sqlite.SQLiteDatabase v12 = v9_0;
                                            break;
                                        case 702:
                                        case 703:
                                            v13_0.mNumDeletes = (v13_0.mNumDeletes + 1);
                                            v0_10 = v9_0.delete("files", v14_0.where, p42);
                                            break;
                                        default:
                                            android.database.sqlite.SQLiteDatabase v12_1 = v9_0;
                                            v13_0.mNumDeletes = (v13_0.mNumDeletes + 1);
                                            v0_10 = v12_1.delete(v14_0.table, v14_0.where, p42);
                                    }
                                    android.net.Uri v2_12 = new StringBuilder();
                                    v2_12.append("content://media/");
                                    v2_12.append(v15);
                                    this.getContext().getContentResolver().notifyChange(android.net.Uri.parse(v2_12.toString()), 0);
                                    return v0_10;
                                } else {
                                    Throwable v0_5 = new StringBuilder();
                                    v0_5.append("(");
                                    v0_5.append(v14_0.where);
                                    v0_5.append(") AND (_id NOT IN (SELECT parent FROM files WHERE NOT (");
                                    v0_5.append(v14_0.where);
                                    v0_5.append(")))");
                                    v14_0.where = v0_5.toString();
                                }
                            }
                        }
                    }
                } else {
                    try {
                        if (this.mMtpService == null) {
                        } else {
                            this.getContext().unbindService(this.mMtpServiceConnection);
                            v13_3 = 1;
                            this.mMtpService = 0;
                        }
                    } catch (Throwable v0_66) {
                        throw v0_66;
                    }
                    v0_10 = v13_3;
                }
            } else {
                this.detachVolume(v11);
                v0_10 = 1;
            }
            return v0_10;
        } else {
            if (this.mMediaScannerVolume != null) {
                android.net.Uri v2_24 = new StringBuilder();
                v2_24.append("content://media/");
                v2_24.append(this.mMediaScannerVolume);
                v2_24.append("/audio");
                android.net.Uri v2_27 = this.getDatabaseForUri(android.net.Uri.parse(v2_24.toString()));
                if (v2_27 != null) {
                    v2_27.mScanStopTime = android.os.SystemClock.currentTimeMicro();
                    com.android.providers.media.MediaProvider.logToDb(v2_27.getWritableDatabase(), this.dump(v2_27, 0));
                } else {
                    long v4_12 = new StringBuilder();
                    v4_12.append("no database for scanned volume ");
                    v4_12.append(this.mMediaScannerVolume);
                    android.util.Log.w(com.android.providers.media.MediaProvider.TAG, v4_12.toString());
                }
                if ("internal".equals(this.mMediaScannerVolume)) {
                    long v4_16 = this.getContext().getSharedPreferences("MediaScanBuild", 0).edit();
                    v4_16.putString("lastScanFingerprint", android.os.Build.FINGERPRINT);
                    v4_16.apply();
                }
                this.mMediaScannerVolume = 0;
                this.pruneThumbnails();
                return 1;
            } else {
                return 0;
            }
        }
    }

Method com.android.providers.media.MediaProvider.pruneThumbnails():


    private void pruneThumbnails()
    {
        android.util.Log.v(com.android.providers.media.MediaProvider.TAG, "pruneThumbnails ");
        android.database.sqlite.SQLiteDatabase v12 = this.getDatabaseForUri(android.provider.MediaStore$Images$Thumbnails.getContentUri("external")).getWritableDatabase();
        v12.execSQL("delete from thumbnails where image_id not in (select _id from images)");
        v12.execSQL("delete from videothumbnails where video_id not in (select _id from video)");
        java.util.HashSet v13 = new java.util.HashSet();
        try {
            String v4_2 = new java.io.File("/sdcard/DCIM/.thumbnails").getCanonicalFile();
            String v5_0 = v4_2.list();
        } catch (SecurityException v0) {
            return;
        }
        if (v5_0 == null) {
            String[] v6_0 = new String[0];
            v5_0 = v6_0;
        }
        String[] v6_1 = v4_2.getPath();
        int v7_0 = 0;
        while (v7_0 < v5_0.length) {
            if (v5_0[v7_0].endsWith(".jpg")) {
                int v8_5 = new StringBuilder();
                v8_5.append(v6_1);
                v8_5.append("/");
                v8_5.append(v5_0[v7_0]);
                v13.add(v8_5.toString());
            }
            v7_0++;
        }
        SecurityException v0_8 = new String[] {"thumbnails", "videothumbnails"});
        int v15 = v0_8.length;
        int v11_0 = 0;
        while (v11_0 < v15) {
            int v18 = v11_0;
            String v4_8 = v12.query(v0_8[v11_0], new String[] {"_data"}), 0, 0, 0, 0, 0);
            if ((v4_8 != null) && (v4_8.moveToFirst())) {
                do {
                    v13.remove(v4_8.getString(0));
                } while(v4_8.moveToNext());
            }
            libcore.io.IoUtils.closeQuietly(v4_8);
            v11_0 = (v18 + 1);
        }
        String v4_4 = v13.iterator();
        while (v4_4.hasNext()) {
            try {
                new java.io.File(((String) v4_4.next())).delete();
            } catch (SecurityException v0) {
            }
        }
        android.util.Log.v(com.android.providers.media.MediaProvider.TAG, "/pruneDeadThumbnailFiles... ");
        return;
    }

Method java.io.File.<init>() not found.