ARM Pointer Authentication¶
Armv8.3 cryptographic CFI: pointer'ları (return address'ler, code/data pointer'ları) kullanılmayan high bit'lerde saklanan keyed bir PAC ile sign'la ve kullanmadan önce authenticate et — corrupt edilmiş bir pointer check'i fail eder ve dereference'ta fault verir.
Mechanism¶
Neden çalışır
Bir 64-bit virtual address, 64 bit'ten çok daha azını kullanır, dolayısıyla bir pointer'ın üst bit'leri boştadır. PAC, keyed bir cryptographic signature'ı o bit'lerin içine tıkar. Kullanılabilir bir pointer forge etmek için bir attacker'ın o değer için doğru PAC'ı üretmesi gerekir — secret key olmadan infeasible'dır.
PAC*, (pointer value, 64-bit bir context/tweak, 128-bit secret bir key) üzerinden bir PAC hesaplar ve onu pointer'ın kullanılmayan bit'lerine yazar.AUT*, PAC'ı yeniden hesaplar ve doğrular, başarı durumunda onu strip eder.- Failure durumunda hardware branch yapmaz; "an error code is placed in the pointer's extension bits so that a fault is triggered if the pointer is dereferenced" — corrupt high bit'ler adresi non-canonical yapar, dolayısıyla onu kullanmak bir translation fault raise eder.
Cipher QARMA'dır (QARMA-64: 128-bit key, plaintext olarak 64-bit pointer, tweak
olarak 64-bit context); implementation'lar başkasını ikame edebilir. Beş key vardır:
APIAKey, APIBKey (instruction pointer'ları), APDAKey, APDBKey (data
pointer'ları), APGAKey (keyfi data'nın generic signing'i). Standart pac-ret ABI'si,
return address'i prologue'da context olarak SP ile sign'lar ve epilogue'da
authenticate eder.
Invariant şu: yalnızca key'i tutan code, authenticate olan bir pointer mint
edebilir, dolayısıyla attacker adreslerini ikame eden ROP/JOP, AUT*'ta fail eder.
Walkthrough¶
1. Return-address signing ile build et.
$ clang --target=aarch64-linux-gnu -mbranch-protection=pac-ret -c f.c
# or: gcc -mbranch-protection=pac-ret
2. Prologue/epilogue PAC instruction'ları kazanır. Context olarak SP ile A instruction key'ini kullanarak:
foo:
paciasp ; sign LR with APIAKey, context = SP
stp x29, x30, [sp,#-16]!
...
ldp x29, x30, [sp],#16
autiasp ; authenticate LR; corrupted -> poisoned pointer
ret ; ret to a non-canonical addr -> translation fault
(PACIBSP/AUTIBSP, B key'ini kullanır; XPACI, bir PAC'ı strip eder; RETAB,
BLRAA vb. auth'u branch'in içine katlar.)
3. Bypass denemesinin fault verdiğini gözlemle. Stack'teki kayıtlı LR'ı overwrite
et; autiasp'ten sonra değerin top bit'leri poison'lanır, dolayısıyla ret attacker'ın
gadget'ına değil, geçersiz bir adrese jump eder → SIGSEGV.
Key'ler, oracle'lar ve reuse
PAC'ın gücü, key'in gizliliği ve context'in benzersizliğidir. Geçerli bir (pointer, PAC) çifti farklı bir context'te yeniden kullanılabiliyorsa ya da code, attacker data'sını sign'layıp sonucu açığa çıkarmaya zorlanabiliyorsa (signing oracle), CFI key bilinmeden bypass edilebilir. Same-context pointer swap'leri (örn. ikisi de SP ile sign'lanmış iki return site'ı) bilinen bir zayıflıktır.
Mitigation¶
(Residual risk.) PAC, hangi signed pointer'ın kullanıldığını değil, yalnızca
geçerli biçimde sign'lanmış bir tanesinin kullanıldığını authenticate eder — bu da
reuse saldırılarına olanak tanır. Forward edge'in de kapsanması için
BTI ile (pac-ret+bti olarak) eşleşir.
References¶
- Project Zero. Examining Pointer Authentication on the iPhone XS. — https://projectzero.google/2019/02/examining-pointer-authentication-on.html
- Arm Community. Enabling PAC and BTI on AArch64 for Linux. — https://developer.arm.com/community/arm-community-blogs/b/architectures-and-processors-blog/posts/enabling-pac-and-bti-on-aarch64