Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
MOBILE PENTEST BOOTCAMP
Day 5 — Insecure Data Storage
Session length: 2.5–3 hours | First dynamic day — app running | Target: InjuredAndroid
Session time estimate
2.5 to 3 hours of hands-on work. Where the time goes:
Part 2 Session startup + exercise the app + pull the sandbox ~30 min
Part 3 Shared preferences and internal files inspection ~35 min
Part 4 SQLite databases — open, enumerate, dump ~30 min
Part 5 Logcat capture and live analysis ~20 min
Part 6 Local crypto thread — spot and decrypt 'encrypted' data ~25 min
Part 7 External storage + logout data-clearing test ~15 min
Part 8 Triage + write the finding ~20 min
Total: ~2.5 to 3 hours.
Note: InjuredAndroid is deliberately vulnerable — you will find real storage issues.
What changes on Day 5 — the dynamic shift
Days 3-4 were static: you read the APK sitting on disk.
Day 5 is dynamic: the app is RUNNING on your emulator, creating files as you use it.
The mental model shift:
Static analysis tells you what the app CAN store.
Dynamic storage analysis shows you what it ACTUALLY stored.
A login screen might show in jadx that it writes a session token — but only
by logging in and then pulling the files do you confirm it wrote it in cleartext.
You MUST use the app before inspecting. An unused app writes nothing.
Catalogue test cases you mark today
M9-01 No sensitive data in shared_prefs / NSUserDefaults
M9-02 SQLite / Core Data free of cleartext secrets
M9-03 Files in sandbox not storing secrets cleartext
M9-04 Keychain / Keystore used correctly
M9-06 No sensitive data on external storage
M9-07 Cache / temp files do not retain secrets
M9-08 Data cleared on logout / uninstall
M1-05 Credentials not stored in cleartext locally
M1-08 Session tokens not leaked to logs
M10-02 Keys not hardcoded or poorly derived
Test only against accounts and apps you own — minimum data principle always applies. Page 1
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
M10-03 Proper IV / nonce usage
M10-04 Encrypted local data truly protected
M6-08 Backup excludes sensitive data (confirm from Day 3)
M8-05 Verbose logging disabled in release
Open your catalogue now — mark all of these In progress.
Part 1 — Android Storage Architecture: Know Where to Look
Half the skill of storage testing is knowing where to look. Android has a specific directory structure for
each app's private data. Understanding it means you never miss a storage location.
Storage location adb path What to look for Catalogue IDs
Shared preferences /data/data/<pkg>/ Cleartext tokens, M9-01, M1-05
shared_prefs/*.xml passwords, PINs, session
flags, user IDs
SQLite databases /data/data/<pkg>/ Credential tables, cached M9-02, M1-05
databases/*.db API responses, message
history, PII
Internal files /data/data/<pkg>/ Downloaded content, temp M9-03
files/ files, crypto material,
session state
Cache /data/data/<pkg>/ WebView cache, HTTP M9-07
cache/ cache, thumbnail cache
with sensitive content
External storage /sdcard/Android/ Backups, exported files, M9-06
data/<pkg>/ photos — readable by any
app (no root needed)
Logcat adb logcat (live Debug output: tokens, M1-08, M8-05
stream) passwords, request bodies,
stack traces
KeyStore/Keychain /data/data/<pkg>/ Is sensitive data using the M9-04, M10-02/03
shared_prefs/ or KeyStore properly?
system
The two access methods — know both
Method 1: adb root + adb pull
Works on your rooted emulator for ANY app. The approach used throughout today.
Requires: adb root to succeed (Google APIs image, not Google Play).
Method 2: adb shell run-as <package>
Works WITHOUT root but ONLY for apps with android:debuggable=true.
Remember flagging debuggable=true on Day 3 (M8-01)?
This is one reason that finding matters: debuggable apps are accessible without root.
Use this if you are ever testing on a non-rooted device against a debug build.
Test only against accounts and apps you own — minimum data principle always applies. Page 2
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
Part 2 — Session Startup and Pulling the Sandbox
Cardinal rule: exercise the app BEFORE inspecting storage
An app that has not been used has not written anything to storage.
Before pulling any files: log in, enter data into forms, tap through screens,
hit any 'remember me' or 'save' features, complete one or two CTF challenges.
Each interaction writes files. Skip this and the prefs folder will be empty.
2.1 — Start your session
# Standard Day 5 session startup
adb root
adb shell "/data/local/tmp/frida-server &" # keep the habit even if not used
today
frida-ps -U | head # confirm Frida sees the emulator
# Confirm InjuredAndroid is installed
adb shell pm list packages | grep injured
# Expected: package:[Link]
PKG=[Link] # set this variable — used in every command below
2.2 — Exercise InjuredAndroid meaningfully
Launch InjuredAndroid in the emulator and interact with it before doing anything else. Specifically:
1. Tap through the login / register screen if one exists — use any credentials
2. Enter data into any input fields you see
3. Tap 'Save', 'Remember me', or similar buttons
4. Complete Flag 1 and Flag 2 in the CTF challenges (these trigger storage writes)
5. Open the settings or profile screen
6. If there is a webview screen, open it
Spend at least 5 minutes interacting. The more you do, the more gets written to storage, and the richer
your findings.
2.3 — Pull the entire sandbox to your Mac
# Create the storage analysis folder in your target directory
mkdir -p ~/mobile-pentest-bootcamp/targets/injuredandroid/storage/pulled
cd ~/mobile-pentest-bootcamp/targets/injuredandroid/storage
# Method A: Pull the entire sandbox as a tar archive (cleanest)
adb exec-out "su 0 tar -cf - /data/data/$PKG" > [Link] 2>/dev/null
Test only against accounts and apps you own — minimum data principle always applies. Page 3
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
tar -xf [Link] -C pulled/
ls pulled/data/data/$PKG/
# Method B: Pull each folder individually (use if tar fails)
adb pull /data/data/$PKG/shared_prefs pulled/shared_prefs
adb pull /data/data/$PKG/databases pulled/databases
adb pull /data/data/$PKG/files pulled/files
adb pull /data/data/$PKG/cache pulled/cache
# Confirm you have files
find pulled/ -type f | head -30
If adb pull says 'Permission denied'
Run: adb root (confirm it says 'restarting adbd as root')
Then retry the pull commands.
If adb root fails: you are using a Google Play emulator image — go back to Day 2
and create a Google APIs image. Root access is essential for storage testing.
Part 3 — Shared Preferences: The Most Common Leak
Shared preferences are the number-one place sensitive data leaks in Android. They are plain XML key-
value files, trivially human-readable, and developers reach for them constantly to 'remember' things —
auth tokens, 'keep me logged in' flags, user IDs, PIN codes. Open them first.
3.1 — Read every preferences file
cd ~/mobile-pentest-bootcamp/targets/injuredandroid/storage
# List all shared_prefs files
ls -la pulled/shared_prefs/
# or if using method A:
ls -la pulled/data/data/$PKG/shared_prefs/
# Read every .xml file
cat pulled/shared_prefs/*.xml
# Or use find to handle nested paths from method A
find pulled/ -name '*.xml' | xargs cat
3.2 — What you are looking for in the XML
Shared prefs are XML. Each entry is a key-value pair. Read every key name and value looking for:
Test only against accounts and apps you own — minimum data principle always applies. Page 4
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
Item Detail
auth_token / Session token in cleartext — direct account takeover if stolen. M9-01 Fail,
session_id / M1-05 Fail. HIGH.
access_token
password / passwd / pin Cleartext password or PIN. M9-01 Fail, M1-05 Fail. HIGH.
user_id / email / PII stored in preferences. M9-01 possible Fail depending on sensitivity.
username
is_logged_in / Client-side boolean flags — hint at client-side auth decisions (connects to
is_premium / is_admin M7-05).
Base64 string Decode it: echo 'VALUE' | base64 -d — if it decodes to a token or
password, that is a finding.
Random-looking bytes Might be 'encrypted' data — see Part 6, the local crypto thread.
3.3 — InjuredAndroid walkthrough: what to expect
InjuredAndroid deliberately writes cleartext data to shared_prefs. After completing Flag challenges, you
should see a preferences file containing flag values and app state. Here is an example of what a
vulnerable preferences file looks like:
# Example vulnerable shared_prefs content (InjuredAndroid)
# File: b3nac.injuredandroid_preferences.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="AUTH_TOKEN">[Link]</
string>
<boolean name="IS_LOGGED_IN" value="true" />
<string name="USER_EMAIL">attacker@[Link]</string>
<string name="FLAG_ONE">F1ag_on3</string>
</map>
# AUTH_TOKEN is a JWT in base64 — readable without any key
# Decode the JWT payload (middle segment between dots):
echo 'eyJ1c2VyIjoiYWRtaW4ifQ' | base64 -d
# Output: {"user":"admin"}
Base64 is encoding, NOT encryption
A base64 string in shared_prefs is NOT protected.
Any developer who writes base64 'to hide' a value has only obscured it visually.
echo 'BASE64STRING' | base64 -d reveals it in one command.
This is a very common misunderstanding. Flag every base64 value and decode it.
If it decodes to a token, key, or password: Fail.
3.4 — Broad grep across all pulled files
Test only against accounts and apps you own — minimum data principle always applies. Page 5
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
# Run a broad sensitive-keyword sweep across the entire sandbox
grep -rEi 'token|password|passwd|secret|session|email|pin|user.?id|auth|credit|
ssn|key' \
pulled/ 2>/dev/null | grep -v '.class:' | head -50
# Decode any base64 strings you find
echo 'PASTE_BASE64_VALUE_HERE' | base64 -d
# Search for JWT tokens specifically (they have three dot-separated segments)
grep -rEo 'eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+' pulled/
2>/dev/null
Part 4 — SQLite Databases: Structured Data Hunting
SQLite databases hold the app's structured data — user records, message history, cached API
responses, sometimes login credentials. They are in the databases/ folder as .db files. You need the
sqlite3 command-line tool to inspect them.
4.1 — Install sqlite3 if needed
# macOS: sqlite3 is pre-installed
sqlite3 --version
# Optional: install DB Browser for SQLite (GUI tool — easier for big databases)
brew install --cask db-browser-for-sqlite
4.2 — List and open every database
cd ~/mobile-pentest-bootcamp/targets/injuredandroid/storage
# List all database files
find pulled/ -name '*.db' -o -name '*.sqlite' | head -20
# Common names: [Link], [Link], google_app_measurement.db
# Open a database
sqlite3 pulled/databases/[Link]
4.3 — SQLite commands to run on every database
Once inside the sqlite3 prompt, run these commands in order:
-- Step 1: List all tables
.tables
-- Step 2: See the structure of every table
Test only against accounts and apps you own — minimum data principle always applies. Page 6
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
.schema
-- Step 3: Dump any table that looks sensitive
SELECT * FROM users;
SELECT * FROM sessions;
SELECT * FROM messages;
SELECT * FROM credentials;
-- Step 4: Look for cached API responses
SELECT * FROM api_cache LIMIT 5;
-- Step 5: Check WebView cache database if present
.tables
-- WebView databases often contain: formdata, cookies, passwords
SELECT * FROM password;
SELECT host, username, password FROM logins;
-- Step 6: Quit
.quit
What to look for in database tables
users / accounts tables: password column in cleartext? token stored?
sessions / tokens tables: session_id or auth_token readable?
messages / chat tables: PII or sensitive content in message bodies?
cached API responses: does the cache include other users' data?
WebView formdata table: auto-filled form values including passwords?
Any table with > 0 rows containing auth data = M9-02 Fail.
4.4 — InjuredAndroid: CTF database findings
InjuredAndroid creates a database that stores flag-related data and user interaction history. After
completing some CTF challenges, you should find a database containing flag values stored in cleartext.
Here is the exploration workflow:
# Open InjuredAndroid's main database
find pulled/ -name '*.db' | head -5
# Open the first .db file
sqlite3 pulled/databases/[Link]
.tables
-- Expected output: flags user_data challenges (or similar)
SELECT * FROM flags;
-- If flags are stored: you see them in cleartext -> M9-02 Fail
.schema
Test only against accounts and apps you own — minimum data principle always applies. Page 7
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
-- Read every table definition to understand what the app stores
Part 5 — Logcat: The Live Log Stream
Logcat is Android's logging system. It is a live stream of debug output from every running app and the
system. You do not need root to read it. You do not need to pull files. You just watch it while using the
app. Developers notoriously leave Log.d(), Log.e(), and Log.i() calls in production code that print
tokens, passwords, request bodies, and API responses in full.
Run logcat WHILE using the app — not after
Logcat is a live stream — past messages scroll out of the buffer.
Start logcat FIRST, then exercise the app, watching what prints.
Alternatively: capture to a file and grep it afterward.
5.1 — Start logcat capture
# Method A: Live stream filtered to sensitive keywords
# Do this first, then use the app in the emulator
adb logcat -c # clear old logs
adb logcat | grep -Ei 'token|password|secret|session|user|auth|http|api|flag|key'
# Method B: Filter to just InjuredAndroid's process (less noise)
adb logcat --pid=$(adb shell pidof -s $PKG) | tee logcat_capture.txt
# Method C: Capture everything to a file, grep after
adb logcat -c
adb logcat > logcat_full.txt & # runs in background
# Now exercise the app for 2-3 minutes
# Then stop capture with Ctrl-C and grep the file:
grep -Ei 'token|password|secret|session|auth|http|api' logcat_full.txt | head -40
5.2 — What sensitive logcat output looks like
Here are the patterns you are hunting in logcat output:
# FINDING — session token in log (M1-08 Fail, M8-05 Fail)
D/AuthManager: Login successful. Token:
[Link]
# FINDING — password printed on login (M1-08 Fail)
D/LoginActivity: Attempting login with: admin@[Link] / MyPassword123
# FINDING — full HTTP response body in log (M8-05 Fail)
D/OkHttp: <-- 200 OK [Link]
D/OkHttp: {"user":"admin","balance":10000,"ssn":"123-45-6789"}
Test only against accounts and apps you own — minimum data principle always applies. Page 8
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
# LOW — debug log in release build (M8-05 — severity depends on content)
D/InjuredAndroid: Starting flag challenge 1
# NOT a finding — system-level logs that happen to match keyword
I/ActivityManager: Starting: Intent { cmp=[Link]/.MainActivity }
5.3 — InjuredAndroid logcat walk
While interacting with InjuredAndroid, watch the logcat output for the tag 'InjuredAndroid' and for any
flag values or tokens being printed. InjuredAndroid deliberately logs debug output that reveals sensitive
data. When you find a log line containing a flag, token, or credential:
# Filter to just InjuredAndroid tags
adb logcat -s InjuredAndroid:D
# Or filter for the package name
adb logcat | grep -i 'injuredandroid\|b3nac'
# Look for lines containing flag values or tokens
# A finding looks like:
# D/InjuredAndroid: User flag value: F1ag_on3
# D/InjuredAndroid: JWT Token: eyJ...
Part 6 — The Local Crypto Thread: 'Encrypted' Data
This thread began on Day 4 when you looked for hardcoded crypto keys in the decompiled code. Today
is where it pays off. Some apps store data that appears encrypted — random-looking bytes in a file or
preference — and the developer considers this secure. If the encryption key is hardcoded (which you
may have found on Day 4), it is not secure at all. You can decrypt their 'protected' data yourself.
Why this finding is rated HIGH even though you have to do extra work
A developer who hardcodes an AES key and encrypts local data believes they have
protected the data. The attack surface map says 'encrypted — not a finding.'
When you can decrypt it using the key from the decompiled code, you prove:
(1) The key is accessible to anyone with the APK.
(2) The 'encryption' provides zero real protection.
(3) The developer made a security decision based on false assumptions.
This is worse than cleartext storage because it creates a false sense of security.
Catalogue IDs: M10-02 (key not hardcoded), M10-04 (encrypted data truly protected).
Test only against accounts and apps you own — minimum data principle always applies. Page 9
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
6.1 — Identify encrypted storage
While reviewing shared_prefs and files in Part 3, flag any value that looks like random binary or base64
but does NOT decode to printable text. That is your 'encrypted' data candidate.
# Found in shared_prefs: a value that looks random
# <string name="secure_token">xK9mP2qR7sL4nT8vW1jY6eU0cF3dA5bH</string>
# Step 1: Check if it is base64
echo 'xK9mP2qR7sL4nT8vW1jY6eU0cF3dA5bH' | base64 -d | xxd | head -5
# If it decodes to non-printable hex: likely real encryption
# Step 2: Cross-reference with Day 4 — did you find a hardcoded AES key?
# Search jadx_out for AES/crypto patterns
grep -rEi 'AES|SecretKeySpec|IvParameterSpec|Cipher' jadx_out/sources/ | head -20
grep -rEi 'hardcoded16bytkey\|secretkey\|encryptionkey' jadx_out/sources/ | head -
10
6.2 — Decrypt using the hardcoded key (Python)
Once you have identified the hardcoded key in jadx and the encrypted value in storage, decrypt it.
Python's pycryptodome library is the easiest approach:
# Install pycryptodome if not present
pip3 install pycryptodome --break-system-packages
# decrypt_storage.py
from [Link] import AES
from [Link] import unpad
import base64
# Values from your Day 4 decompiled code analysis
KEY = b'hardcoded16bytkey' # 16 bytes for AES-128
IV = b'hardcoded_iv_here' # 16 bytes IV (if static — also a finding M10-03)
# The 'encrypted' value from shared_prefs (base64 decode first if stored as
base64)
encrypted_b64 = 'PASTE_THE_VALUE_FROM_SHARED_PREFS_HERE'
ciphertext = base64.b64decode(encrypted_b64)
cipher = [Link](KEY, AES.MODE_CBC, IV)
plaintext = unpad([Link](ciphertext), AES.block_size)
print('Decrypted:', [Link]('utf-8', errors='replace'))
python3 decrypt_storage.py
# If the output is readable text (a token, password, PII): FINDING
# M10-02 (hardcoded key), M10-04 (encrypted data not truly protected)
Test only against accounts and apps you own — minimum data principle always applies. Page 10
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
6.3 — Static IV: a second crypto finding
While looking at the crypto code in jadx, check whether the IV (Initialization Vector) is also hardcoded
or static. A static IV with the same key means two encryptions of the same plaintext produce the same
ciphertext — breaking confidentiality even against observers who cannot decrypt. If you find a static IV
in the code, that is catalogue M10-03 = Fail.
# In jadx, search for IV patterns
grep -rEi 'IvParameterSpec|iv|[Link]' jadx_out/sources/ | head -15
# A finding looks like:
# byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // all-zero IV
# IvParameterSpec ivSpec = new IvParameterSpec("staticIV12345678".getBytes());
# M10-03 Fail
Part 7 — External Storage and Logout Data Clearing
7.1 — External storage (no root needed)
Any app with WRITE_EXTERNAL_STORAGE permission can write to the shared storage area. Files
here are readable by any app on the device — there is no sandbox. This is catalogue M9-06.
# Check external storage for your app's files
adb shell ls /sdcard/Android/data/$PKG/ 2>/dev/null
adb pull /sdcard/Android/data/$PKG/ pulled_external/ 2>/dev/null
# Also check the root of external storage
adb shell ls /sdcard/ | head -30
# Any sensitive file here is readable by every app on the device
grep -rEi 'token|password|secret|session' pulled_external/ 2>/dev/null
7.2 — Logout test: does data get cleared? (M9-08)
Many apps fail to clear stored credentials and tokens when the user logs out. This is a real finding: if a
device is stolen or passed to another person, the next user can bypass the login screen because the
stored session is still valid. Test it explicitly.
# Step 1: Log in to the app and pull shared_prefs
adb pull /data/data/$PKG/shared_prefs pulled_before_logout
cat pulled_before_logout/*.xml # note the auth_token or session value
# Step 2: Log out of the app using the normal logout button / menu
# Step 3: Pull shared_prefs again immediately after logout
adb pull /data/data/$PKG/shared_prefs pulled_after_logout
cat pulled_after_logout/*.xml
Test only against accounts and apps you own — minimum data principle always applies. Page 11
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
# Step 4: Compare
diff pulled_before_logout pulled_after_logout
# If the auth_token STILL EXISTS after logout: M9-08 Fail
# If the auth_token is gone after logout: M9-08 Pass
Also test the allowBackup confirmation (M6-08 from Day 3)
If you flagged allowBackup=true on Day 3, confirm it today:
adb backup -apk -noshared -f injuredandroid_backup.ab [Link]
A successful backup proves the data can be extracted without root.
The backup file contains the app's private data — open it and check for tokens.
Part 8 — Triage and Write the Finding
8.1 — Severity calibration
Not every storage finding is High. Use this table to calibrate before writing up.
What you found Severity Reasoning
Cleartext session token / auth HIGH Token can be stolen for account takeover
token without any additional exploit.
Cleartext password HIGH Direct credential theft. Can be reused across
services.
'Encrypted' data where key is HIGH Developer claimed protection; actual
hardcoded protection is zero. Broken trust.
PII (name, email, address) in MEDIUM Privacy exposure. Severity scales with
cleartext sensitivity of data.
Sensitive data surviving logout / MEDIUM-HIGH Residual token after logout = session doesn't
uninstall truly end (M9-08).
Session token in logcat MEDIUM-HIGH Any adb-connected attacker or app with
READ_LOGS can harvest it.
Debug verbose logs in release LOW-MEDIUM Information disclosure, reveals internals but
build not directly exploitable alone.
Non-sensitive flag or preference INFO No real-world impact. Mention in report
in cleartext appendix only.
8.2 — Complete finding write-up: cleartext session token in shared_prefs
Pick your strongest finding from today. A cleartext session token or auth token is the cleanest — it has
clear, demonstrable impact. Here is the complete six-section write-up:
FINDING WRITE-UP EXAMPLE — M9-01 / M9-04
Test only against accounts and apps you own — minimum data principle always applies. Page 12
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
TITLE
Session token stored in cleartext in shared preferences (M9-01, M1-05)
SEVERITY: High
JUSTIFICATION:
The session token stored in cleartext allows any attacker with brief
physical
device access, or any app that gained read access to the sandbox (e.g.
via a
backup or exploiting another vulnerability), to harvest a valid session
and
impersonate the authenticated user without knowing their password.
AFFECTED COMPONENT
File:
/data/data/[Link]/shared_prefs/b3nac.injuredandroid_preferenc
[Link]
Key: AUTH_TOKEN
IDs: M9-01 (shared prefs), M1-05 (credentials not stored securely)
DESCRIPTION
The application writes the user's session authentication token to Android
shared preferences in cleartext after a successful login. On a rooted
device,
or via an adb backup on a debuggable build, any party with device access
can
read this file and extract the token without user interaction or
knowledge.
REPRODUCTION STEPS
1. Install InjuredAndroid and complete the login flow.
2. Pull the shared preferences file:
adb pull /data/data/[Link]/shared_prefs/ .
3. Open b3nac.injuredandroid_preferences.xml in a text editor.
4. Observe: <string name="AUTH_TOKEN">eyJhbGciOiJIUzI1NiJ9...</string>
5. Decode the JWT payload:
echo 'eyJ1c2VyIjoiYWRtaW4ifQ' | base64 -d
-> {"user":"admin"} (no decryption, no key, fully readable)
EVIDENCE
Screenshot: shared_prefs XML showing AUTH_TOKEN value in cleartext
(attach)
Token decoded: base64 output showing plaintext claims
REMEDIATION
Test only against accounts and apps you own — minimum data principle always applies. Page 13
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
1. Store session tokens in the Android Keystore, not shared preferences.
Use EncryptedSharedPreferences (AndroidX Security library) as a
minimum.
2. If using EncryptedSharedPreferences, ensure the master key uses
AES256_GCM_HKDF_4KB with Keystore-backed protection.
3. Clear all stored tokens on logout (see M9-08 — verify independently).
Part 9 — Catalogue Test Cases: Mark Your Verdicts
ID Test case What you check / how to mark it
M9-01 No sensitive data in shared_prefs Pull shared_prefs/*.xml. Auth token/password in cleartext? -
> Fail HIGH.
M9-02 SQLite free of cleartext secrets sqlite3: .tables -> SELECT * from sensitive tables. Credential
columns? -> Fail.
M9-03 Files not storing secrets cleartext grep -rEi sensitive_keywords pulled/files/. Any hits? -> Fail.
M9-04 Keystore used correctly Is sensitive data written to Keystore, or to plain prefs? Plain
prefs = Fail.
M9-06 No sensitive data on external ls /sdcard/Android/data/<pkg>/. Sensitive files world-
storage readable? -> Fail.
M9-07 Cache not retaining secrets grep -rEi keywords pulled/cache/. WebView cache
containing auth data? -> Fail.
M9-08 Data cleared on logout Pull prefs before logout, logout, pull after. Token still
present? -> Fail.
M1-05 Credentials not stored in cleartext Same evidence as M9-01/02. Cleartext password/token
anywhere? -> Fail.
M1-08 Tokens not leaked to logs logcat | grep token/password while using app. Token in log?
-> Fail MEDIUM-HIGH.
M10-02 Keys not hardcoded jadx grep for SecretKeySpec/hardcoded key. Found in Part
6? -> Fail HIGH.
M10-03 Proper IV/nonce usage jadx grep for IvParameterSpec. Static IV found? -> Fail.
M10-04 Encrypted data truly protected Can you decrypt with the recovered key? Success -> Fail
HIGH.
M6-08 Backup excludes sensitive data Confirm allowBackup=false (Day 3) or test adb backup
today.
M8-05 Verbose logging disabled in Sensitive data in logcat on release APK? -> Fail LOW-
release MEDIUM.
Part 10 — Complete Command Reference
Test only against accounts and apps you own — minimum data principle always applies. Page 14
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
Session startup
adb root
adb shell "/data/local/tmp/frida-server &"
PKG=[Link]
Pull the sandbox
adb exec-out "su 0 tar -cf - /data/data/$PKG" > [Link] 2>/dev/null
tar -xf [Link] -C pulled/
# Or individual folders:
adb pull /data/data/$PKG/shared_prefs pulled/shared_prefs
adb pull /data/data/$PKG/databases pulled/databases
adb pull /data/data/$PKG/files pulled/files
adb pull /data/data/$PKG/cache pulled/cache
Shared preferences
cat pulled/shared_prefs/*.xml
find pulled/ -name '*.xml' | xargs cat
grep -rEi 'token|password|secret|session|pin|auth|email' pulled/ 2>/dev/null |
head -40
echo 'BASE64_VALUE' | base64 -d # decode any base64 blobs
SQLite databases
find pulled/ -name '*.db' | head -10
sqlite3 pulled/databases/[Link]
.tables
.schema
SELECT * FROM users;
.quit
Logcat
adb logcat -c # clear first
adb logcat | grep -Ei 'token|password|secret|session|auth|http|api|flag'
adb logcat --pid=$(adb shell pidof -s $PKG) | tee logcat_capture.txt
External storage + logout test
adb shell ls /sdcard/Android/data/$PKG/ 2>/dev/null
adb pull /sdcard/Android/data/$PKG/ pulled_external/ 2>/dev/null
Test only against accounts and apps you own — minimum data principle always applies. Page 15
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
# Logout test
adb pull /data/data/$PKG/shared_prefs pulled_before_logout
# -> log out in app ->
adb pull /data/data/$PKG/shared_prefs pulled_after_logout
diff pulled_before_logout pulled_after_logout
Part 11 — Troubleshooting
Item Detail
adb pull: Permission Run adb root first. If adb root fails: you are using a Google Play
denied emulator image. Go back to Day 2 and create a Google APIs image —
root is essential for storage testing.
Shared prefs folder is You did not use the app before pulling. Interact with it (log in, complete
empty actions), then pull again. The app only creates files when it needs to
write data.
tar: cannot create: No The app may not have all standard subdirectories. Use individual adb
such file or directory pull commands per folder instead of the tar method.
sqlite3: file is not a Some .db files are WAL-mode databases with a -wal or -shm
database companion file. Pull all three: adb pull /data/data/$PKG/databases .
(pull the entire folder).
logcat: too much noise to Filter to your app: adb logcat --pid=$(adb shell pidof -s $PKG). Or use -
read s TAG:LEVEL to filter by log tag. InjuredAndroid's tag is
'InjuredAndroid'.
Python decrypt: Padding The IV or key is different from what you expected. Check jadx again for
is incorrect the exact byte values. Some apps XOR-encode the key before use —
trace the code path from the key declaration to the [Link]() call.
Logout test: diff shows no The app does not clear storage on logout (M9-08 = Fail). Alternatively,
difference the token may be in a different file — grep the entire
pulled_after_logout folder for the token value.
Before Day 6 — Homework
7. Complete the full storage pull for InjuredAndroid: shared_prefs, databases, files, cache, logcat,
and external storage. Fill the storage worksheet — every row must have a verdict.
8. Run the logout data-clearing test (Part 7.2) and mark M9-08 in the catalogue.
9. If you found any 'encrypted' data candidate in Part 6: trace the crypto in jadx, find the key, and
attempt decryption. Even a failed attempt teaches you the code navigation skill.
10. Update ATTACK_SURFACE_MAP.md with any new leads from today — any endpoint URLs
you found in databases or logs belong in the Day 11 target list.
Day 6 preview — Traffic interception deep dive
Test only against accounts and apps you own — minimum data principle always applies. Page 16
Mobile Pentest Bootcamp | Day 5 — Insecure Data Storage
Day 6 is the payoff of the Day 2 proxy setup.
You will proxy all of InjuredAndroid's HTTPS traffic through Burp,
build a complete endpoint inventory, and use Burp Repeater to replay
and tamper with requests.
Catalogue IDs: M5-01, M5-04, M5-05, M5-06, M5-08, M6-02, M6-03, M1-06, M8-04.
The endpoints you catalogued in Day 4 and the URL patterns found in logs today
are your starting point — you already know where to look.
Session startup: adb root -> frida-server -> Burp listening -> emulator proxy set.
Test only against accounts and apps you own — minimum data principle always applies. Page 17