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 영역에 포인터에 대해 암호학적으로 계산된 시그니쳐 값을 주입함으로써 포인터를 검증하는 방법입니다.
이제 포인터를 사용하려면 세 단계를 거치게 됩니다. 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 를 계산하고 검증하는 방법입니다. 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 을 이용하여 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
-
프로그램 또는 공유 라이브러리에 존재하는 코드 조각을 활용한 공격 ↩