[하루한줄] usbliter8 : Apple A12/A13, S4/S5 SoC의 USB 패킷 처리 과정 중 발생하는 BootROM 취약점
URL
Target
- Apple A12, A13 및 S4, S5 SoC
- Apple A12X와 A12Z의 경우 이론상 가능하지만 현재 PoC에 포함되지 않음
Explain
이번 취약점은 ParadigmShift 사에서 usbliter8라는 별칭으로 공개한 BootROM 취약점으로 checkm8와 동일하게 소프트웨어 및 펌웨어를 통해 패치할 수 없는 취약점입니다.
원본 블로그의 경우 PoC 뿐만 아니라 취약점 트리거 이후 익스플로잇을 위한 분석 내용 또한 자세하게 포함되어 있습니다. 해당 내용은 checkm8 이후 공개된 현대적인 SecureROM 취약점으로 하드웨어에서 발생한 취약점이 전체 시스템 권한을 탈취하기까지 좋은 참고자료로 활용될 수 있을 것 같습니다.
background

USB 규격에 따르면 모든 제어 전송은 Setup 트랜잭션으로 시작되어야 합니다. 위 이미지는 host에 의해 전송되는 Setup 트랜잭션을 구성하고 있는 두 개의 패킷입니다. 문서에 따르면 데이터 패킷의 페이로드는 반드시 8-byte 형식을 준수해야합니다.
전달되는 페이로드는 소프트웨어 드라이버에 그대로 전달되어 처리됩니다.
Apple SoC의 USB 컨트롤러는 Synoopsys의 DWC2를 사용합니다. 컨트롤러 내부 동작 방식은 리눅스 커널 관련 내용에서도 찾아볼 수 있기 때문에 여기서는 컨트롤러가 메인 메모리에 데이터를 어떻게 작성하는지에 관한 것을 중점적으로 봤습니다.
AP(Application Processor)는 메모리 영역을 할당하고 해당 physical address를 컨트롤러의 MIMIO 영역에 있는 DOEPDMA에 기록하여 DMA(Direct Memory Access)를 구성합니다.
컨트롤러는 SETUP과 OUT 패킷을 통해 전달된 데이터를 저장하기 위해 이 버퍼를 사용하며 저장된 데이터는 디바이스가 처리할 때 참조합니다.
분석 과정 중 DOEPDMA 레지스터는 USB 컨트롤러가 데이터 청크를 작성할 때 직접적으로 증감되었으며 이는 단순 설정의 용도가 아닌 다음 DMA 전송 시 직접적으로 참고되는 기준임을 확인하였습니다.
root cause
DesignWare USB 컨트롤러—Synopsys사의 제품명으로 보임—의 경우 최대 3개의 Setup 패킷을 메모리에 저장할 수 있습니다. 만약 4번째 Setup 트랜잭션을 수신하는 경우 데이터를 메모리에 기록하기 이전 DMA 베이스 주소를 시작 주소로 초기화합니다.
전달받은 각 패킷의 데이터를 메모리에 작성하는 경우 컨트롤러는 DOEPDMA 레지스터의 크기를 작성한 데이터 크기만큼 증감합니다. 베이스 주소를 초기화하는 방법은 DOEPDMA 레지스터를 24만큼 감산하는 방식으로 진행됩니다.
앞서 설명했듯이 데이터 크기는 8-byte가 되어야 하며 최대 3개의 패킷을 저장할 수 있기 때문에 $8 * 3 =24$라는 결론이 나오기 때문입니다.
가장 큰 문제는 USB 컨트롤러가 8-byte보다 작은 4-byte 패킷 또한 수신받을 수 있다는 점입니다. 만약 3개의 4-byte 데이터를 작성하고 24를 감산하는 경우 DOEPDMA의 값은 처음 시작보다 12만큼 작은 값이 됩니다.
exploit & PoC
이는 USB 컨트롤러의 문제이기 때문에 여러 대상을 분석한 결과 A12와 A13의 SecureROM은 취약한 것으로 확인되었습니다. 반면 A11은 그렇지 않았는데 이는 각 패킷을 받은 이후 USB 드라이버가 수동으로 처음 지정된 주소로 초기화하도록 설계되어 있기 때문입니다.
A12와 A13 모두 취약하였지만 보호기법의 차이로 익스플로잇 과정 또한 차이가 존재하였습니다.
A12의 경우 USB 컨트롤러의 DMA 버퍼가 USB task의 스택에 근접한 힙 영역에 위치하였습니다. 그렇기 때문에 스택에 저장된 LR(Link Register)의 값을 변조하여 PC(Program Counter)를 제어하는 것이 가능하였습니다.
반대로 A13의 경우 PAC가 추가되어 있기 때문에 스택 영역에 위치한 LR을 직접적으로 조작하는 것이 어려웠습니다. PAC는 Pointer Authentication의 약자로 공격자가 PC를 조작 가능하더라도 함수나 데이터 접근 시 실제로 유효한지 검증하는 특별한 명령어를 사용하는 보호기법입니다.
PAC는 ARMv8.3에서 추가된 기술로 A12/A13, S4/S5 모두 적용된 것으로 보이나 이 글에서 다루는 취약점은 A13을 제외하고 영향을 받지 않은 것으로 보입니다.
A13의 경우 성공적으로 PC를 조작하기 위해 여러 단계에 걸쳐 진행하였습니다:
- 힙 영역의 DMA 버퍼 이전에 위치한 DART 관련 데이터를 덮어쓴다.
- DFU 루프를 탈출할 때 아주 제한적인 write primitive로 사용할 수 있다.
- 제어된 데이터를 클린업하는 루틴을 통해 DART 할당과 관련된 글로벌 포인터를 0으로 설정한다.
- 이는 힙 체크섬 검사 중 패닉이 발생하지 않도록 방지하기 위해 필수적이다.
- 다음으로 동일한 클린업 과정 중 전역으로 관리되는 패닉 카운터를
0xf로 덮어쓴다.- 다음 패닉 발생 시 CPU는 재부팅 대신 무한 루프에 진입하게 된다.
- USB task의 컨텍스트 전환 중 사용되는 LR 및 SP가 오염되지 않도록 한다.
- Task가 활성화된 상태일 때 DMA 쓰기 작업을 진행하여 올바른 값으로 덮어쓴다.
- 이후 task 구조체 내에 critical-section depth를 추적하기 위한 필드를 대상으로 할 수 있다.
- 이를 통해 IRQ(Interrupt Request)가 활성화 된 상태로 패닉을 유발할 수 있으며 첫 단계에서 발생한 ISR(Interrupt Service Routine)이 실행 중임에도 무한 루프로 빠지게 하는 것이 가능하다.
- 추가적으로 USB 컨트롤러는 지속적으로 메모리에 데이터가 작성 가능한 상태를 유지한다.
- 위 모든 과정을 진행하여 USB IRQ 핸들러가 포함된 전역 변수에 도달할 때까지 메모리를 자유롭게 덮어쓸 수 있다.
A12와 동일하게 Apple Watch 시리즈에 사용되는 S4와 S5 SoC도 PAC가 적용되어 있지 않기 때문에 간단하게 LR 값을 변경하여 PC를 조작할 수 있었습니다.
관련 PoC는 기술 문서와 공개된 깃허브에서 확인할 수 있습니다.