Ghost files in the shared preferences
Have you ever encountered an exceptionally clever bug, only to be thwarted by an unforeseen obstacle just moments before exploiting it? Perhaps a check, initially designed for another purpose, now inadvertently blocks you from leveraging this significant bug you’ve discovered?
That’s precisely what this post aims to explore….
The bug ? what bug 🕷️?
Numerous bug categories can enable you to circumvent WRITE restrictions in an application’s home directory, sparking considerable excitement due to the typically high impact of such attacks. For example, achieving code execution through overwriting a native library can lead to significant repercussions, among other potential exploits.
Even in the absence of a native library, numerous avenues remain for exploiting such a bug, with the shared preferences directory being a prime target. Android applications often store connection settings within the shared_prefs
directory. Should you succeed in overwriting a trusted domain within this space, you could redirect the application to communicate with a malicious server. This could lead to the unintended transmission of user tokens and other sensitive information directly to an attacker-controlled environment.
Write is not always … Overwrite
Many apps implement checks prior to overwriting a file; it’s quite typical, for instance, to verify the file’s existence. If the file exists, they might either halt the operation altogether or prompt for user consent for overwriting it. This requirement for user interaction can elevate the user-interaction metric of a CVE (Common Vulnerabilities and Exposures) and consequently reduce the overall CVE score, impacting the perceived severity of the vulnerability.
This is exactly the obstacle that I encountered in numerous cases while trying to overwrite a file…
if(file.exists()) abort();
And this is how it feels….
…and I might have given up, if it wasn’t this great file monitoring medusa module called file_write…without any direct intervention on my part, I observed the appearance of unusual .bak
files within the shared preferences directory. The answer to the purpose of these mysterious .bak
files lies within the implementation of the shared preferences class:
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
startLoadFromDisk();
}
Notice the makeBackupFile
, which simply returns a new file with the .bak extention:
static File makeBackupFile(File prefsFile) {
return new File(prefsFile.getPath() + ".bak");
}
Let’s say you are using the example.xml
file , then this one will return example.xml.bak
. Now take a look below:
The StartLoadFromDisk
at the end of the constructor, calls the loadFromDisk
, which checks if the example.xml.bak
exists and if it does, it deletes the example.xml
and renames the example.xml.bak
to example.xml
Write is always … Overwrite (when it comes to shared_prefs)
I guess, it’s clear where this is leading… Suppose you have WRITE but not OVERWRITE in the shared preferences directory. Instead of attempting to write the file directly, you could simply create a .bak
file and allow the behavior described previously to work in your favor. This approach leverages the inherent handling of .bak
files by the shared preferences mechanism to indirectly achieve file modification, circumventing the restriction on direct overwriting ;)