Back in the day, things were much simpler. We’d throw up a massive firewall, segment the DMZ, and sleep like a baby. But the world moved on. No one just logs into corporate apps from a desktop at the office anymore. Those massive firewalls have been replaced by mobile devices everyone carries in their pocket—used on the subway, in cafes, even mid-flight. Trends have shifted so much that instead of banging on the corporate network's front door, cyber attackers focus on the user's mobile app—the keys to the kingdom sitting right in their pocket.
When you say, "I log in with FaceID, I’m super secure," we on the Red Team are actually watching a different movie. Mobile security doesn't start and end with a screen lock or biometric data. What’s hidden inside that app’s code? How is data being handled on the device? Today, drawing from simulations we’ve run at testCompany and real-world field experience, we’re diving into the "kitchen" of mobile security—the parts people rarely talk about.
The Irony of Hardcoded Secrets
When we start analyzing a mobile app, the first place we look is the static data a developer sprinkled into the code, thinking, "no one will ever see this." In the Android world, cracking open an APK with jadx or apktool takes literally seconds. Developers sometimes bury API keys, encryption salts, or even test environment credentials right in the source code.
Take a look at this example (defanged):
// A classic example of bad practice
public class APIClient {
private static final String API_KEY = "AKIA_MOCK_12345_67890_EXAMPLE";
private static final String BASE_URL = "https://api.testcompany-example.com/v1/";
public void connect() {
// Connection logic...
}
}
Anyone decompiling that code can grab the sensitive API key for the testCompany platform in seconds. You might say, "But I obfuscate my code!" Using ProGuard or R8 might turn your variable names into a, b, or c, but that string value (the API Key) will still be sitting there in plain sight. The fix? Never keep sensitive keys on the client side, or manage them dynamically using secure elements like Keystore or Keychain.
Data Storage: SharedPreferences is Not a Vault
Another classic we encounter in almost every penetration test: sensitive data stored as plain text in SharedPreferences (Android) or UserDefaults (iOS). These areas are meant for app settings—like whether dark mode is on or the user's language preference—not for storing a user’s JWT token or the last four digits of a credit card.
On a rooted device or during a backup attack, accessing the following file content is child's play:
<!-- /data/data/com.testcompany.app/shared_prefs/UserPrefs.xml -->
<map>
<string name="session_token">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.mock_data_for_education</string>
<string name="user_email">[email protected]</string>
</map>
