Skip to main content
  1. Posts/

Android - Storage

·8 mins
mobile android
Table of Contents

We will look at how android applications store files and further have access to the file system of the device. We will focus mostly around the internal storage, external storage, and the keychain. Internal storage also contains things we are already familiar with like shared preferences, the cache files, and databases - some of the things discussed here will tie back to Content Providers, so keep that in mind as we continue.

Internal Storage
#

When we talk about internal storage, we are usually referring to areas within the file system of the device that are exclusive to the application and are used to store private application data. (One example might be a messaging app storing your sent attachments.) Typically not even the user can access this on a non-rooted device.

When we talk about the private data of an application, this is typically in the /data/data/[APK_PATH]/ directory. This is where you’ll probably find other directories and files like cache, files, shared_prefs, and so on. Of course, you need to use a rooted device to actually read these directories.

Luckily, we can use Objection to patch an APK, allowing us to still dump files from it and access the private internal storage of the applications.

The /cache directory is often used to store temporary files that aren’t persistently needed. This directory might get automatically cleared by the system under certain circumstances. The application context provides helper functions like getCacheDir() to resolve the path to the internal directory.

The ./files directory of an application can be used to store whatever the developer wants, but this is sometimes used to store private application data that the application may not want the user to access. In your app you could use openFileOutput() to create a file in the internal storage - by default in the /data/data/[APK_NAME]/files/ directory.

The shared preferences are essentially a key-value store - they often times store information like settings, API keys, passwords, and so on. Despite being called shared preferences, these are not shared across applications and they are unique to each application, but they are shared across processes and threads. These are stored in an XML file in the ./shared-prefs/ directory.

Internal databases typically use SQLite3 databases because android natively supports these. You can create one from your app by calling openOrCreateDatabase() and you can execute SQL statements against it. This isn’t an SQL server, just a file in the file system of the application. You can easily browse these using sqlite3, which is usually installed by default on the command line for android devices.

External Storage
#

The way we talk about external storage has changed a lot since smart phones have been in the market. We used to mostly talk about external storage as SD cards, but most phones now have enough space on the internal drive or don’t allow for SD cards to be used to expand existing storage - this means that most of the time the external storage is a part of the internal drive of the device. Most applications still use this for storing larger files like photos and videos. In the past, android applications typically had wide access to external storage but that has changed in more recent years.

External storage is still used to contain private app directories and shared data and we can look at this in the /sdcard directory of an android file system.

emu64x:/sdcard # ls -lah
total 104K
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 Alarms
drwxrws--x 5 media_rw media_rw 4.0K 2024-10-26 15:26 Android
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 Audiobooks
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 DCIM
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-12-23 20:42 Documents
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-12-23 20:58 Download
drwxrws--- 3 u0_a120  media_rw 4.0K 2024-10-26 15:26 Movies
drwxrws--- 3 u0_a120  media_rw 4.0K 2024-10-26 15:26 Music
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 Notifications
drwxrws--- 4 u0_a120  media_rw 4.0K 2024-11-04 22:02 Pictures
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 Podcasts
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 Recordings
drwxrws--- 2 u0_a120  media_rw 4.0K 2024-10-26 15:26 Ringtones

The only unclear directory here might be DCIM which is for digital camera images, we also have the /Android directory. This is not a real SD card as we know and we can observe this by looking at that directory:

emu64x:/ # ls -lah sdcard                                                                                            
lrw-r--r-- 1 root root 21 2023-12-16 17:01 sdcard -> /storage/self/primary

emu64x:/ # cd /storage

emu64x:/storage # ls self/primary/                                                                                   
Alarms   Audiobooks  Documents  Movies  Notifications  Podcasts    Ringtones
Android  DCIM        Download   Music   Pictures       Recordings
emu64x:/storage # ls emulated/0                                                                                      
Alarms   Audiobooks  Documents  Movies  Notifications  Podcasts    Ringtones
Android  DCIM        Download   Music   Pictures       Recordings

The /storage directory in my case has the emulated and self directories. In a real device the /self directory would be linked to /mnt/user/0/primary, but in my case they are linked to /storage/emulated/0 because I am running off of an emulator.

When android was first released it would allow all access to the /sdcard directory for applications. Then after Android 4.4 there were some write limitations. Android 5 added the Storage Access Framework, which we looked at while messing with content providers. Android 6 added additional runtime permissions and Android 10 added scoped storage. Android 11 essentially enforced the changes made to scoped storage from Android 10.

As you might expect, this means that vulnerabilities are very different between Android Versions. So when you think something might be exploitable, especially when related to reading sensitive files, try it on older versions of android that the target app still functions on.

Since Android 10, we saw the Android directory, which contains the /data and /obb directories - obb is used to store very large assets and data is used to store the private directories for apps. On older versions of Android, most apps had the READ_EXTERNAL_STORAGE permissions, which was problematic because apps could read each other’s information.

Scoped storage restricts each application to their own directory to prevent this privacy issue. If your target app runs android 10 or older, you might be able to bypass scoped storage protections.

Android 11 and up do enforce scoped storage, but the permission MANAGE_EXTERNAL_STORAGE essentially lets an application bypass the scoped storage limitations. The purpose of this permission is for apps like antivirus and search tools to be able to look through all files within the external storage. Use of this is regulated in the play store and even if you side load an application, you will need to explicitly allow the application to read files with a runtime permission - where the user is presented with a dedicated warning screen.

The Keychain
#

This isn’t so much of file storage in the same way we have talked about, in the keychain we can store cryptographic key materials such as private keys etc. The keychain is often protected by some hardware-backed security component within the device. The keychain is not used to store passwords themselves, it only stores keys - we can however use the keychain to interact with keys which have the ability to decrypt other pieces of data.

Omni-Notes Example
#

We will be taking a close look at the process of exploiting CVE-2023-33188, which was a vulnerability discovered in the Omni-Notes Android app. The app’s purpose is to provide generic note-taking features, specifically for those who prefer to use older versions of Android. We will be using Android 10 to examine this vulnerability and try to extrapolate on how it may have been discovered. At the time of writing this post, Android 10’s last security update was February 2023, but the Google Play Services for this device have been updates as recently as December 2024.

Basic Functionality
#

This app is, as we covered, used to take basic notes. We can use the knowledge we gained earlier to determine where this note might be on the Android file system:

storage-1

Something to keep in mind is that Omni-Notes supports the functionality to receive notes and files from other applications. For example, we can share a picture from the gallery/camera roll to the Omni-Notes app and it will create a note:

storage-2
storage-3

If we take a look at where this image is stored, you can observe that it is in the storage directory, in the external storage section:

storage-4

We also know that some other applications with certain permissions might be able to access all the files on the external storage. So - if we send an intent with the correct type to the Omni-Notes app, while including a file path, Omni-Notes will try to attach it to our note. This might not make much sense now so let’s make an app that can perform this task for us.

You can use the provided skeleton-code that is given in the hextree.io course and add the following to the exploit() method:

try {  
    Intent intent = new Intent();  
    intent.setAction(Intent.ACTION_SEND); //This is the common intent action used to share  
    intent.setType("image/*");  
    intent.putExtra(Intent.EXTRA_TEXT, "Malicious note");  
    intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:///storage/emulated/0/Android/data/it.feio.android.omninotes/files/20250109_110146_595.jpg"));  
    //We need to use extra_stream and we can just paste in the URL from the data base and our file name
    intent.setClassName("it.feio.android.omninotes", "it.feio.android.omninotes.MainActivity");  
    //To make sure we don't need to select a receiver, we can just specify it manually  
    startActivity(intent);  
} catch (Exception e){  
    Log.e("Exploit","Failed to send malicious Intent",e);  
}

When we run the app and hit the button we will see that another note is created as we specified in the intent:

storage-5

We can also observe that a copy of the previous image was created:

storage-6

This basically means that just by sending an intent, we can get Omni-Notes to send any file that it has access to into the semi-public sdcard directory. We can try to trick Omni-Notes into copying its entire database to the sdcard. First we replace the path that goes to the image from earlier to the path of the database itself.

intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:///data/data/it.feio.android.omninotes/databases/omni-notes"));

Running the app at this point results in the entire database being copied to the sdcard where other apps would be able to access it. You could even make a proof of concept stealer app that sends this intent then stores that stolen data it in its own app directory.

Related

Android - Services
·12 mins
mobile android
What is a Service? # A service is defined as an application component that can perform long-running operations in the background, not providing a user interface.
Android - Permissions
·7 mins
mobile android
Overview # So far we have only examined whether or not apps are exported.
Android - Broadcast Receivers
·10 mins
mobile android
What is a Broadcast Receiver? # Android applications can send and receive broadcast messages from both the operating system and other android applications.