Skip to content

How a single PostScript file leaks your Mac's memory

Csaba Fitzl Csaba Fitzl
How a single PostScript file leaks your Mac's memory

When I started my InfoSec journey, most of the offensive classes I took focused on memory corruption exploits. I learned a lot about buffer overflows, DEP and ASLR bypasses, and even got into kernel exploitation. However, since my job at that time was mostly hunting for bad guys in an insane amount of logs and telemetry data, I never really had the time to apply this knowledge.

About 8 years ago, I started learning about macOS, and from the beginning I approached it via logic bugs, starting with simple things such as dylib hijacking. Years went by, I started to really enjoy looking for these kinds of vulnerabilities, and slowly I drifted further and further away from memory corruption work.

Then I read the following in Jonathan Levin's OS Internals books, about Spotlight and QuickLook plugins: "Plugins, however, involve a potential security risk: File formats can be malformed in a nigh infinite number of ways, and targeted malformations could lead to code execution.

It sounded interesting, so I saved it to the very bottom of my to-do list, thinking that maybe, someday, I should try fuzzing some file types. But I hate fuzzing, that is not the way I like to work, and I never got to this. Time passed, and the idea stayed in the back of my mind, but I knew I’d probably never get to this.

Then 2026 came, and by now AI has advanced so much in vulnerability research that it's impossible to ignore. It's everywhere. You can read about people using it for their own research, how Claude finds tons of bugs, and how it can find vulnerabilities just by asking it, without any hints. I also use AI as part of my research work, and it definitely speeds things up.

One day I decided to run a short side quest. I dusted off this old idea about file parser vulnerabilities, uploaded some decompiled functions from the PostScript Spotlight plugin, and gave Claude the following complex prompt:

Can you find any potential memory corruption issues in this ps file parser / spotlight plugin?

And it did.

The vulnerability

The vulnerability is in the Spotlight PostScript plugin. The PostScript file contains Document Structuring Conventions (DSC) headers, and the parser extracts metadata information from these such as version, creator, title, page count, and bounding box dimensions. These fields are stored in the Spotlight index and become queryable through mdls, mdfind, and the MDItem API later. A header like this looks like.

%!PS-Adobe-2.0

%%Creator: Iru Inc

%%Title: iru.ps

%%Pages: 1

%%BoundingBox: 0 0 612 792

The issue is within the function _GetMetadataForFile, which is the standard function name in older Spotlight importers. It reads the first 4000 bytes (0xFA0) of the target file into a heap-allocated buffer:

__nitems = min(file_size, 0xFA0);  // cap at 4000

x0_5 = malloc(__nitems);

fread(x0_5, 1, __nitems, stream);

x28_1 = &x0_5[__nitems]; // buffer end pointer

Next, the function uses strnstr(buffer, keyword, __nitems) (wrapped in _OUTLINED_FUNCTION_4) to locate each DSC comment keyword within the buffer.

After locating a DSC keyword, the code scans forward byte-by-byte from the end of the keyword to find the end of the value (the first byte < 0x20, i.e., a control character such as newline, null, or tab). There are three different scanning loop variants used across the four DSC keywords each behaving slightly differently, though all of them are vulnerable. We'll focus here on just one, since it allows most bytes to be leaked.

The %%Creator: scanning loop (binary offsets 0xB44–0xB6C) and %%Title: scanning loop (binary offsets 0xBB8–0xBD0) both use an incrementing counter; below is the loop for %%Title:.

x10_2 = &x0_5[__nitems] - x0_10 - 0xa; // starts POSITIVE

do {

byte = *(uint8_t*)scan_ptr;

scan_ptr++;



if (byte >= 0x20) {

z_3 = !x10_2; // true only when x10_2 == 0

} else {

z_3 = true; // control char → stop

}



x10_2 += 1; // INCREMENTS each iteration

end_ptr++;

} while (!z_3);

The counter x10_2 starts at a positive value and increments by 1 each iteration. It holds the offset for the data referenced by the %%Title: metadata in this case. The counter-based exit condition !x10_2 is true only when x10_2 == 0, which never happens, since the counter moves away from zero on every iteration. This means the loop can only terminate by encountering a byte less than 0x20.

There is no buffer bounds check in this loop variant. If the value portion of the DSC comment contains only printable characters (bytes ≥ 0x20) all the way to the buffer boundary, the loop crosses that boundary and continues scanning through adjacent heap memory indefinitely, until it finally hits a byte < 0x20.

This loop calculates the start and end of a string for the metadata we want to collect.

The pointer range accumulated by the scanning loop — potentially extending far past the buffer boundary — is passed to CFStringCreateWithBytes (via _OUTLINED_FUNCTION_0), creating a CFString that contains the out-of-bounds heap content. For the three string-valued metadata fields, this CFString is then written directly into the Spotlight metadata dictionary via CFDictionaryAddValue (wrapped in _OUTLINED_FUNCTION_3):

cf = CFStringCreateWithBytes(alloc, value_start, end_ptr - value_start, ...);

CFDictionaryAddValue(theDict, *kMDItemCreator, cf); // heap data persisted

The metadata is returned to the Spotlight indexing infrastructure and persisted to the on-disk index.

The exploit

An attacker needs a specially crafted .ps file to trigger the vulnerability. It must satisfy the following conditions.

1. The file is at least 4000 bytes (so __nitems = 0xFA0 and the large-file code path activates).

2. A DSC comment keyword (e.g., %%Creator:) appears somewhere in the first 4000 bytes.

3. The bytes following the keyword, up to byte 4000, contain no control characters (no newline, null, tab, or any byte < 0x20).

The keyword does not need to be near the end of the buffer. For example, %%Creator: at byte 50 followed by 3939 bytes of printable ASCII (no newline) before the 4000-byte boundary is sufficient. The scanning loop will consume all in-buffer bytes, cross the boundary, and continue through heap memory.

Spotlight importer plugins execute automatically via mdworker whenever a new file appears on the filesystem. No user interaction is required. Delivery methods that trigger indexing include: AirDrop file transfer, email attachment download, mounting a DMG or disk image, file sync via iCloud Drive or other cloud services, and files appearing in any Spotlight-indexed directory.

After indexing completes, the leaked heap data is persisted as file metadata and can be read via:

mdls -name kMDItemCreator /path/to/malicious.ps

mdls -name kMDItemTitle /path/to/malicious.ps

Or programmatically:

MDItemCopyAttribute(item, kMDItemCreator);

The mdworker process that runs the importer also processes other files concurrently. Its heap contains data from parsing other documents — file paths, text content, metadata from PDFs, emails, and other file types. The leaked bytes can therefore contain fragments of other files' content that were adjacent on the heap.

But there is more. Unless the unbound memory read hits a non allocated memory region, it will never crash the process, there won’t be any way to notice that this leak happened other then looking into the Spotlight database for weird stuff. We didn’t experiment a whole lot with this, but we could always leak some memory, most of the time a portion of a sandbox profile, this is shown below.

user@max ps % mdls output.ps

_kMDItemDisplayNameWithExtensions = "output.ps"

kMDItemCreator = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA



(...)

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (allow file-read* file-write* (subpath (param "DARWIN_TEMP_DIR")))))"

kMDItemDateAdded = 2026-03-31 06:45:14 +0000

Disclosure and fix

Apple fixed this vulnerability quickly. We reported it in early April, and the fix was released by the end of July as part of macOS 26.6, assigned CVE-2026-43774. This was much faster than the usual turnaround time, though patching this kind of issue is also significantly easier than fixing complex logic bugs with corner-case race conditions. Here, a proper boundary check solves the problem.

I'll continue exploring logic bugs, but this was a nice detour. There are a few more Spotlight and QuickLook file parsers on the system, and it's likely worth focusing on these, since I'm sure there's more to be discovered.

You might ask why I'm not the one doing it? Simply because I'm putting my effort into other areas, and you can't have it all. That said, I think I can finally mark this long-outstanding task of finding file parser issues in Spotlight as COMPLETE.

Recent Articles

Featured image: Rustbot, the macOS malware used in the latest Rust Supply Chain Attack
Cristian Molina 10 min read

Rustbot, the macOS malware used in the latest Rust Supply Chain Attack

On August 20, 2026, attackers published malicious versions of three widely used Rust packages to crates.io, the official Rust package registry. On macOS, the payload is a remote access trojan (RAT) that collects cloud credentials, SSH keys, cryptocurrency wallet data, and browser profile information, then sends it to attacker-controlled infrastructure. It installs a launch agent to survive reboots and accepts follow-on commands from its operators. Once running, it decrypts its configuration, profiles the host, Reads the local browser stores, installs persistence via LaunchAgent, and beacons out. The packages were available for approximately two hours before removal. The attackers also withdrew the previous stable versions, which pushed automated dependency resolution toward the compromised releases. Any environment that compiled an affected project during that window should be treated as compromised. This blog documents Iru's analysis of the native Apple Silicon ARM64 implant.

Threat Intelligence
Featured image: Inside the screensharingd Bugs: How macOS Screen Sharing Went from Root File Access to Pre-Auth Compromise
Csaba Fitzl 6 min read

Inside the screensharingd Bugs: How macOS Screen Sharing Went from Root File Access to Pre-Auth Compromise

In late July 2026, a cluster of vulnerabilities in macOS Screen Sharing turned what initially looked like a fairly constrained privilege problem into one of the more interesting macOS remote-attack stories in years.

Threat Intelligence
Featured image: Apple is deprecating hdiutil in macOS 27 Golden Gate. Are your scripts ready?
Arek Dreyer 3 min read

Apple is deprecating hdiutil in macOS 27 Golden Gate. Are your scripts ready?

If you spent part of last weekend fielding Slack messages about hdiutil, you're not alone. Jeff Johnson's lapcatsoftware.com blog flagged that the man page for hdiutil in the macOS 27 Golden Gate beta now carries a deprecation notice:

Educational

See Iru in action

Discover why thousands of teams choose Iru

By submitting this form I agree to Iru’s Privacy Policy and consent to be contacted by Iru about its products and services.

Stay up to date

Iru's bi-weekly collection of articles, videos, and research to keep IT & Security teams ahead of the curve.