Choice Jacking 실증 분석과 보안 모델의 한계
본 포스팅은 필자의 학위연구를 기반으로 작성한 포스팅이다.
1. Choice Jacking이란? (사례연구 분석)
25년 8월 34th USENIX SECURITY SYMPOSIUM 에서는 키보드나 마우스로 위장하여서 권한 승인 팝업을 눌러버리게 하는 일명 "Choice Jacking"을 발표하였다.

위 그림은 해당 논문에서의 공격 테크닉별 해당 논문의 경우에서는 Galaxy A14 S23 등 안드로이드 14 이하를 기준으로 위장을 성공하여서 공격 벡터로 활용하였다. 애플의 아이패드와 같은 경우에는 MTP나 ADB라는 프로토콜을 따로 사용하지 않아 T3 Targets을 활용하여서 공격을 시도하였다.
이 논문에서 말하는 T1 공격은 AOAP 프로토콜 구현 결함을 악용하였고, T2는 레이스컨디션을 악용하였고 T3는 블루투스 HID 장체 페어링을 악용한 공격이였다.
그 결과는 아래 표와 같이 나타났다.
| 벤더 | 디바이스 수 | T1 성공률 | T2 성공률 | T3 성공률 | 잠금 상태 공격 | ADB 접근성공 |
| 삼성 | 4 | 100% | 100% | 100% | X | X |
| 샤오미 | 1 | 100% | 0% | 0% | X | O |
| 비보(BBK) | 1 | 100% | 100% | 100% | X | X |
| 오포(BBK) | 1 | 100% | 100% | 100% | (MTP) | X |
| 하웨이 | 1 | 100% | 100% | 100% | X | X |
| 아너(하웨이) | 1 | 100% | 100% | 100% | (MTP) | X |
| 구글 | 1 | 100% | 100% | 100% | X | X |
| 애플 | 1 | X | X | 100% | X | X |
필자는 이 논문을 기반으로 하여서 25년 9월 기준 최신 안드로이드 폰(안드로이드 16, 안드로이드 18)에서도 공격이 가능한지 실증을 위하여 테스트 하였다.
2. 실험 도구
| Category | Detailed Specifications |
| Attack Host | Raspberry Pi 4 Model B (Broadcom BCM2711, 4GB RAM) |
| Operating System | Raspberry Pi OS (Linux Kernel 5.x) |
| Target Device | Android 18, 16 Version Device |
| Connectivity | USB Type-C with OTG (Gadget Mode Enabled) |
| Emulated Device | HID Keyboard, MTP Device, ADB Debugging Host |
| Attack Tools | ConfigFS for HID Emulation, Custom ADB Python Scripts |
위장 공격을 위해 사용한 기기는 라즈베리 4 모델 B를 사용하였다. OS는 일반 라즈베리 파이 OS(리눅스 커널 5버전)를 사용하였고, 삼성의 핸드폰 A 시리즈 1개를 사용하였다. 연결에 사용하기 위해서 라즈베리 파이의 가젯모드를 활성화하여 C타입을 활용하였다. 여기서 사용하는 가젯모드란 라즈베리 파이를 PC나 다른 외부기기에 키보드 혹은 마우스로 사용가능하도록(입력을 주입할 수 있도록) 하는 라즈베리 파이에서 지원하는 모드이다. 라즈베리 파이는 HID 기기 중 키보드로 위장하였고, MTP Device 와 ADB의 Debugging Host로 위장하여 에뮬레이팅 하는 것을 목표로 한다. HID와 ADB는 모두 파이썬과 Shell Scripting으로 도구를 작성하였다.
2.1 왜 이도구를 선택하였는가?
위 논문과 같은 경우에서는 라즈베리 파이 4를 사용했다고 한다. 하지만 간단한 도식도만 존재할 뿐 어떻게 구성해야하고 구성을 위해 필요한 것이 무엇인가는 자세히 명시가 안 되어 있었다. 필자는 라즈베리 파이 4와 라즈베리 파이 5를 사용해보면서 테스트 해본 결과, 라즈베리 파이 4와 5는 USB 가젯모드는 C타입으로만 진행된다는 것을 확인하였고, 두 보드와 같은 경우에는 C타입을 충전용 케이블로 사용하기 때문에 전력을 받을 필요가 있었다.

위 그림은 Choice Jacking을 위해 사용할 도구의 설계도이다. 이 설계대로 진행을 하면서 발생된 문제가 생겼다. 라즈베리파이 제로와 라즈베리 파이 4를 제외하고는 매우 불안정하다는 것이다. 필자는 제로를 따로 구할 수 있는 기회가 없어서 라즈베리 파이 5와 4를 사용하였는데, 5는 하드웨어 기반의 오류로 인해서 가젯모드가 불안정하게 작동하게 되어서 여러 실험을 통해 4를 사용하기로 하였다.
3. 실험 진행
실험은 위 설계도를 기반으로 하여서 아래와 그림과 같은 시나리오로 진행하였다.

전체 실행 시나리오는 아래와 같다.
1. 위장코드와 권한승인코드, 유출코드가 삽입이 되어 있는 위장된 충전기에 사용자 스마트폰을 USB로 연결한다.
2. 스마트폰과 디바이스 정보를 교환한다.
3. 위장된 충전기는 키보드로 위장하여서 MTP 권한을 습득한다.
4. MTP 권한에서의 데이터를 추출한다.
5. ADB 권한을 습득한다 ( 만약, 개발자 모드가 안 켜져 있다면 개발자 모드 부터 실행시킨다)
6. ADB Shell을 사용하여서 유출한다.
전체적인 알고리즘의 파이프라인은 의사코드로 아래와 같이 진행이 된다.
/*Input: TargetDevice, AttackHost, Config
Output: ExfiltratedData*/
// Step 1: Initialize HID gadget interface
// Configure USB gadget as composite device (HID + MTP + ADB)
SetupHIDGadget();
// Step 2: Wait for target device connection
WaitForUSBConnection();
// Step 3: Detect MTP permission popup
if (IsPopupVisible("USB_PERMISSION")) {
// Inject keystrokes to approve MTP access
KeyboardChoiceJacking(MTP_PROFILE);
}
// Step 4: Verify actual MTP file system accessibility
if (!CheckMTPAccess()) {
return ERROR_MTP_FAILED; // Stop attack if access not granted
}
// Step 5: Collect media files via MTP
CollectViaMTP();
// Step 6: Trigger ADB connection request
TriggerADBConnection();
// Step 7: Detect ADB authorization popup
if (IsPopupVisible("ADB_AUTH")) {
// Inject keystrokes to approve ADB authorization
KeyboardChoiceJacking(ADB_PROFILE);
}
// Step 8: Wait until ADB state becomes AUTHORIZED
if (!WaitForADBState(AUTHORIZED)) {
return ERROR_ADB_FAILED; // Stop attack if authorization fails
}
// Step 9: Collect sensitive data via ADB shell
CollectViaADB();
위 파이프라인은 USB 기반 복합 장치를 구성한 후, 대상 장치와 연결하여 단계적으로 데이터 접근 권한을 확보하고 정보를 수집하는 파이프라인이다.
먼저 공격 호스트는 HID, MTP, ADB 기능을 포함한 USB gadget 인터페이스를 초기화한 뒤 대상 장치와의 연결을 대기한다. 연결 이후, 시스템은 USB 파일 접근(MTP) 관련 권한 요청 팝업을 탐지하고, 입력 인터페이스를 통해 승인 동작을 수행한다. MTP 접근이 정상적으로 활성화되었는지 검증한 후, 미디어 파일 등 공개 저장소 데이터를 수집한다.
이후 ADB 연결 요청을 발생시키고, 디버깅 인증 팝업을 탐지하여 동일한 방식으로 승인 절차를 수행한다. ADB 상태가 인증(authorized)으로 전환되었는지 확인한 뒤, ADB 셸을 통해 추가적인 민감 정보에 접근한다.
전체 절차는 USB 권한 승인 흐름과 입력 신뢰 모델을 기반으로 단계적 접근 권한을 확보하는 구조를 가진다.
위 파이프라인을 실행하기 위해서는 먼저 라즈베리 파이 4를 HID로 위장하기 위한 가젯모드를 설정해야한다. 위 파이프란인의 Step 1에 들어가는 SetHIDGadget()을 위한 의사코드는 아래와 같다.
// Algorithm: RPi4 USB Gadget (Mass Storage) with Virtual Backing File
typedef struct {
const char *g; // gadget name, e.g., "g1"
const char *img; // backing file path, e.g., "/var/gadget/storage.img"
size_t img_mb; // size in MB
const char *udc; // UDC name (NULL => auto)
} cfg_t;
int EnableGadgetMS(const cfg_t *c) {
LoadModules("dwc2", "libcomposite");
MountConfigFS();
CreateSparseFile(c->img, c->img_mb); // backing store (virtual storage)
// Optional: FormatFileSystem(c->img, "FAT"); // if host mounting is needed
Mkdir("/sys/kernel/config/usb_gadget/%s", c->g);
WriteHex("/.../%s/idVendor", 0x1d6b); // minimal descriptors (example)
WriteHex("/.../%s/idProduct", 0x0104);
Mkdir("/.../%s/functions/mass_storage.0", c->g);
WriteStr("/.../%s/functions/mass_storage.0/lun.0/file", c->g, c->img);
WriteInt("/.../%s/functions/mass_storage.0/lun.0/ro", c->g, 0);
Mkdir("/.../%s/configs/c.1", c->g);
Symlink("/.../%s/functions/mass_storage.0", "/.../%s/configs/c.1/ms", c->g, c->g);
if (!c->udc) c->udc = DetectUDC();
WriteStr("/.../%s/UDC", c->g, c->udc); // bind => gadget enabled
return 0;
}
위 의사코드는 ConfigFS 기반 USB Gadget 프레임워크를 사용하였다. 시스템은 libcomposite 모듈을 통해 gadget 객체를 생성하고, Mass Storage 기능을 활성화하여 가상 메모리 공간을 논리적 유닛 번호에 매핑하였다. Mass Storage 기능은 C 포트로 입력하는 단자에 HID 입력이 들어갈 가상 메모리 공간이 필요하여서 가상 저장소를 생성하였고, 가상 메모리 공간을 따로 활성화하지 않는 경우에는 HID기기로 정상적으로 인식이 되지 않는다.
3.1 실험 시연
위 파이프라인을 전제로 진행을 하게 되면 1차적으로 MTP 권한 단에서 접근상태 확인은 아래와 같이 된다.

위 상태는 USB를 통한 기기충전과 저장장치가 동시에 인식이 된 것을 확인할 수 있다. 여기서 저장장치로 인식이 된 이유는 키보드 입력을 줄 수 있는 메모리가 저장장치로 인식이 된 것이다. 즉, 충전을 하는 키보드로 인식이 된 것이라는 논리적으로 이해되지 않는 상황이 일어난 것이다. 이 상태에서의 패킷은 아래 그림과 같이 된다.

해당 패킷은 본래 PC와 스마트폰이 USB로 연결하였을때 서로간의 파일 포맷을 협상하는 단계의 패킷 흐름이다. 즉 위장된 충전기와 스마트폰이 연결이 됐을 경우에는 충전기, 키보드, PC 의 상태가 모두 공존하는 상태가 되었다는 것이다. 즉, PC면서 키보드로 입력을 할 수 있다는 것이고 그 상황에서 키보드의 입력값이 검증없이 들어갈 수 있다는 것이다. 실험이 성공적으로 진행이 되었으면 아래 그림과 같이 파일 접근 허용 팝업이 뜨고, 그 팝업을 키보드 입력 인터럽트를 사용해서 허용 버튼을 누를 수 있다.

ADB도 연결에 성공하여서 파일들이 유출되고 저장되는 것을 확인할 수 있었다.
3.2 USENIX 발표 사례와 안드로이드 18 환경에서의 비교 분석
필자는 USENIX 발표와 실제 안드로이드 18에서의 실험을 해보았다. 그에 대한 결과는 아래의 표와 같이 정리하였다.
| 기기 | T1 성공 여부 | T2 성공 여부 | T3 성공 여부 | 장금 상태 공격 | ADB 접근 성공 |
| S시리즈 (안드로이드 16) |
O | X | O | O | O |
| A시리즈 (안드로이드 18) |
O | X | X | O | O |
안드로이드 보안 패치 수준은 A시리즈는 25년 9월 수준, 18은 25년 11월 수준으로 진행하였다. T2 공격 벡터는 레이스 컨디션을 사용하는 공격이였는데, HID로 위장하여서 레이스 컨디션을 시도하였으나 레이스 컨디션을 시도하는 경우 일정 바이트 이상에서는 입력값이 Segmentation Error등 접근이 안 되는 것을 확인하였다. 해당 발표 이후(혹은 이전)에 레이스 컨디션은 치명적인 권한 상승을 야기할 수 있는 문제이기 때문에 해당 사항을 보완한 것을 보인다.
T1의 경우에는 마우스를 사용해서 입력값을 사용하여 승인 팝업 입력을 시도하였을 때는 승인 팝업의 클릭 위치가 디스플레이 크기 OneUI 버전마다 상이한 문제로 자동화 스크립트를 사용하긴 어려웠으나 키보드 입력 같은 경우에는 통일되게 사용이 가능한 것으로 확인이 되었다. 따라서 해당 공격 벡터는 아직 유효하다는 것을 확인하였다.
T3의 경우에는 블루투스를 이용하여 HID 위장을 하는 경우인데, 안드로이드 18기준으로 블루투스로 HID 기기가 들어올 경우 승인절차가 추가로 있는게 확인이되었다. 해당 상황에서는 입력값이 들어가진 않은 문제가 생겨 T3 백터에서는 안드로이드 18 버전에서는 성공하지 못 하였으나 안드로이드 16 버전에서는 블루투스를 활용한 공격이 성공적으로 작동되었다.
4. 결과 분석
데이터들의 유출 정보는 아래와 같다.
| 데이터 유형 | 소요 시간 | 데이터 특징 |
| 문서파일 | 200kb 당 0.2초 | |
| 사진 | 200kb 당 0.1초 | GPS과 촬영날짜등 포함 |
| 동영상 | 200kb 당 0.3초 | GPS과 촬영날짜등 포함 |
| 연락처 | 2.26초 (400명부) | 닉네임 데이터 또한 추출 |
| SMS | 1.42초 (2400통 기준) | 인증번호, 임시비밀번호 데이터 포함 |
| MMS | 1.72초 (3200통 기준) |
해당 정보들의 보안의 방어 실패 지점은 사용자 승인 UI 팝업에 HID 장비의 입력이 들어간다는 점과, USB HID 입력 값에 대한 검증 절차 처리가 없다는 구조에서 일어난다. 해당 문제는 AOAP(AOA 프로토콜)의 현시점의 문제로 ADB 인증 키를 고도화 하기 위하여 RSA 서명 기반 교환이 있어 암호학적 안전한 설계를 하여도 결국 사용자 승인 UI에서 승인이 완료되니 ADB 인증이 뚫려버린 것이다.
이와 같이 추출된 데이터들은 단순히 개인의 데이터들이 유출되는 것으로 끝나는 것이 아닌, 문서파일 유출로 인한 기업 문서등의 유출이 가능할 것이며, SMS나 MMS 유출을 통하여 임시비밀번호등을 갈취하는 행위로 이어질 수 있다.
4.1 AOA 프로토콜이란?
Android Open Accessory Protocol(AOAP, 안드로이드 오픈 액세서리 프로토콜)은 외부 USB 하드웨어(액세서리)가 USb 호스트 역할을 하여 안드로이드 기기와 통신할 수 있게 하는 구글의 표준 프로토콜이다. 안드로이드 USB 액세서리는 AOAv1 에서 adb 디버깅을 지원할 수 있게 되었고, v2 부터는 HID 기능을 지원하게 되었다. 해당 프로토콜은 오디오, USB 마우스 및 키보드, Android Auto 등에서 사용하는 프로토콜이며 지금 USB로 연결된 기기가 "신뢰성 있는 기기"이다를 전제로 구현이 되어 있는 프로토콜이다.
이러한 문제는 단순히 AOAP에서만 일어나는 것이 아니라, Killer USB나 USB Bootloader based Malware 등 PC에서도 일어나는 문제이다. 주된 이유는 USB 입력이라는 것은 "빠르고 간편한 연결"에 초점이 되어 있기 때문이다.
5. 마무리
SELinux 정책 강화 및 보안 패치로 Choice Jacking 기법 중 레이스 컨디션 기반 공격(T2)은 일부 보완되었으나, 입력 계층에 대한 신뢰 모델은 근본적으로 변경되지 않았다. Android 보안 정책은 저장소 접근 통제와 애플리케이션 권한 격리에 집중되어 있으며, USB 기반 입력 채널에 대한 무결성 검증은 포함되어 있지 않다.
현재 Android의 주변 기기에 대한 신뢰 모델은 “물리적으로 연결된 장치는 사용자의 의도를 반영한다”는 전제를 기반으로 설계되어 있다. 그러나 본 실험 결과는 해당 전제가 더 이상 항상 성립하지 않음을 보여준다. HID 입력은 사용자 입력과 동일한 신뢰 수준으로 처리되며, 사용자 승인 UI는 이러한 입력을 구분 없이 수용한다. 그 결과, 암호학적으로 안전하게 설계된 ADB 인증 체계조차 사용자 인터페이스 계층에서 우회될 수 있다.
이는 단순한 AOAP 구현상의 취약점이 아니라, 입력 신뢰 경계(Trust Boundary)의 구조적 위치 설정 문제에 가깝다. 향후 보안 강화는 프로토콜 수준의 인증 추가를 넘어, 입력 장치에 대한 신뢰 재정의와 UI 기반 승인 모델의 재설계를 포함해야 할 것이다. 예를 들어, 입력 이벤트의 출처를 구분하거나, 사용자 승인 과정에서 입력 채널의 무결성을 검증하는 보조 메커니즘이 고려될 수 있다. 또한 엔드포인트 관점에서 입력 이벤트와 권한 상승 행위를 연계 분석하는 보호 기법(E-DLP와 유사한 접근 방식) 역시 보완적 대응 방안으로 검토될 수 있다.
작성자 : FaranSky (강창협)
출처
[1] Florian Draschbacher, Lukas Maar, Mathias Oberhuber, and Stefan Mangard. 2025. CHOICEJACKING: compromising mobile devices through malicious chargers like a decade ago. Proceedings of the 34th USENIX Conference on Security Symposium. USENIX Association, USA, Article 225, 4363–4379.
[2] Android Open Accessory https://source.android.com/docs/core/interaction/accessories/protocol?hl=ko
[3] C. H. Kang, "Analysis of USB Masquerade Connection Vulnerability and Proof of Concept on Latest Android Smartphones," Master's thesis, Mokpo National University, Feb. 2026.
[4] C. H. Kang, M. K. Kim, and M. S. Kim, "Analyzing the Possibility of Privacy Information Leakage in ADB Communication Using USB," Proc. of CISC-W, 2025.
'Security > Mobile' 카테고리의 다른 글
| [3탄] 취약점 점검 전 알아야 할 모바일 실전 진단 프로세스와 런타임 제어 (0) | 2026.06.01 |
|---|---|
| 모바일 앱 취약점 분석에서의 후킹(Hooking) (0) | 2026.05.31 |
| [2탄] 취약점 점검 전 알아야 할 Android 파일 시스템과 보안 아키텍처 (1) | 2026.04.30 |
| [1탄] 취약점 점검 전 알아야 할 iOS 파일 시스템과 보안 아키텍처 (0) | 2026.02.28 |
