들어가기 앞서
안드로이드는 전 세계 스마트폰 시장의 상당 부분을 차지하고 있지만, 동시에 다양한 보안 위협에 노출되어 있다. 오늘은 그 중에서도 실습을 통해 Task Hijacking(CVE-2021-33699) 취약점에 대해서 다뤄보고자 한다.
실습에 사용할 소스코드 및 APK 파일은 아래의 Github Repository에서 다운로드 받을 수 있다.
What is Task Hijcaking(CVE-2021-33699)?
개요
Task Hijcaking은 안드로이드 보안 취약점 중 하나로, 악성 앱이 특정 앱의 작업(task)이나 활동(Activity)를 가로채고 제어할 수 있는 취약점이다.
이를 통해 공격자는 사용자가 특정 앱을 사용하는 동안 악성 앱을 백그라운드에서 동작시켜 민감한 정보(권한 수집, 자격 증명 도용 등)를 수집할 수 있다.
다만, 이러한 공격을 성공적으로 수행하기 위해서는 2가지 사전 조건이 필요하며, 이를 이해하기 위해서는 안드로이드 앱의 작업(Task)과 백스택(Back Stack) 대한 구조를 알아야 한다.
안드로이드 작업(Task) 및 백 스택(Back Stack)
안드로이드에서 작업(Task)은 애플리케이션의 활동을 관리하는 단위로, 사용자 인터페이스 상에서 특정 작업을 수행할 때 관련된 일련의 액티비티를 관리하는 역할을 한다.
Task 내의 액티비티는 LIFO 스택 구조로 관리되어, 가장 최근에 시작된 액티비티가 스택의 맨 위에 위치하고, 해당 액티비티가 사용자에게 표시된다.
예를 들어 카카오톡 앱에서 "선물하기" 버튼을 클릭하면 모바일 쿠폰을 구매할 수 있는 새 액티비티가 열리게되며, 해당 액티비티는 카카오톡 Task에서 관리되는 것이다.
백스택(Back Stack)은 Task 내에서 액티비티가 추가되고 제거되는 순서를 관리하는 스택 구조이다. 예를 들어, 카카오톡 앱에서 "선물하기" 버튼을 클릭하여 새 액티비티를 실행하면, 이 액티비티는 백스택 의해 스택 맨 위에 푸시되고 포커스된다. 즉, 이전 액티비티인 카카오톡은 백그라운드로 이동하게 되고 선물하기 액티비티가 포그라운드로 전환되며 인터페이스를 통해 사용자와 상호 작용하게 된다.
그리고 사용자가 뒤로가기 버튼을 눌러 "뒤로" 동작을 수행하면, 현재 활동 중인 "선물하기" 액티비티가 종료되고 스택에서 제거된다. 이로 인해 최 상단 스택이 카카오톡 액티비티가 되며 백그라운드에서 포그라운드로 전환되어 활동이 재개된다.
공격을 위한 사전 조건
Task Hijcaking 공격을 수행하기 위해서는 목표로 하는 타겟 앱이 아래의 2가지 조건을 충족해야 한다.
1. 영향 받는 버전
✓ Android 11 이전 버전을 지원(설치 및 실행 가능)해야 한다.
* 즉, Task Hijacking 취약점은 Android 10 이하 버전의 디바이스에서 동작한다.
2. 루트 런처 태스크 속성
✓ android.intent.category.LAUNCHER 인텐트 카테고리가 지정된 액티비티의 launchMode 값이 singleTask로 설정되어 있어야 한다.
* LaunchMode는 앱에서 Activity가 어떻게 시작될지 결정하는데 사용되는 속성이다. 자세한 설명은 실습 파트에서 다루도록 하겠다.
Hands-On Prepare
실습 환경
항목 | 설명 |
디바이스 | Nox 앱 플레이어 |
OS | Android 9.0 |
루팅 여부 | N |
본 실습에서는 Nox 앱 플레이어를 사용하지만, Android 10 이하 버전의 디바이스가 있는 경우 해당 디바이스를 사용해도 무방하다.
실습 준비
실습을 위해 Github Repository에 공유한 파일을 보면, Target App과 Attacker App 앱으로 나뉜다. Target App은 Task Hijacking에 취약하게 만들어진 앱으로 로그인 기능을 제공하고 있으며, Attacker App은 이러한 Target App을 대상으로 Task를 가로채어 로그인 자격 증명을 탈취할 수 있도록 제작된 Task Hijacking 용 앱이다.
Github Repository를 보면, 앱 소스코드와 별개로 컴파일 된 APK 설치 파일을 제공하고 있다. 따라서, 별도의 소스코드 빌드 없이 제공된 APK 파일을 설치하여 실습을 진행하면 된다.
다만, Attacker App의 경우 제공된 APK 파일을 그대로 사용할 경우 [그림 3]과 같은 로그인 기능을 이용할 수 없다.(탈취한 정보를 공격자 서버로 전송하기 위한 Host 주소가 필요하기 때문이다.) 로그인 기능을 통해 정보 탈취 실습을 하고 싶다면, 아래에 설명된 소스코드 빌드 과정을 따라야 한다. 만약 로그인 기능이 필요하지 않다면, 제공된 APK 파일을 그대로 사용해도 무방하다.
소스코드 빌드
안드로이드 용 소스코드를 APK 파일로 직접 빌드하기 하기 위해서는 Android Studio 개발 도구가 필요하다.
Android Studio는 아래의 링크에서 다운로드 받아 설치를 진행하면 된다,
설치가 완료 되었다면, Android Studio를 통해 Attacker App 프로젝트를 불러온다. "java/cve/test/attacker/MainActivity.kt" 파일의 58번 라인에 Host 주소를 입력한다. 이렇게 설정하면, [그림 3]의 Attacker App에서 로그인을 시도 시 입력한 계정 정보(Email, Password)가 설정한 서버로 GET 메소드를 통해 값을 전송한다.
만약, 정보 파싱 용도의 별도의 웹 서버가 없다면, Dream Hack Tools를 이용할 수 있다.
Dream Hack Tools는 웹 인퍼페이스를 통해 사용자 접속 기록과 더불어 여러 종류의 인디코딩과 암복호화를 지원하는 도구이다.
첨부된 링크를 통해 Dream Hack Tools에 접속한 후, Request Bin 페이지에서 "링크 생성" 버튼을 클릭하여 생성된 URL을 복사한다.
* 참고로 생성된 링크는 가상 URL로 일정 시간이 지나면 폐기 된다.
복사한 URL을 [그림 6]과 같이 host() API 인자 값으로 입력해준다.
[그림 7]과 같이 상단의 "Build -> Build App Bundle(s) / APK(s) -> Build APK(s)"를 클릭해 소스코드를 APK 설치 파일로 빌드해준다.
공격 분석 및 시연(개념 증명)
이 단락에서는 단순한 공격 시연이 아닌, 공격자의 관점에서 분석 과정을 거쳐 공격을 시연하고 개념을 증명할 예정이다.
공격 시나리오
Task Hijacking은 특정 다수를 무차별적으로 공격하기보다는 특정 타겟을 대상으로 하는 공격에 더 적합한 특성을 지닌다
- 공격자는 사전에 사용자가 자주 사용하는 모바일 앱 목록을 획득한다. 그 후 Task Hijacking에 취약한 앱을 선별 후 Task를 가로채는 악성 앱을 제작하고 배포 URL을 사용자에게 전달한다. 이때, 사용자가 해당 링크를 클릭해서 다운로드 받을 수 있도록 유도 멘트를 추가로 작성한다.
- 사용자는 공격자로부터 전달 받은 URL을 통해 앱을 다운로드 받아 설치를 진행한다.
- 사용자는 평소와 같이 자주 사용하던 앱을 이용한다.
- 백그라드에서 동작 중이던 설치한 악성 앱이 실행된 앱의 Task를 가로채며, 해당 앱과 동일한 로그인 화면을 사용자에게 출력한다.
- 사용자는 별 다른 의심 없이 평소와 같이 앱에 로그인을 시도하게 된다. 이때, 입력된 사용자 계정 정보는 공격자 서버로 전송되며, 정보 탈취가 이루어진다.
취약점 분석(Target App)
Target App에 대한 Test Hijacking 취약 유/무를 확인하기 위해 target.apk 설치 파일에서 AndroidManifest.xml 파일을 추출한다.
* AndroidManifest.xml 파일 추출 방법은 필자가 포스팅한 다른 게시물에 여러 번 설명을 했기 때문에 추출 과정은 생략하겠다.
앞서 [2.3 공격을 위한 사전 조건] 단락에서는 Task Hijacking 공격을 수행하기 위해 목표 대상 앱이 두 가지 조건을 충족해야 한다고 설명했다. 첫 번째 조건은 해당 앱이 Android 11 이전 버전에서 설치 및 실행 가능해야 한다는 것이고, 두 번째 조건은 루트 런처로 지정된 액티비티의 launchMode 속성이 "singleTask"로 설정되어 있어야 한다는 것이다.
위 조건을 확인하기 위해 target.apk 설치 파일에서 추출한 [그림 9]의 AndroidManifest.xml 파일을 살펴보겠다.
① "uses-sdk" 태그를 보면 minSdkVersion 값이 "24"로 설정된 것을 볼 수 있다. 이는 해당 앱이 지원하는 Android OS 최소 버전이 Android 7.0이라는 뜻이다.
* SDK 24는 Android 7.0 버전에 해당한다.
② "cve.test.taskhijacking.MainActivity" 액티비티가 런처 모드로 설정되어 있음을 알 수 있다. 이는 사용자가 디바이스에서 앱 실행 아이콘을 클릭했을 때 가장 먼저 보여지는 활동(Activity)이라는 뜻이다.
③ "cve.test.taskhijacking.MainActivity" 액티비티의 설정을 확인해보면, launchMode 속성 값이 2로 설정되어 있다. 숫자 2는 "singleTask"를 의미한다.
"singleTask" 모드로 지정된 경우 새 작업의 루트에서 활동(Activity)을 생성하거나, 동일한 어피니티(Affinity)를 가진 기존 작업(Task)에서 활동을 찾아 시작한다. 또한, 해당 활동이 백그라운드에 있을 경우, 전체 작업이 포그라운드로 전환된다. 이로 인해 공격자 앱이 취약한 앱과 동일한 어피니티 값을 설정할 수 있게 되면, 공격자 앱의 활동(Activity)과 취약한 앱의 활동이 동일한 작업(Task)에 함께 공존하게 되어 Task Hijacking이 가능해진다. 이는 공격자가 악의적인 의도로 해당 작업을 가로채어 사용자의 의도와 상관없이 다른 작업을 수행하도록 유도 할 수 있게 된다.
Affinity?
어피니티(Affinity)는 애플리케이션의 Activity가 어떤 작업(Task)과 연관될지를 정의하는 속성이다. 어피니티는 애플리케이션의 화면이 다른 애플리케이션과 어떻게 상호작용하는지, 그리고 사용자가 앱을 시작할 때 어떤 작업이 포그라운드에 나타나는지를 결정하는 역할을 수행한다.
launchMode의 각 속성에 대한 설명은 아래를 참고하면 된다.
launchMode는 앱의 Activity가 어떻게 시작될지를 결정하는 속성으로, 아래와 같이 총 5가지 모드를 제공한다.
1. standard(0)
✓ Activity를 호출할 때 마다 새로운 인스터스가 생성된다.
2. singleTop(1)
✓ 만약 Activity가 스택의 맨 위에 이미 존재하는 경우, 새 인스턴스를 생성하지 않고 기존 인스턴스를 재사용한다.
3. singleTask (2)
✓ Activity가 이미 존재한다면, 그 Activity를 최상위로 가져오고, 그 위에 있는 다른 Activity들을 모두 제거한다.
✓ 다른 활동이 해당 작업의 일부가 될 수 있다.
4. singleInstance (3)
✓ 다른 애플리케이션이 이 Activity와 함께 실행되지 않으며, 이 Activity는 오직 하나의 인스턴스만 가질 수 있다.
5. singleInstancePerTask(4)
✓ 활동은 작업의 루트 활동, 즉 작업을 만든 첫 번째 활동으로만 실행된다. 따라서 한 작업에는 이 활동의 인스턴스가 하나만 있을 수 있다.
위 일련의 분석 과정을 통해 Target App 앱은 Task Hijacking에 취약하다는 것을 알 수 있었다.
취약점 분석(Attacker App)
Target App을 살펴 봤으니, 이제는 Target App을 대상으로 Task Hijacking 공격을 수행할 Attacker App을 살펴보겠다.
Android Studio에서 Attacker App 프로젝트를 열고, AndroidManifest.xml 파일을 확인한다.
① application 태그에서 taskAffinity 속성을 통해 어피니티(Affinity)를 "cve.test.taskhijacking(Target App 패키지 이름)"으로 지정하고 있다. 이 경우, [3.5.1 취약점 분석(Target App)] 단락에서 설명한 바와 같이 Target App에 설정된 launchMode="singleTask" 옵션에 의해 Attacker App의 활동(Activity)과 Target App의 활동이 동일한 작업(Task) 내에 공존하게 된다. 즉, Attacker App이 백그라운드 상태에서 동작 중일 때 Target App이 실행되면, Attacker App의 작업 공간에 Target App의 활동이 포함되는 것이다. 결과적으로 Attacker App이 Target App의 작업을 가로채게 된다.
② excludeFromRecents 속성은 특정 액티비티(Activity)가 최근 앱 목록(Recent Apps)에서 제외되도록 설정하는 옵션으로 이 속성이 설정된 액티비티는 실행 시 디바이스의 최근 실행된 앱 목록에 표시되지 않는다. 다만, 이 옵션의 경우 Task Hijacking 공격을 수행하기 위한 필수 설정은 아니며, 주로 악성 앱이 실행 흔적을 숨기기 위해 사용된다.
다음으로 "java/cve/test/attacker/MainActivity.kt" 클래스의 onCreate() 함수를 살펴보겠다. onCreate() 함수는 Activity가 생성될 때 호출되며, 앱의 초기화 및 설정 작업을 수행한다.
* "MainActivity" 액티비티는 Attacker App의 루트 런처로 설정되어 있어 앱 실행 시 가장 먼저 사용자에게 호출되는 액티비티이다.
onCreate() 함수 안에는 moveTaskToBack() 함수가 호출되고 있다. 이 함수는 현재 Activity가 속한 작업(Task)을 백그라운드로 이동시키는데 사용된다. 즉, 앱이 실행되어 루트 런처 속성에 의해 MainActivity가 호출되면, 앱이 포그라운드에서 바로 백그라운드로 전환되어 사용자에게 앱의 실행 유무를 숨길 수 있게 된다.
이 설정도 이전의 excludeFromRecents 옵션과 마찬가지로 Task Hijacking 공격을 수행하는 데 필수적이지는 않으며, 주로 악성 앱이 실행 흔적을 숨기기 위해 사용한다.
공격 시연(개념 증명)
Target App과 Attacker App을 [그림 12]와 같이 Nox 앱 플레이어에 설치한다. "TaskHijacking" 이름을 가진 앱이 Target App이고, "TH Attacker" 이름을 가진 앱이 Attacker App이다.
설치가 완료되면 먼저 TH Attacker(Attacker App) 앱을 실행한다. 앱을 실행해도 아무 반응이 없는 것처럼 보이는데, 이는 [4.3 취약점 분석(Attacker App)] 단락의 [그림 10]과 [그림 11]에서 설명한 excludeFromRecents 옵션과 moveTaskToBack() 함수 때문으로 실제로는 앱이 실행되고 있는 상태이다.
excludeFromRecents 옵션에 의해 앱이 실행되었어도 [그림 13]과 같이 최근 실행된 앱 목록에 표시되지 않으며, moveTaskToBack() 함수에 의해 앱이 실행되자마자 바로 백그라운드로 전환되므로, 앱이 아무런 반응이 없는 것처럼 보이게 된다.
TaskHijacking(Target App) 앱을 실행하면 [그림 14]와 같이 로그인 화면이 표시된다. 이때, 백그라운드에서 동작 중인 TH Attacker(Attacker App) 앱에 설정된 taskAffinity 속성으로 인해 TaskHijacking(Target App) 앱의 활동(Activity)이 라우팅되며 Attacker App의 작업(Task)에 포함된다.
[그림 15]는 TaskHijacking(Target App) 앱이 실행될 때, 해당 앱의 활동(Activity)이 라우팅되어 TH Attacker(Attacker App) 앱의 작업(Task) 스택에 어떻게 쌓이는지를 보여주는 일련의 흐름을 나타낸다. 결과적으로, TaskHijacking(Target App) 앱의 활동이 TH Attacker(Attacker App) 앱의 작업에서 실행되어 TH Attacker(Attacker App) 앱이 TaskHijacking(Target App) 앱의 Task를 가로챌 수 있게 된다. 이러한 현상은 TaskHijacking(Target App) 앱에 설정된 launchMode="singleTask" 옵션과 TH Attacker(Attacker App) 앱에 설정된 taskAffinity 속성으로 인해 발생하는 현상이다.
ADB 명령을 통해 TaskHijacking(Target App) 앱의 실제 스택 구조를 확인해보면, [그림 16]에서 볼 수 있듯이 "t3"라는 작업(Task) 공간에 TH Attacker(cve.test.attacker) 앱의 활동(Activity)과 TaskHijacking(Target App) 앱의 활동이 함께 배치되어 있다. 그리고 스택 순서(Hist #)를 통해 TaskHijacking(Target App) 앱이 TH Attacker(Attacker App) 앱 위에서 실행되고 있는 것을 확인할 수 있다.
디바이스에서 최근 앱 실행 목록을 열어보면, [그림 17]의 오른쪽 화면처럼 앱의 이름이 TaskHijacking이 아닌 TH Attacker로 표시되는 것을 볼 수 있다. 이는 TaskHijacking(Target App) 앱이 TH Attacker(Attacker App) 앱 위에서 실행되고 있음을 의미한다.
Nox 앱 플레이어에서 뒤로가기 버튼을 클릭하면 작업(Task) 스택 구조에 따라 TH Attacker(Attacker App) 활동이 호출되어 Task Hijacking이 정상적으로 수행된 것을 확인할 수 있다
[그림 19]는 [그림 18]에서 기존 활동(Activity)가 제거되고 새로운 활동이 호출되는 일련의 과정을 작업(Task) 스택 구조의 흐름도로 나타내고 있다.
사용자는 [그림 20]과 같이 Task Hijacking된 TH Attacker(Attacker App) 앱을 평소에 사용하던 앱으로 착각하여 로그인 시도를 하게 된다. 이때 입력된 로그인 자격 증명 정보는 [그림 6]에서 설정한 공격자 서버로 전송되어 탈취된다.
공격 시연(동영상)