Skip to content
Sedat Özdemir
Writing

android-hacking

Trojan Horses in Our Pockets: Mobile App Security Blunders We Swear We'd Never Make

From mistaking public API keys for crown jewels to the 'allowBackup' trap, let's dive into some common mobile security pitfalls I've seen in the field—and how to avoid them.

Sedat Özdemir
· 4 dk read

Ah, youthful enthusiasm... In my first mobile pentest, when I was still very green, I dove deep into a banking app. Our target was a new wallet application for a firm called testCompany. Back then, I was trying to do everything manually, basically thinking I was Neo from 'The Matrix'. I decompiled the app, got lost in Smali code for hours, and finally had that famous 'Eureka!' moment. I claimed I’d found an API key that would give me keys to the kingdom. I told the team, 'Found it guys, we’re in!'

But there was one tiny detail: The key I found was actually just a public, unrestricted Google Maps API key used for showing branch locations. That day, I learned that just finding something isn't enough; knowing what, where, and why you found it—understanding the context—is everything. Since then, I go into every mobile project thinking, 'Alright, let's see where we’re going to trip up this time?'

Decompiling? That's the Easy Part!

When we start a Red Team engagement, usually the first thing we do is grab the APK or IPA file and tear it apart. In the Android world, this is child's play with jadx-gui or apktool. But the point isn't just reading the code; it's finding that little 'moment of forgetfulness' a developer left behind on a tired Tuesday evening.

My favorite type of mistake is finding API keys embedded inside strings.xml or hardcoded directly into Java/Kotlin code. My developer friends often say, 'Well, this app becomes a binary, nobody can see my code.' Oh buddy, decompiling takes literally seconds.

Look at this, for example—it's so common it hurts:

// Defanged example
public class ApiConfig {
    private static final String API_KEY = "sk_test_51Mz9...THIS_IS_A_MOCK_KEY..."; // DANGER!
    private static final String BASE_URL = "https://api.example.com/v1/";
}

At this point, you have to think: If this key falls into the hands of a Red Teamer, what kind of permissions does it have on the backend? If this is a payment system and that key points to 'live' instead of 'test', it's game over.

Local Storage: The Black Hole We Think Is Safe

Apps love storing data. User preferences, session tokens, and sometimes (unfortunately) user passwords... On Android, it's SharedPreferences; on iOS, it's UserDefaults; or SQLite databases on both.

There’s a mistake that hasn't changed in years: storing sensitive data in plain-text. The developer thinks, 'I'm storing this in the app's private area, other apps can't touch it.' On a rooted device or against an attacker with physical access, that 'private area' protection is just an illusion.

For instance, check out this innocent-looking line in an AndroidManifest.xml file:

<application
    android:allowBackup="true"
    ... >
</application>

This line allows an attacker to pull all the app's data to their computer using a simple adb backup command. Even if your phone has a passcode, if the attacker gets their hands on the device while it's unlocked (or uses other tricks), they can extract your local databases and preferences in seconds.

Remember: if it's sensitive, it needs to be in the Keychain (iOS) or Keystore (Android), and it definitely shouldn't be backed up in plain sight.

Related posts