← Back to list

DroidCave — Stealing Passwords Through an Over-Permissive ContentProvider

This is my writeup for the DroidCave challenge from 8kSec Android Labs. The goal is to build an app that looks completely innocent but…

Mohammed Ashraf · 2026-05-24 14:19 · 0 claps · 2.6 min read
#android-security #penetration-testing #ctf #ctf-writeup #8ksec
Open on Medium ↗

DroidCave — Stealing Passwords Through an Over-Permissive ContentProvider

This is my writeup for the DroidCave challenge from 8kSec Android Labs. The goal is to build an app that looks completely innocent but silently extracts all stored passwords — including encrypted ones — from DroidCave’s database with a single button tap.

AndroidContentProviderData ExfiltrationJADX8kSec

Step 1

Understanding the App

DroidCave is a password manager. You can store account credentials — username, password, and some details — and optionally enable an encryption mode that encrypts the password before saving it to the local database.

From a user’s perspective it’s straightforward. But the moment I opened the manifest in JADX, the real picture became clear.

Step 2

Manifest Analysis — One Component Changes Everything

Most of the app’s components are unexported, which is the right call. But there’s one exception — the PasswordContentProvider:

AndroidManifest.xml

<provider
    android:name="com.eightksec.droidcave.provider.PasswordContentProvider"
    android:exported="true"
    android:authorities="com.eightksec.droidcave.provider"
    android:grantUriPermissions="true"/>

Exported. No permission required. Any app on the device can query it freely. That’s the entire vulnerability in one attribute.

Step 3

What the Provider Actually Exposes

Looking at PasswordContentProvider in JADX, the provider registers a UriMatcher with several URI paths, each mapped to a different action:

The execute_sql path alone is a critical finding — it lets any caller run raw SQL queries against the app's internal database. But for this challenge, the disable_encryption path is what we really need.

Step 4

How disable_encryption Works

When queried, this URI path does two things. First, it flips the encryption flag in SharedPreferences to false. Then it loops through every password in the database that has isEncrypted = 1, decrypts it using the app's own EncryptionService, and writes the plaintext back. The app does all the hard work for us.

The decryption key lives inside the app — we never need to know it. We just ask the provider to decrypt everything, and it does.

Step 5

The Exploit — Two Methods, One Button

I wrote two methods in the PoC app. The first triggers decryption. The second pulls all the now-plaintext passwords out of the database. Both are called when the user taps a single button.

Exploit.java — Step 1: Trigger Decryption

Exploit.java — Step 2: Pull the Passwords

One important detail — on Android 11 and above, you need a <queries> block in the PoC app's manifest, otherwise the system won't let you talk to other apps' providers:

AndroidManifest.xml — PoC App

<manifest ...>
    <!-- Required on Android 11+ for package visibility -->
    <queries>
        <package android:name="com.eightksec.droidcave" />
    </queries>
   <application ...>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

Install the PoC, open it, tap the button. DroidCave decrypts its own database and hands us every stored credential — logged cleanly to Logcat.

Takeaway

Why This Worked

The ContentProvider was exported with no permission guard, which means any installed app could query it freely. But what made it especially damaging was the disable_encryption endpoint — it allowed an outsider to trigger internal decryption logic and then immediately read back the results. The app essentially broke its own encryption on command.

Fix: Set android:exported="false" if the provider is only used internally. If external access is needed, protect it with a custom android:permission declared with protectionLevel="signature". Never expose sensitive operations — like decryption — through an unprotected provider URI.


메타데이터
post_id
bfe3cf345332
slug
droidcave-stealing-passwords-through-an-over-permissive-contentprovider-bfe3cf345332
url
https://medium.com/@TionoX/droidcave-stealing-passwords-through-an-over-permissive-contentprovider-bfe3cf345332
canonical_url
https://medium.com/@TionoX/droidcave-stealing-passwords-through-an-over-permissive-contentprovider-bfe3cf345332
author_url
https://medium.com/@TionoX
status
ok
fetched_at
2026-06-09 15:37:30