MediumI Got a Critical Vulnerability in a Popular Social Media App Within 5 Minutes | by Foysal Hossain, Senior Consultant @EY | Oct, 2026 | Medium
Sitemap
Foysal Hossain, Senior Consultant @EY

Foysal Hossain, Senior Consultant @EY

Senior Penetration Tester | Red Teamer | OSCP+ | CRTP | LPT | CPENT | Web, API, Mobile, Network & AI Security | BCS CTF 2025 Champion

I Got a Critical Vulnerability in a Popular Social Media App Within 5 Minutes

--

Five minutes. That’s all it took between opening a rooted Android device for the first time and sitting on a Critical severity finding in a mobile app used by millions of people. No fuzzing, no custom tooling, no weeks of reverse engineering — just a methodical first pass through the app’s local storage.

This is the writeup of how that happened, why the fix was rated Critical, and the mindset that makes bugs like this so common even in apps built by large, well-resourced teams.

TL;DR

  • Target: A popular social media app’s Android client
  • Vulnerability: Sensitive local storage — full chat conversations stored in plaintext in the app’s local SQLite database
  • Access required: Physical/local access to the device, or a backup/extraction of app data
  • Severity: Critical
  • Time to find: ~5 minutes after setup, once the storage directory was inspected
  • Root cause: No encryption at rest for a local database holding private user-to-user messages

Setting Up the Lab

The whole test rig was deliberately boring — nothing exotic, just the standard mobile pentest baseline:

  1. Target app — downloaded directly from the Google Play Store, the exact build any real user would install.
  2. Rooted Android device — a physical test device with root access, used to reach the app’s private data directory under /data/data/<package>/.
  3. Root detection bypass — the app shipped with a root/jailbreak check that blocked normal usage on a rooted device. A Frida script hooked the relevant detection calls at runtime and returned “not rooted” so the app would launch normally.
  4. adb shell — used to browse the app’s sandbox once the app was running and had generated its local data.

None of this required a custom exploit or a zero-day bypass technique — public Frida root-detection bypass scripts and a rooted test phone are standard kit for any mobile app assessment.

Reconnaissance

With the root check bypassed, the next step was ordinary usage, not hacking: create a throwaway test account, explore the main flows, and build a feel for what the app does with user data.

For a social/messaging app, the obvious core features are the ones most likely to touch sensitive data: profile setup, following/connecting with other users, and — critically — direct messaging. A second test account was used on another device so a real conversation could be exchanged between two accounts, generating realistic chat data to inspect later.

At this point nothing unusual had happened yet. The app behaved exactly as expected from the outside. The interesting part was about to start — not in the traffic, but on disk.

The Finding: Plaintext Chat Storage

Most mobile apps built with cross-platform frameworks (Flutter, in this case) persist local state — settings, drafts, cached data, and often entire conversation histories — in a local SQLite database inside the app’s sandboxed data directory. That directory is only reachable with root or a rooted emulator, which is exactly why this class of bug survives so long: it’s invisible from the outside and invisible in a non-rooted QA pass.

The process was simple:

  1. adb shell into the device and navigate to the app's private storage: /data/data/<package>/app_flutter/.
  2. List the directory contents — a .sqlite database file stood out immediately, named after the logged-in user's internal ID.
  3. Open it with sqlite3 <database>.sqlite and run .tables to see the schema.
Press enter or click to view image in full size

The table list included a messages table — exactly what you'd hope not to find unprotected.

Get Foysal Hossain, Senior Consultant @EY’s stories in your inbox

Join Medium for free to get updates from this writer.

Run SELECT * FROM messages; and the entire chat history came back in plain, readable text — no encryption, no hashing, nothing. Every message sent and received through the app, stored verbatim in a queryable local database.

Press enter or click to view image in full size

In other words: anyone with physical access to the device, malware with local storage permissions, a lost/stolen/resold phone, or a local backup/extraction tool could read a user’s full private conversation history with a single SQL query — no credentials, no app login, no server interaction required.

Why This Was Rated Critical

At first glance, “local storage isn’t encrypted” can sound like a low-impact nitpick — it’s not a remote exploit, and it needs some form of access to the device. The severity comes from what was actually exposed and how easily it could be reached:

  • Data sensitivity — direct messages routinely carry phone numbers, financial details, addresses, intimate conversations, and other PII users reasonably expect to stay private. This isn’t metadata; it’s full message content.
  • Low attack complexity — no reverse engineering of a protocol, no cryptographic attack, no privilege escalation beyond standard root. A single SQL query against a world-accessible-once-rooted file returns everything.
  • Realistic threat scenarios — lost or stolen devices, resold or recycled phones that weren’t properly wiped, malicious apps with storage access on a rooted device, forensic/backup extraction tools, and shared or family devices are all common, not theoretical.
  • Scale — this wasn’t a one-off account issue; it’s a storage design decision that affects every user’s chat history on the platform.

Mapped to standard classifications, this falls under CWE-312 (Cleartext Storage of Sensitive Information) and OWASP Mobile Top 10 — M9: Insecure Data Storage. Given the sensitivity of the data, the ease of access once local storage is reached, and the scale of exposure, the program rated it Critical.

Responsible Disclosure

The finding was documented with a clear reproduction path — steps, screenshots of the storage layout and the database schema, and a plain-language explanation of impact and real-world attack scenarios — and submitted through the program’s official bug bounty / responsible disclosure channel. Out of respect for the program’s disclosure policy, the platform name and exact timeline are withheld here until public disclosure is permitted; this writeup focuses on the technique and the lesson rather than naming the target.

Remediation

For teams shipping similar chat or messaging features, a few concrete fixes close this class of bug:

  • Encrypt data at rest. Use SQLCipher or an equivalent encrypted database layer instead of a plain SQLite file, with keys backed by the Android Keystore / iOS Keychain rather than stored alongside the data.
  • Minimize what’s persisted locally. Cache only what the UI needs (recent messages, not the full history) and let the server remain the source of truth where possible.
  • Use platform-protected storage APIs (EncryptedSharedPreferences, Keystore-backed storage) for anything sensitive, instead of hand-rolled local files.
  • Treat the local sandbox as untrusted on rooted/jailbroken or physically compromised devices — root detection is a speed bump, not a security control, and should never be the only thing standing between an attacker and plaintext data.
  • Include local storage review in every mobile security test and release checklist, not just API and network-layer testing.

Lessons for Bug Hunters

The biggest takeaway from this bug isn’t a clever technique — it’s that local storage review is one of the highest-ROI steps in mobile pentesting, and it’s often skipped because people assume “it’s just on the device, who can even reach that.”

A few things worth internalizing if you’re starting out in mobile bug hunting:

  • Check the boring stuff first. Local databases, shared preferences, cache files, and log files are consistently under-protected compared to network traffic, which gets far more developer attention.
  • Root detection bypasses are table stakes, not an exploit. A Frida script that patches a boolean check isn’t “hacking the app” — it’s clearing an obstacle so you can test the app the way it’s actually used in the wild (on rooted devices, which are common among both testers and real attackers).
  • Severity isn’t about how hard the bug was to find. A five-minute discovery can still be Critical if the impact is severe and the data sensitive — triagers rate based on consequence, not effort.
  • Build a repeatable checklist. App storage directories, SQLite/Realm/Hive databases, SharedPreferences/NSUserDefaults, keychain usage, log output, and cached media are worth checking on every single mobile target.

--

--

Foysal Hossain, Senior Consultant @EY
Foysal Hossain, Senior Consultant @EY

Written by Foysal Hossain, Senior Consultant @EY

Senior Penetration Tester | Red Teamer | OSCP+ | CRTP | LPT | CPENT | Web, API, Mobile, Network & AI Security | BCS CTF 2025 Champion