I hacked macOS!!!

CVE-2022-32947

With Lina✨ & Cyan💎

GitHub · Video

On today's menu...

But first a demo!!!

Live demo · Source code

GPU intro

CPU vs. GPU

How Apple GPUs work

That day in 2022...

Virtual Memory

What's a page table anyway?

Pointers

#include <iostream>

	using namespace std;

	int main() {
		int a = 0;
		void *p = &a;

		cout << "&a is " << p << " nya!" << endl;
	}
&a is 0x7fffc6ba5ddc nya!

This is a virtual address!

0x7fffc6ba5ddc


0x814745ddc

This is the physical address!

How does this work?

GPU/CPU Memory

Page Tables

40 bits for virtual address (1 TiB)

16 KiB pages → 67,108,864 pages total

8 bytes per page address
=
512 MiB page table!!!!

Too big!!!

Permissions

TTBAT

Translation Table Base Address Table

That day in 2022...

Hacking command buffers

Weird machines?

Command buffers

Micro Commands

  • 00: -
  • 01: Wait For Idle
  • 02: Wait 2
  • 03: Doorbell
  • 04: Write 8-bit reg
  • 05: Write 32-bit reg
  • 06: Write 64-bit reg
  • 07: -
  • 08: Read 8-bit reg
  • 09: Read 32-bit reg
  • 0a: Read 64-bit reg
  • 0b: Wait 32-bit reg
  • 0c: Wait 64-bit reg
  • 0d: -
  • 0e: Store 32-bit
  • 0f: Store 64-bit
  • 10: Load 32-bit
  • 11: Load 64-bit
  • 12: ?
  • 13: ?
  • 14: ?
  • 15: Test (If)
  • 16: Logic op
  • 17: Add 16-bit
  • 18: Retire
  • 19: Timestamp
  • 1a: KTrace
  • 1b: ?
  • 1c: ?
  • 1d: ?
  • 1e: Jump (Cond)
  • 1f: ?
  • 20: ?
  • 21: ?
  • 22: Start Vertex
  • 23: Finish Vertex
  • 24: Start Fragment
  • 25: Finish Fragment
  • 26: Start Blit
  • 27: Finish Blit
  • 28: ?
  • 29: Start Compute
  • 2a: Finish Compute
  • 2b: ?
  • 2c: ?

It's Turing Complete!

  • 2 64-bit scratch registers
  • Loads and stores (direct only)
  • AND, OR, XOR, shifts
  • Test for zero ("if")
  • Jump / Conditional jump

Tricks

Indirect read/writes?
R0 = *R1;

Tricks

Multiply?
R1 = 10 × R0;

We can:

  • Read/write firmware data memory
  • Do math

We can't:

  • Modify the firmware code
  • Modify the page tables

But...

uPPL

More secrets...

uPPL can map memory!

  • If page is uPPL: DENY
  • If page is page table: DENY
  • If page is secure uPPL/kernel data: DENY
  • If page is TTBAT.... ??? ALLOWED!!!
  • They forgot to blocklist that one!!!!

Hack Idea

  • Make fake PT that gives access to the real PTs
  • Ask uPPL to give us access to the TTBAT
  • Write fake PT to TTBAT (context 63 TTB0)
  • Ask uPPL to activate context 63
  • Write fake PT address to TTB1 page table

GPU can now read/write page tables directly!!

Problem

We can read/write stuff from the microsequence, but we can't call the uPPL...

We can't modify the firmware code directly...

Solution

Return-oriented Programming (ROP)!

Return-oriented programming

More weird machines????

Call and return

Multiple calls?

Challenge!

Data registers

Gadgets can be weird...

g_calltwo: Call two functions...

But the first argument is a pointer to a pointer to a pointer to the function

And the second one is a pointer to 0x70 before a pointer to the function

Gadgets can be weird...

g_store: Store a value to memory...

But it actually stores to the address you pass plus 0xa78

Exploit time!

This is going to be so yabai...

Putting it together

  • Shader: Set up microseq and ROP chain data in unused memory
  • Shader: Overwrite microseq pointers
  • Microsequence: Calculate and fill in a lot of data using firmware info, then overwrite stack to start ROP
  • ROP: Use uPPL bug and make a page table accessible to shaders
  • Shader: Map more page tables and get access to ALL RAM
  • Profit?????

It's hard to demo this all working in practice...

Challenge Accepted!

Microsequence registers

R0 0x0000000000000000 R1 0x0000000000000000
X0
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
X11
X12
X13
X14
X15
X16
X17
X18
X19
X20
X21
X22
X23
X24
X25
X26
X27
X28
X29
LR
SP
PC