Content and FileProvider
Android has an interesting feature to share data and files with other apps in a secure way. But this also increases the attack surface and there are a few pitfalls developers run into.
Cursor
A Cursor in Android is an interface that provides read-only, forward-only access to a set of data retrieved from a database, such as the contacts stored on the device. It acts as a pointer to rows in the result set of a query.
Content Provider
What is a Content Provider?
A Content Provider is an Android IPC mechanism that allows apps to securely share structured data with other apps. It acts like a database API, allowing apps to query, insert, update, and delete data using URIs.
Example Use Cases:
Contacts (
ContactsContract.Provider)Media files (Photos, Videos)
Call logs, Messages
Custom app data sharing
Content Providers are identified and accessed with a content:// URI.
Using the getContentResolver().query() method that URI can be querried.
this query returns data that is a table structure that can be explored using the Cursor object.
Using content provider and cursor to access contacts
ContactsContract.RawContacts.CONTENT_URIis actually a URI in android i.e.
First part is scheme of content provider i.e.
content://secodn part is package name
com.android.contacts also called authority.
Android content providers are also implemented using android IPC binder mechanism.
for above example. content provider app for contacts in our phone is com.android.providers.contacts
on decompiling it we can see it registers content provider.
This Content Provider is responsible for storing, managing, and providing access to contacts.
It uses the authority
"contacts;com.android.contacts", meaning multiple URIs can be used:content://contacts/people(deprecated)content://com.android.contacts/contactscontent://com.android.contacts/raw_contactscontent://com.android.contacts/data
It allows apps with the correct permissions to read (
READ_CONTACTS) and write (WRITE_CONTACTS) contacts.and it's main logic is in class
ContactsProvider2
as we queries content provider there is query method defined in ContactsProvider2 class to handle query. similarly there is methods for updating, writing, deleting data etc.
e.g. getContentResolver().query, getContentResolver().update, getContentResolver().delete etc.
Lots of ContentProviders are backed by a SQLite database,
so any of your action is directly mapped to a SQL query which performs SQL commands to make changes in sqlite database.
Now lets' look at challenge, we have a provider aith authority io.hextree.flag30that is exported and mapped to Flag30Providerclass.
Let's look at that class
As you can see query method is overridden and has custom logic to handle incoming uri.
In order to reach success function we have just pass this check uri.getPath().equals("/success")
Which means our exploit just have to include /successin path.
Note: if you get this error
It is a security feature called package visibility more here: https://developer.android.com/training/package-visibility
and to resolve this you just have to declare in manifest we are querying with this app. As we did previously in services section.
Content Providers can use a UriMatcher to route the incoming queries to different methods. e.g.
You can see first provider class creates URI matcher which assigns different number to all possible path values.
uriMatcher2.addURI(AUTHORITY, "flag/#", 2); here # is placeholder for integer values. which means a path like content://authoritu/flag/123 will match with second URI matcher
long parseId = ContentUris.parseId(uri); extracts integer ID part from URI.
so, solution becomes
Note: same as broadcast, services content providers also doesn't allow us to launch activity from background to not disturb user UI experience. Unless app was recently opened .
more: https://developer.android.com/guide/components/activities/background-starts
As you have ssen already user supplied data is passed to SQL query, what does that hint sql injeciton. let's look at this challenge
if path matches /flags it goes on to create sql query String str3 = "visible=1" + (str != null ? " AND (" + str + ")" : ""); and then executes it to retrieve all flags that are visibile =1 . where str is user controlled selectionparameter in query() that we send.
so solution
query that looks like:
After injection, the query becomes:
which then returns flag with visible as value 0 too.
and success methos is called.
grantUriPermissions
The android:grantUriPermissions="true" attribute in the <provider> tag allows the content provider to grant URI-based permissions to other apps, meaning that you can give specific external applications access to particular URIs within your content provider, even if the provider itself is not exported to the outside world.
When you set android:grantUriPermissions="true", you're allowing external applications (other apps besides your own) to access specific data within your content provider, but only for particular URIs that you explicitly share.
How It Works:
URI Permissions: These are specific permissions granted for accessing certain content within a content provider using URIs. For instance, you might want to allow another app to access just one table or one type of data in your provider without giving them full access to everything.
e.g.
this activity recive an intent , modify it with URI of a ocntent provider and add a permission for receiving app. i.e. intent.addFlags(1);which allows receiving app to access resource at content://io.hextree.flag33_1/flags flag 1 is Intent.FLAG_GRANT_READ_URI_PERMISSION this is a temporary permission.
When you use Intent.FLAG_GRANT_READ_URI_PERMISSION (or FLAG_GRANT_WRITE_URI_PERMISSION for write access), you're telling Android to allow the external app to access the resource specified by the URI and only that resource.
code of content provider class.
in order to reach success we need to include all flag from database that has name flag33.
however that is present in different table. we will need some sql injection also
here we send the intent receive the content provider URI with permission and send sql payload along with URI.
target application can send implicit intent too with content provider URI and content provider logic is same as last one.
in this case we just have to register a listener
note: without scheme or hostname os won't see your app as correct lsitener. Inititlaly i was doing intent action filter and i was getting error. then including atleast scheme solved it.
code
Take a look at this intent reflection vulnerability.
when else block executes it sends back the same intent that it recived. in this scenario this target app had access to contacts. which opens a door for a malicious app to access contacts without asking for that permission by just sending a intent with content provider URI and permission flag to this activity.
when it sends back that result intent, it will have temporary permission to access contacts too.
e.g.
here we send the content provider URI and permission that we want in our intent . which is reflected back by our target app on receiving it we can query contacts and dump .
FileProvider
Content resolvers are not just used for accessing database content. They can be used to share files in memory with other apps too.
One example is https://developer.android.com/training/data-storage/shared/photopicker#java
using this an app can ask for specific images instead of taking access of whole gallery. and os returns access to particular image only.
Android Jetpack (or androidx) is a commonly used official library implementing lots of useful classes. Including the widely used FileProvider.
Such a provider can easily be identified in the Android manifest where it references the androidx.core.content.FileProvider name.
Notable is the referenced XML file filepaths.xml which contains the configuration for this FileProvider.
A typical URI generated for this FileProvider could look like this content://io.hextree.files/other_files/secret.txt. Where the sections can be read like so:
content://it's a content providerio.hextree.filesthe authority from the android manifestother_fileswhich configuration entry is used
e.g
In this activity you can see on create, a content provider uri is created depending upon what we send as stringextra or do not set any extra. and that URI is sent back as result. with following permission
Read the file (
FLAG_GRANT_READ_URI_PERMISSION).Write to the file (
FLAG_GRANT_WRITE_URI_PERMISSION).
How it works?
getFilesDir() resolve to every app's internal file directory. e.g.
/data/data/io.hextree.attacksurface/filesnew FIle(getFiledir(), secret.text)Creates a
Fileobject that represents the file located inside the app’s internal storage. e.g. `/data/data/io.hextree.attacksurface/files/secret.txt`Calling
FileProvider.getUriForFile()This method takes:
Context (
this) → Needed to retrieve app-specific resources.Authority (
"io.hextree.files") → Must match the provider defined inAndroidManifest.xml.File object (
new File(getFilesDir(), stringExtra)) → Represents the actual file to be shared.
Finding the Corresponding
FileProviderAndroid checks the authority (
"io.hextree.files") against providers declared inAndroidManifest.xml.If it finds a
FileProvidermatching the given authority, it loads its associated file paths fromres/xml/file_paths.xml.
xml_path xml file:
the app defines which directories can be exposed via FileProvider.
"path='.'"→ Means all files insidegetFilesDir()are accessible.
Generating content URI:
will become
and indeed that's what we get as result.
we need to read flag so we set the filename as extra and receive the uri and read the content from memory.
this returns a content URI content://io.hextree.files/flag_files/flag34.txt which is mapped for /data/data/io.hextree.attacksurface/files/flags/flag34.txt
whose content we can read.
Insecre root path
Compare the filepaths.xml to the rootpaths.xml file provider configuration. Why is the <root-path> considered "insecure"?
filepaths.xml
Remember that the file provider configuration is used to generate file sharing URIs such as content://io.hextree.files/other_files/secret.txt. These sections can be read like so:
content://it's a content providerio.hextree.filesthe authority from the android manifestother_fileswhich configuration entry is used/secret.txtthe path of the file relative to the configuredpathin the .xml file
rootpaths.xml
The file provider with a <root-path> configuration will generated URIs like this content://io.hextree.files/root_files/data/data/io.hextree.attacksurface/files/secret.txt. If we decode these sections we can see that this provider can map files of the entire filesystem
content://it's a content providerio.hextree.rootthe authority from the android manifestroot_fileswhich configuration entry is used/data/data/io.hextree.attacksurface/files/secret.txtthe path of the file relative to the configuredpath, which is mapped to the filesystem root!
In itself the <root-path> configuration is not actually insecure, as long as only trusted files are shared. But if the app allows an attacker to control the path to any file, it can be used to expose arbitrary internal files.
which allows us to do file path traversal if path is attacker controlled
Here we tried reading a file that is outside the directory which getfilesdir() returns.
and we get access to arbitrary files.
Arbitrary file write
As shown before we also get write permission to content URI, which means we can update arbitrary files. But general app security applies here too, an app in android doesn't have permission to write files everywhere like /etc/hosts so you can't overwrite that.
This can be used if you know (from you analysis )if target app uses some .sofiles which can be overwritten or any other imporatant files.
FileProvider Receivers
Apps not always provide access to files using content URI, they can also receive data using content URI. e.g. an app can send content provider URI to target app and target app tries to import URI data to its' internal files .
e.g.
This app recives the intent and extracts the content provider URI and query it and tries to fetch _display_name & _size metadata .
then app move forward to read content from content provider URI and matches it to `give flag`.
We can write our app which implements this contentprovider and send back result.
First create a content provider class

it will write chnages to manigfest file
Setting
android:exported="true"means any app can access your provider.This allows any app to query and read your data without needing explicit permission.
we didn't specify:
This means that Android allows unrestricted access to your provider as long as it's exported.
content provider class
We implemented two methods to respond to queryand openfile requests.
In our main class we just send the intent to target app
Last updated