This is private device storage where apps save data that only they can access. It’s used to store private app data such as sensitive information, configurations, databases, temporary cache files, or anything that should not be accessed by the user or other apps.
Location: /data/data/package-name/
or /data/user/0/package-name/
Permissions needed: None
Access:
Lifecycle: when the user uninstalls your app, the system automatically deletes all files in this directory
In Android terminology, “external storage” does not necessarily mean a physical SD card. It means: “storage that’s accessible to both apps and the user (shared, public area)”.
Location:
/storage/emulated/0): this is the storage that most
users think of as their phone’s “internal storage” for files,
photos, and downloadsNote: the “/0” represents the user ID. Android has a multi-user system. The primary user of the phone is user 0
Note: the
/sdcardpath is an alias (shortcut) that points to your device’s primary external storage.ls -l / [...] lrw-r--r-- 1 root root 21 2023-05-04 18:16 sdcard -> /storage/self/primary [...] ls -l /storage/self/primary lrwxrwxrwx 1 root root 19 2025-10-10 10:00 /storage/self/primary -> /storage/emulated/0
/storage/XXXX-XXXX): this refers to any physical,
removable storage volume that the user has inserted into the
deviceLocation:
<EXTERNAL_STORAGE>/Android/data/com.package.name/<EXTERNAL_STORAGE>/Android/obb/com.package.name/Permissions needed: None
Access from Android 11:
/Android/obb or
Android/data directories. [↗]Android/data
folderLifecycle: when the user uninstalls your app, these directories and all their contents are also deleted
Tip: if you want to manage files in these folder you can use the system file picker app. To open it you can:
download Marc Files which is just a shortcut to the hidden android system file picker/manager
or send one on the following intent:
# First try am start -a android.intent.action.VIEW -n com.google.android.documentsui/com.android.documentsui.files.FilesActivity # Second try am start -a android.intent.action.VIEW -n com.android.documentsui/com.android.documentsui.files.FilesActivity # Third try am start -a android.intent.action.VIEW -n com.android.documentsui/com.android.documentsui.FilesActivity
This is the rest of external storage. It’s the public space intended for files that the user expects to be able to access directly and share between apps.
Location (from Android 11):
DCIM/,
Pictures/, Music/,
Downloads/, etc.<EXTERNAL_STORAGE>/Android/media/com.package.name/Here we can store cryptographic keys like private keys. On most devices, the keychain is protected in hardware by special security chips. It does not store password, but only cryptographic keys.
You need to analyze both internal and external storage.
Storing sensitive data in external storage can expose it to users or malicious actors. On Android versions below 11, any app with the appropriate storage permission could freely read data stored in external storage, including other apps’ files. Although Android 11 and higher introduced scoped storage to restrict this access, sensitive data stored externally can still be exposed if an attacker gains physical access to the device (for example, by connecting it to a computer).
# External storage
<EXTERNAL_STORAGE>/Android/data/com.package.name/
<EXTERNAL_STORAGE>/Android/obb/com.package.name/
# Data app location folder
/data/data/<package_name>To monitor the storage you can use fsmon or frida by using the following script.
Credits: OWASP
If you need to monitor changes in internal storage, update the
corresponding value in external_paths.
Note: the script monitor even
ContentResolver.insert()because files created via this method are managed by MediaStore ascontent://URIs (not file paths), so they can’t be opened with libcopen(). [↗]
function printBacktrace(maxLines = 8) {
Java.perform(() => {
let Exception = Java.use("java.lang.Exception");
let stackTrace = Exception.$new().getStackTrace().toString().split(",");
console.log("\nBacktrace:");
for (let i = 0; i < Math.min(maxLines, stackTrace.length); i++) {
console.log(stackTrace[i]);
}
});
};
// Intercept libc's open to make sure we cover all Java I/O APIs
Interceptor.attach(
Process.getModuleByName('libc.so').getExportByName('open'),
{
onEnter: function(args) {
const external_paths = ['/sdcard', '/storage/emulated'];
const path = args[0].readCString();
external_paths.forEach(external_path => {
if (path.indexOf(external_path) === 0) {
console.log(`\n[*] open called to open a file from external storage at: ${path}`);
printBacktrace(15);
}
});
}
}
);
// Hook ContentResolver.insert to log ContentValues (including keys like _display_name, mime_type, and relative_path) and returned URI
Java.perform(() => {
let ContentResolver = Java.use("android.content.ContentResolver");
ContentResolver.insert.overload('android.net.Uri', 'android.content.ContentValues').implementation = function(uri, values) {
console.log(`\n[*] ContentResolver.insert called with ContentValues:`);
console.log(`\t_display_name: ${values.get("_display_name").toString()}`);
console.log(`\tmime_type: ${values.get("mime_type").toString()}`);
console.log(`\trelative_path: ${values.get("relative_path").toString()}`);
let result = this.insert(uri, values);
console.log(`\n[*] ContentResolver.insert returned URI: ${result.toString()}`);
printBacktrace();
return result;
};
});On Android, logging APIs like can accidentally expose sensitive
data. Logs go to logcat, which since Android 4.1 is accessible only
to system apps with READ_LOGS.
However, many pre-installed apps hold this privilege, creating a
risk of data leaks. For this reason, directly logging sensitive
information to logcat is discouraged. [↗]
# Open the app and then run this command
adb logcat --pid <PID>It is often better to run adb --clear beforehand to
ensure a cleaner environment.
# Start objection
objection -g 'exampleapp' explore
# Search a specific string
memory search <input_string> --string
# Dump all and then extract strings
memory dump all appMemoryDump
strings appMemoryDump > appMemoryDump.txt