PACMAN: Attacking ARM Pointer Authentication with Speculative Execution

paper: https://pacmanattack.com/paper.pdf

Abstract

這篇 paper 提出新的攻擊方式叫做 PACMAN,結合 memory corruption 漏洞和 speculative excution 漏洞的攻擊,利用 side channels 在不造成 crash 的情況下 speculatively leaks PAC verification,並在 Apple M1 SoC (第一個支援 ARM Pointer Authentication 的個人電腦處理器) 上成功利用。

Introduction

PACMAN 主要是個跟硬體有關的攻擊,先前曾經有像是 Meltdown 和 Spectre 也都是和硬體有關的漏洞,這個 PACMAN 主要就是攻擊 speculative exection (推測執行)這個特性來 bypass ARM 的 pointer 驗證

ARM Pointer Authentication

一般在做 memory 相關的攻擊時,常常會要控制 data pointer 然後控執行流程,所以 ARM 就有提供 Pointer Authentication (PA) 來保護 pointer 不被亂動,有了這個保護機制讓竄改 pointer 不被發現變很難。

通常實際的 address space 在 64-bit 架構下是小於 64 bits 的,所以沒用到的 bits 就能拿來存一個 hash 值 (Pointer Authentication Code PAC) 來保護 pointer,若用 pointer 的時候 PAC 不對程式就會 crash 掉。

PAC 可以小到 11 bits 大到 31 bits (看系統設定),不過即使很小也不太可能被暴力破解,因為 crash 掉之後重啟就會用新的 secret key,而且太頻繁 crash 掉也容易被發現。

Key Challenge

在 Apple 上研究的難點

  • 很少關於他們 micro-architectural 的公開文件
  • by default, to not expose a high-resolution timer to userspace
    • (說是對微架構逆向很重要)

Contributions

  • propose the PACMAN attack
  • develop micro-architectural timing side-channel attack primitives and a reverse engineering tool for Apple M1 processors
  • reverse engineer the TLB organizations and perform the first TLB-based side-channel attack with speculative execution on Apple M1 processors
  • show that the PACMAN attack works across privilege levels (We demonstrate several proof-of-concept of PACMAN attacks, including constructing PAC oracles, brute-forcing PACs, and a control-flow hijacking attack targeting a PAenabled kernel module)

Impact

  • 業界,可以影響所有有用上現有 PAC 設計的 ARM 處理器
  • 學術,很多 based on memory 安全前提的論文和假設都可以被推翻,有必要變動 threat mode 重新評估
  • highlights that security mechanisms that employ a securityby-crash design principle and rely on low collision probability are potentially vulnerable to speculative execution attacks
  • speculation can serve as a primitive to suppress crashes

Background

Memory Corruption Vulnerabilities

在低階語言寫的軟體中出現,包括

  • out-of-bound writes
  • out-of-bound read
  • use-after-free
  • doublefree bugs
  • integer overflows and underflows
  • size confusion attacks
  • type confusion attacks

可以竄改或破壞掉原本 memory 中的東西,像是 data and code pointers,去修改 code pointer 可以改變程式的執行流程,造成嚴重的影響,這又叫做 control-flow hijacking attacks,像是 ROP 和 JOP 都算是。

常見的保護包括

  • stack canaries
  • data execution prevention
  • ASLR
  • kASLR (kernel ASLR)

雖然提供了保護提升攻擊難度但還是都有被繞過的可能。

ARM Pointer Authentication

ARMv8.3 開始提供的 overhead 很小的保護機制,保護 pointer 不會被亂改,主要就是去幫 pointer 簽一個 hash,這裡稱為 Pointer Authentication Code (PAC),在用這個 pointer 之前會先檢查 PAC。

Signing and Verifying Pointers

實際上 64 bits 架構的 address space 不會真的用到 64 bits,PA 就會把 PAC 吋在多的沒用到的 bits。

圖中拿來簽hash 的這個 context value 裡面有存在高權限才碰得到的 register 中的 key 和 program-specified salt。

ISA Extension


為了提供 PA 功能,PA 提供了兩組 instructions sets 來做這些事

  • pac 開頭的是做 signing 的工作
    • 最多有五組的 key 可以同時硬體記錄著,當次 signing 要用的 key 跟 opcode 有關
    • ex. pacia ptr, salt
  • aut 則是做驗證

在 Apple 晶片中除了保護 return address,PAC 也被用來保護很多其他的 data structures,包括 C++ vtable pointers、vtable entries、Objective-C method caches。

Micro-architectural Side Channels

有 shared micro-architectural structures (including caches , TLBs, functional units, and network-on-chips) 都可以被用來 leak 重要資訊

Prime+Probe

  • 限制最少的
  • attacker 先把 cache 塞滿,victim 存取過後,attacker 可以再去 access 整個 cache 利用時間差來知道 victim access 了哪些東西

Flush+Reload

  • 需要 sharing memory
  • 先 flush 掉要觀察的 memory 位置,然後等受害者執行完以後再去掃記憶體,就可以由時間差知道那些有被存取過
  • Meltdown 與 Spectre 都有用到的

Evict+Reload

  • 需要 sharing memory
  • 類似 Prime+Probe,不過對象是 shared memory

Speculative Execution Attacks

Speculative execution 又翻推測執行,是處理器的一個優化,因為處理器速度很快,所以為了更好的利用資源處理器會偷跑,像是分支預測,他會在分支判斷之前就先猜這次的分支會走哪一條,然後先偷跑,猜對了就可以直接繼續,猜錯了就會到分岔點走正確的路,但這樣的優化反而衍生出一些漏洞。

Meltdown

利用 exception 和預先執行,在發生 exception 停下來之前偷跑到後面,load 重要資訊的指令,然後停下。

Spectre

conditional branches 或是 indirect branch。

Threat Model

  • Target
    • 在一個有開 PA 的 victim 上用 PAC oracle 爆出 PAC 成功達成 control flow hijacking attack
  • Attacker
    • unprivileged userspace application
  • Victim
    • the operating system kernel
  • Assumption
    1. 在 victim program 中有個可以 exploit 的 memory corruption 漏洞,可以讓攻擊者任意寫入 memory
    2. 有開 PA 而且存在 PACMAN gadgets
    3. 可以做 side channel attack (要有high-resolution timer)

The PACMAN Attack

主要就是在不造成 crash 的情況下利用 speculative execution attacks 和 micro-architectural side channels 來 leak PAC verification 的結果。

PACMAN gadgets

有些可以利用的 code patterns 這邊會稱作 PACMAN gadgets,由兩種 operation 組成

  • verification
    verifies the PAC,給一個 pointer ,然後他會驗證 PAC 是否正確,正確就 output 一個有效的 pointer,錯誤就 output 一個壞的 pointer,執行的時候就會出錯 (前面 Background 有講過)
  • transmission
    他會預先去執行 verificated 的輸出 (pointer) (可能是個 load/store,反正就是預先轉成 physical address 去使用他),如果過了就會產生可以被觀察到的 side effect (就能經由 side channel 偷相關的資訊),若預先執行失敗就會有個 speculative exception,而這些因為都是在 mis-speculated branch 之下發生的所有不會造成 crash

其中 PACMAN gadgets 又分成以下兩種

  • Data PACMAN Gadget (圖3c)
    • 預先去猜分支做 data accesses 造成的
    • AUT: pointer authentication instruction
    • guessed_ptr: 攻擊者提供的 pointer
    • verified_ptr: AUT 的 output pointer
    • 觀察有沒有執行 load 還是 exception 的差異來知道 pointer 對不對
  • Instruction PACMAN Gadget (圖3d)
    • 預先去猜分支做 instruction fetches 造成的
    • 在完成 AUT 之前會使用 branch target buffer (BTB) 預測 BR2 (這在 nested branch 的分支預測中很常見)
    • AUT 完成後發現 BR2 預測錯誤所以 eagerly squashes BR2
    • fetch 正確的 BR2 (也就是剛剛 AUT 的結果)
    • 限制:
      • 必須支援 eager squashing of nested branches,一定要有第三步的 squash AUT 後的 pointer 才會被用,才能 leak AUT 結果
      • 需要區分 fetching the verified pointer 和 BTB prediction 的 side effect,以這裡用的 PoC 用的 TLB-based side channel, BTB prediction 和 verified pointer 要在不同 page 才行

Gadget Detection

用 Ghidra’s scripting API 找 kernel 中 conditional branches,檢查它之後的 32 個 instructions,若驗證後的 pointer 會被用在 transmit instruction (a memory access or a branch instruction) 就視為 PACMAN Gadget。

在 xnu-8019.80.24 中找到13867 個 data PACMAN gadgets 和 41292 個 instruction PACMAN gadgets,因為這裡只往後找 32 個指令,所以若再找多一點還說不定更多。

An End-to-End Illustrative Example


有個 BoF 可以蓋到 function pointer。

  1. 先正常執行程式,train BTB 和 branch predictor (cond = true)
  2. BoF 竄改 fp,猜 PAC 直到猜中 (cond = false,不真的進 branch,而是讓它 speculative 跑進 branch,不會造成 crach)
  3. 最後用猜到的 PAC 成功利用 (con = true)

Attack Platform

  • Apple M1 SoC
    • 第一個支援 ARMv8.3 extensions (包括 PA) 的 aarch64 桌面版本
    • EL0 : usermode programs 執行的地方
    • EL1 : kernel 執行的地方
  • macOS and XNU Kernel
    • OS : macOS
    • XNU Kernel
      • 有 loadable kernel extensions -> kexts
      • 在後面他們做 reverse 的時候會用到

上述的東西都是沒有 opensource 的而且也沒什麼文件,所以需要做 Reverse 來得到一些需要的資訊,這也是這篇論文其中一大 contribution。

Reverse Engineering Tools

High-Resolution Timers

前面有提到 side channel attack (Prime+Probe),這種攻擊需要比較精準的時間,所以他們找出 M1 中有那些 timer 可以使用。

  • CNTPCT_EL0
    • System Counter,所有的 core 都用共享這個 counter 但是 24 MHz 太低了,沒辦法很精準地觀察到有沒有 cache hit/miss 這種差異
  • PMCCNTR_EL0
    • ARM Cycle Count Register,大多 ARM Cortex 處理器都有 performance monitoring units (PMU),就會有這個 counter,但 M1 的 PMU 不是 standard PMU 所以沒有
  • PMC0
    • 沒有文件提到的,count cycle,另外還有一個 PMC1 count instruction,但都不是 userspace 可以碰到的
    • 為了可以使用到,他們用了剛剛提到的 kext 修改一個控制的 register PMCR0 來讓 PMC0 可以被 userspace 碰到 (但這是為了 reverse engineering 用的兒部會在攻擊中用到)
  • Custom Multi-thread Timer
    • 自己做了一個不需要 kext 的 userspace accesible timer,做法像這樣
    • isb 是控制流程用的,但是後來發現不加變動會稍微變大,但時間的精細度會高一點所以最後還是拿掉了

PacmanOS

用 Rust 寫的,可以在 M1 上跑的 bare mental 環境,可以讓他們完全控制整個環境,包括硬體、其他干擾等等方便跑實驗。

Reverse Engineering

Basic Memory Hierarchy Information

M1 用 big.LITTLE design

big.LITTLE: ARM 提出的把運算能力比較強的 big core 和比較弱但較不耗電的 little core 結合在一起的組合,這裡用 p-core (Performance) 和 e-core (Efficient) 分別稱呼 big 和 little,但這是 intel 的命名

M1 memory 有兩層的 cache,不同 core 的 size 不一樣,然後 kernel 用 48 bits 的 virtual address 和 16 bit 的 PAC。

L1 Data TLB and L2 TLB

主要研究 p-core 的行為,因為在 p-core 上的 speculative 行為會比較可靠,但不像 Linux 有可以直接指定要用哪個 core 執行的 API (taskset),他們用 pthread_set_qos_class_self_np 來 “建議” kernel 用哪個 core,然後用到的 counter 是會需要 kext 的那個 PCM0。

做的實驗就是用 Prime+Probe 來測量 TLB 的 size,主要步驟就是

  1. load 某個 address
  2. load N 個 address 來把 TLB 塞滿
  3. 再 load 一次最一開始 load 的那個 address,測量 latency

最後得到 (a) 圖,可以看出每一次的差距都是 TLB size 的臨界 (L1 dTLB/L2 TLB),雖然無法斷言 cache 設計是不是真的就跟推測的一樣但至少可以知道怎麼樣才能填滿 TLB

1
2
1) To evict a page table entry from the L1 dTLB, we can create an eviction set with 12 or more addresses with a stride of 256×16KB.
2) To evict a page table entry from the L2 TLB, we can create an eviction set with 23 or more addresses with a stride of 2048×16KB.

至於 TLB conflicts and cache conflicts 則用不同 load N 個 address 的 address 算法測量,得到 (b) 圖 (L1 cache/L2 cache/L1 dTLB/L2 TLB)

L1 Instruction TLB

  1. 先把 L1 dTLB 和 L2 TLB 都填滿
  2. branch 到某個 address
  3. 執行 N 個 branch 把 iTLB 填滿 (稱為 instruction eviction set)
  4. Load 第 2 步的那個 address,測量 latency (為什麼是 “load” 得到 data access 的 latency 下面會解釋)

得到的結果是圖 c,解釋結果之前先講一下他們推論的整個 TLB 架構

  • user 和 kernel 的 L1 iTLB 並不共享,userspace 無法觀察到 kernel space 的差異
  • L1 dTLB 是 L1 iTLB 的 non-inclusive backing-store,iTLB 塞滿了就會被塞到 dTLB 去,這就是為什麼剛剛測量的步驟 4 是用 “load” 測量 data access 的 latency
1
3) To evict a page table entry from the L1 iTLB, we can create an eviction set with 4 or more branch instructions with a stride of 32×16KB.

Summary of Reverse Engineering Results

  • 先把 iTLB 填滿之後就能填到 dTLB

  • 不管是用 PMC0 或 Multi-thread Counter 都可以清楚分出有沒有 TLB hit、cache hit 的差別

Proof-of-Concept Attacks

PAC Oracles

實驗中他們裝個有可以用 syscall trigger PACMAN gadget 的 kext,然後傳一堆有正確 PAC 的 pointer 和沒有 PAC 的 pointer 測試然後用他們定義的那個 counter 測量時間,然後分別用 data access 和 instruction fetch 兩種方式來 leak PAC 是否正確

  • Leaking via Data Accesses
    • 因為 dTLB user 和 kernel 共享,所以可以直接測
    1. 先 train PACMAN gadget 的 branch predictor (64 次)
    2. Reset the TLB
    3. Prime the L1 dTLB
    4. Trigger the PACMAN gadget by passing in the pointer with the PAC to test
    5. Probe the L1 dTLB set by re-accessing the eviction set and report the number of L1 dTLB misses
  • Leaking via Instruction Fetches
    • kernelspace 和 userspace 的 L1 iTLBs 不共享,所以從 userspace 觸發 kernelspace 的 self-conflicts,然後塞滿它的 iTLB 進而塞到 dTLB (共享的)
    • 前面步驟都跟 data access 一樣
    1. 用塞滿 iTLB 的 set 去塞塞看
    2. 最後檢查有沒有塞到 dTLB 去就知道有沒有 miss 了

Brute-Force Attack

  • Attack Speed
    • M1 uses a 16-bit PAC
    • 2.94 minutes on average 試玩所有可能的 PAC values
    • 主要花時間的是 train predicter 時的 syscall,可以縮短但非必要
  • Attack Accuracy
    • noisy environment
    • 90% correct PAC (45)
    • 10% no PAC value was found (5)
    • 沒有 false Positive (找錯 PAC)

Jump2Win Attack

有 PAC 就能單純的去利用漏洞了

Contermeasures

  • explore PAC-agnostic execution
    • 修改架構或軟體來確保 PAC 不會在 speculation 的時候被驗證並使用
    • speculative execution 在 memory access 和 branch 要用驗證後的 pointer 時暫停下來
      • fence/isb
      • 效能變差
    • 預先執行的時候總是假設 PAC 會過
      • 預先執行 invalid pointers 可能造成 Meltdown-style vulnerability
      • 需要和 Meltdown 的 mitigation 配合
  • 用防範 Spectre 的方法改良
    • 讓 speculative load 沒辦法被觀測到
      • Invisible speculation mechanisms
      • such as InvisiSpec, SafeSpec, and Delay-on-Miss
    • 利用 information flow tracking 來防止 leaking speculatively accessed secret
      • STT, NDA, and Dolma
      • taint pointer authentication instruction
  • 別寫出有 memory corruption 洞的程式

PACMAN: Attacking ARM Pointer Authentication with Speculative Execution
https://wiiwu959.github.io/2022/06/28/2022-06-28-PACMAN/
Author
Wii Wu
Posted on
June 28, 2022
Licensed under