New Mitigations in iOS12/A12

iOS 12 + A12 에는 여러가지 보호기법이 추가되었습니다. 대강 정리하면 다음과 같습니다.

  • iOS 12
mitigation description
Enhanced _longjmp 핵심 레지스터들은 thread id로 보호됨 (TPIDRRO_EL0)
active port free 불가 mach_port_destroy를 통해 inactive 시켜야 free 가능
Sandbox 강화 Backboardd를 공격하면 데몬의 권한을 이용하여 arbitrary file r/w가 가능했으나 Dangerous operation에 대해 제한하는 내용이 추가됨
  • A12
mitigation description
PAC 도입
(Pointer Authentication Code)
BLR/BR 삭제, 새로운 인스트럭션 추가
모든 간접 포인터는 검증을 거침 (C++ vptr, GOT function pointer, …)
스택 보호 강화 SP를 context로 사용하여 return address에 대해 PAC 적용
스택 쿠기가 필요 없어짐
힙 랜덤화 강화 PAC, non-PAC 프로세스간 ASLR을 다르게 적용
PPL Layer 도입 Critical operation 보호
- 특수한 system register에 특정 값이 지정되어야 메모리 접근 가능
- Trust Cache AMFI 일부 등에 사용 됨

이번 post 에서는 A12 에 추가된 보호 기법인 PAC에 대해 알아보려고 합니다.

What is PAC/PAuth?

PAC (Pointer Authentication Code) 또는 PAuth (Pointer Authentication) 라는 용어를 사용합니다. 이 보호 기법은 ARMv8.3a 에서 추가된 기능이며 A12 Bionic 칩에서 사용하고 있습니다. 그 대상이 되는 아이폰 버전은 XR, XS, XS Max 가 있습니다.

Physical address space on 64 bit ARM

64 bit ARM 에서 실제 physical address space 는 64 bit 보다 작습니다. 따라서 사용되지 않는 bit 가 존재하게 됩니다. PAuth는 그 bit 영역에 포인터에 대해 암호학적으로 계산된 시그니쳐 값을 주입함으로써 포인터를 검증하는 방법입니다.

reserved_bits

이제 포인터를 사용하려면 세 단계를 거치게 됩니다. PAC 를 계산하여 포인터에 삽입하는 Sign 단계, 포인터에 대한 PAC 값이 올바른지 검증하는 Auth 단계, 원래의 포인터 값을 사용할 수 있도록 PAC 를 제거하는 Strip 단계입니다.

New Instruction Sets

PAuth 를 위해 새로운 인스트럭션 셋 두 가지가 도입되었습니다. PAC-instruction 과 AUT-instruction 입니다. PAC-instruction 은 포인터에 대한 해시값(PAC)을 계산하고 사용되지 않는 bit 영역에 PAC 값을 삽입하여 Sign 된 포인터를 만듭니다. AUT-instruction 은 Sign 된 포인터에서 PAC 값을 추출하고, PAC 값이 포인터와 매칭되는지 검증합니다.

How to Auth?

PAC-instruction

추가된 인스트럭션 셋을 이용하여 PAC 를 계산하고 검증하는 방법입니다. Sign 된 포인터를 만들기 위해서는 128-bit 의 Signing Key 와 64-bit 의 Context 가 필요합니다.

  • 5개의 Signing Key
    • APIAKey, APIBKey (for instruction pointer)
    • APDAKey, APDBKey (for data pointer)
    • APGAKey (for data)

Signing Key 는 시스템 레지스터에 저장되어 있으며 Exception Level 1 (Kernel) 이상만 읽고 쓸 수 있습니다. Signing Key 는 다섯 종류가 있는데 각각의 용도가 다릅니다. APIAKey 와 APIBKey 는 instruction pointer 에 대해, APDAKey 와 APDBKey 는 data pointer 에 대해, APGAKey 는 data 에 대해 검증하기 위해 존재합니다. 이 중 2개의 A Key (APIAKey, APDAKey) 는 boot 마다 달라지는 키 입니다. APIBKey 는 프로세스마다 달라집니다.

Context 는 동일한 포인터 값에 대해 동일한 키를 이용하여 Sign 하더라도 다른 PAC 값이 계산될 수 있도록 하는 요소입니다. SP 레지스터 등이 Context 로 사용됩니다.

AUT-instruction

포인터를 사용해야할 시점이 오면 AUT-instruction 을 이용하여 PAC 값을 검증합니다. 올바르게 검증된다면 PAC 값을 제거하고 원본의 포인터를 사용하게 됩니다. 검증에 실패한다면 PAC 값을 error 값으로 대체합니다.

Effect

PAC/PAuth 의 도입으로 인해 return address 를 보호하던 stack cookie 가 필요 없어졌습니다. 또한 ROP/JOP 등의 code reuse attack1 이 엄청나게 어려워졌습니다! ROP 에서 사용하던 RET 명령어는 RETAB 로 대체되었으며 이 명령어는 return address 를 APIBKey 와 SP Context 를 이용하여 보호합니다. JOP 에서 사용하던 BLR/BR 명령어는 BLRAA/BRAA 로 대체되었으며 이는 점프할 대상 레지스터를 APIAKey 와 Context (다른 레지스터 또는 0) 를 이용하여 보호합니다. 취약점을 통해 PC 를 가로채더라도, Key 와 Context 를 알지 못하는 공격자는 올바르게 Sign 된 포인터 값을 조작할 수 없으므로 다음의 공격을 수행하지 못하게 됩니다.

Potential Attack

하지만 생각해볼 공격 시나리오는 존재합니다.

  • Reuse Signed Pointers
    • A Key 는 프로세스마다 동일 / 상황에 따라 Context 로 0이 사용될 수 있음
  • Sign Pointers
    • 임의 포인터에 Sign 하는 Gadget 찾기
  • Brute force
  • Arbitrary R/W 를 통해 Sign 된 포인터 읽기
  • 프로세스간 동일한 A Key 사용 => A Key 에 대한 계산
  • Context 0 이 사용되는 일부 함수 포인터에 대해 프로세스 간 포인터 바꿔치기

다음 post 에선 User-space 에서 PAC 를 bypass 한 방법에 대해 DEFCON 27 에서 발표된 내용을 정리해보려고 합니다.

Reference

  • POC 2018, “Era of iOS 12 with A12: End of iOS War?”, Liang Chen
  • DEFCON 27, “HackPac: Hacking Pointer Authentication in iOS User Space”, Xiaolong Bai, Min (Spark) Zheng
  1. 프로그램 또는 공유 라이브러리에 존재하는 코드 조각을 활용한 공격