Android attacks – information leakage from file intents

In the last few weeks, I’ve uncovered a number of issues in apps which I would describe as information leakage of private directory. Here’s what that means, and here’s how to stop it

Intents to information leaking

This can be quite a subtle bug and depends a lot on what your application does. Take an exposed intent that is meant to share data, that is defined like this:

<activity android:name=".intents.ShareFileActivity">
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>

And lets say the code is like this:

if (intent.action == Intent.ACTION_SEND) {
val uri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as Uri
val fileToUse = contentResolver.openInputStream(uri)

Obviously this depends somewhat on what your ‘send file somewhere’ function does, but one thing you probably don’t want to do (in most cases) is load and send a file from your “private” app directory — which might contain account credentials, databases (etc).

Like this example here, where an attacker was able to upload a users account details:

Fixing the issue

The obvious thing to do, is not allow files in your private directory to be sent, but this is trickier than you might thing. As the above example shows, you need to think about symlinks, as well as directory traversal and a handful of other edge cases.

In this case, we can use a library — safe to run — to help us validate files.

Let’s first add the library to build.gradle (Latest version can be found here:

implementation "io.github.dllewellyn.safetorun:safetorun:1.0.7"
implementation "io.github.dllewellyn.safetorun:safeToRunCore:1.0.7"
implementation "io.github.dllewellyn.safetorun:inputverification:1.0.7"

Going back to our example before, we can use safe to run to ‘verify’ the URI before we use it.

if (uri.verifyFile(context) { }) {val fileToUse = contentResolver.openInputStream(uri)

That’s actually it for this example. We can be more permissive than this however. Say we want to allow a particular file

val isFileSafeToOpen = uri.verifyFile(this) {File(context.filesDir, "safe_to_read.txt").allowExactFile()}

Or, if we want to add a directory:

uri.verifyFile(this) {addAllowedParentDirectory(context.filesDir.allowDirectory())}

Including subdirs:

uri.verifyFile(this) {addAllowedParentDirectory(context.filesDir.allowDirectoryAndSubdirectories())


Verifying URIs and Files before using them is important to prevent the occurrence of subtle and difficult to track bugs. There are a number of ways which basic file checks can be bypassed, and so using a well-tested library like safe to run as well as using a “default deny” approach is the best way of protecting yourself from these types of attacks.




Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

NVIDIA SHIELD update adds support for more controllers, new security patch

NVIDIA SHIELD update adds support for more controllers, new security patch

Integrating AppGallery Connect Crash in a Xamarin app for Android

How to make Android Studio look Awesome!

Sensitive Android Permissions when publishing on Huawei AppGallery

Surface DUO: how dual-screen devices work in practice

Toss a Coin to your Widget! Or don’t… — Part 2 of 3

A descriptive article image showing a blurred IDE, coins, a smartphone with an installed Home screen widget and a screaming emoji

👨🏼‍💻How to use OAID with AppsFlyer?

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Daniel Llewellyn

Daniel Llewellyn

More from Medium

Firebase Remote Config for app updates in Android

Building Android App Bundle with Jenkins

Implementing TLS Certificate Checking in Android Apps

Password protected setting in Android