Scenario: A smart home company launches “HomeGuard,” an Android companion app for their security camera and door lock products. Before the v2.0 release, the security team must audit the app for privacy leaks. The app requests 8 permissions: camera, microphone, location, contacts, storage, phone state, Bluetooth, and Wi-Fi state.
Step 1: Permission Necessity Audit
Evaluate whether each permission is actually needed for the app’s stated functionality:
| Camera |
QR code scanning for device setup |
Used once during setup, then continuously by analytics SDK |
Over-retained |
| Microphone |
Two-way audio with camera |
Legitimate, used only during live view |
Necessary |
| Location |
“Improve service” (vague) |
Sent to 3 ad SDKs every 15 minutes even when app is backgrounded |
Privacy leak |
| Contacts |
Not disclosed |
Read by analytics SDK, hashed and uploaded for “social graph” |
Undisclosed leak |
| Storage |
Save video clips |
Legitimate |
Necessary |
| Phone state (IMEI) |
“Device identification” |
IMEI sent to 4 different analytics endpoints |
Over-collection |
| Bluetooth |
Device discovery |
Legitimate, scoped to setup flow |
Necessary |
| Wi-Fi state |
Network detection |
Legitimate for local device communication |
Necessary |
Step 2: Static Analysis with FlowDroid
Run FlowDroid on the decompiled APK (42,000 methods across app code + 6 third-party SDKs):
getLastLocation() |
HttpURLConnection to ads.tracker.com |
AdMob SDK |
No explicit consent |
Privacy leak |
getLastLocation() |
HttpURLConnection to analytics.mix.com |
Mixpanel SDK |
No explicit consent |
Privacy leak |
getDeviceId() (IMEI) |
HttpURLConnection to graph.facebook.com |
Facebook SDK |
Buried in ToS page 23 |
Privacy leak |
getContacts() |
HttpURLConnection to analytics.mix.com |
Mixpanel SDK |
Not disclosed anywhere |
Privacy leak |
getContacts() |
Local SQLite |
App code |
N/A (local storage) |
Not a leak |
| Camera frames |
HttpURLConnection to api.homeguard.com |
App code |
User-initiated (live view) |
Legitimate |
FlowDroid found 12 source-to-sink paths, of which 4 are confirmed privacy leaks, 2 are legitimate app functionality, and 6 are false positives (paths that exist in code but are unreachable at runtime).
Step 3: Dynamic Analysis with Network Traffic Capture
Run the app for 24 hours with a MITM proxy (mitmproxy with SSL unpinning via Frida) while simulating normal usage:
api.homeguard.com |
Camera stream, device status |
On user action |
TLS 1.3 |
Yes |
ads.tracker.com |
GPS lat/lng, device model, Android ID |
Every 15 min |
TLS 1.2 |
No |
analytics.mix.com |
IMEI hash, contact count, app events |
Every 5 min |
TLS 1.2 |
No |
graph.facebook.com |
IMEI, installed apps list, Wi-Fi SSID |
Every 30 min |
TLS 1.3 |
No |
crashes.google.com |
Stack traces (may contain user data) |
On crash |
TLS 1.3 |
Partial |
Key finding: Over 24 hours, the app made 287 requests to third-party analytics servers versus 34 requests to the company’s own API. The device transmitted the user’s GPS location 96 times to advertising networks without any in-app disclosure.
Step 4: Taint Analysis Summary
Combining static and dynamic results:
| GPS location |
getLastLocation() |
2 ad networks |
Critical |
Article 6 violation (no legal basis) |
| Contacts |
getContacts() |
1 analytics SDK |
Critical |
Article 9 violation (social graph) |
| IMEI |
getDeviceId() |
4 analytics endpoints |
High |
Article 5(1)(c) violation (minimization) |
| Camera access |
Camera API |
1 analytics SDK (background) |
High |
Article 5(1)(b) violation (purpose limitation) |
Step 5: Remediation
| Remove contacts permission |
Delete Mixpanel social graph feature |
2 hours |
Eliminates contacts leak entirely |
| Replace IMEI with instance ID |
Use UUID.randomUUID() per install |
4 hours |
Eliminates cross-app tracking |
| Add consent dialog for analytics |
GDPR-compliant opt-in before SDK init |
3 days |
Enables legal basis for analytics |
| Remove location from ad SDK |
Set AdMob.setLocationEnabled(false) |
1 hour |
Stops 96 daily location transmissions |
| Audit all 6 SDKs quarterly |
Integrate Exodus Privacy into CI/CD |
2 days |
Catches new leaks from SDK updates |
Outcome: The v2.0 release shipped with 3 permissions removed (location, contacts, phone state), a GDPR-compliant consent dialog, and instance-based IDs replacing IMEI. Third-party network requests dropped from 287/day to 41/day. The app passed an independent privacy audit, enabling EU market distribution.