# PamStealer Skips the Process Chains Defenders Watch. Not All of Them.

A fake macOS prompt asks you to let "Maccy" make changes and enter your password. You type it in. Most infostealers would grab whatever you typed and run. PamStealer checks first: it runs your password against PAM -- the Pluggable Authentication Modules interface macOS uses to actually authenticate you -- confirms it works, and only then decides you're worth robbing. Get it wrong and the prompt just comes back until you get it right.

That one decision -- verify the password before doing anything with it -- is why Jamf Threat Labs named it PamStealer, and it's the tell for where commodity Mac malware is heading. Everything below comes from two vendor teardowns: Jamf, who named it, and ManageEngine, who independently analyzed the same campaign. They agree on the chain and diverge on a couple of specifics I'll flag as they come up.

* * *

Start with the disguise. Maccy is a real, well-liked open-source clipboard manager, the kind of small utility power users install and forget. That makes it a good lure twice over. People seek it out deliberately, so the trust is already there. And a clipboard manager sits on top of exactly what you least want stolen -- pasted passwords, 2FA codes, seed phrases, whatever you copied out of your password manager thirty seconds ago. It arrives the familiar way: a compiled AppleScript, `Maccy.scpt`, on a disk image, served from a lookalike domain, `maccyapp[.]com`, positioned to surface in search alongside the real project. Disk images and AppleScript droppers are old news on the Mac. What happens after the double-click is the story.

* * *

The AppleScript is a thin wrapper around an obfuscated JXA payload -- JavaScript for Automation, the dialect macOS exposes for scripting itself -- and that's the dropper. For the fetch, it doesn't shell out. A normal AppleScript downloader reaches for `curl` or `zsh`, and every one of those is a process spawn behavioral detection watches for. This one downloads through `NSURLSession` and the Objective-C bridge instead: native APIs, no `curl`, no `zsh`.

That doesn't make it processless, and it's worth killing that impression now, because the first wave of coverage read it as artifact-free. Before launching the payload it ad-hoc signs the bundle with `codesign -fs - --deep` -- a spawned process, running from Script Editor against a bundle in Application Support, which both teardowns flag as a detection opportunity. It drops a `.Maccy` marker file and launches hidden, no window, no Dock. So the honest read is narrower than "no artifacts": it skips the download chain classic detection keys on and leaves other artifacts in its place.

It's also picky about who it runs for. The dropper derives a key from a fingerprint of the host -- CPU architecture, locale, keyboard layout, time zone -- and uses it to unlock a config that only decrypts on a matching machine. ManageEngine adds that those checks double as a CIS geo-fence: the malware aborts silently on machines that look CIS-aligned, Russia and its post-Soviet neighbors, the usual move for an operator that doesn't want victims who could complain in its own jurisdiction. Across the samples Jamf pulled, the cosmetic details -- names, config values, obfuscation -- changed build to build while the behaviors held constant, which reads as an automated builder rather than someone hand-editing each one.

* * *

The payload is a stripped, arm64-native Mach-O written in Rust -- uncommon in a stealer ecosystem Jamf notes is dominated by Swift, Go, and Objective-C, and it buys real friction against reverse engineering, since most of its strings decode only at runtime. It plays the native game one level deeper: instead of linking `Security.framework` the normal way, it loads it at runtime, so its keychain-access capability never appears in the binary's static import table. It bundles its own SQLite and reads browser credential, cookie, and wallet-extension databases as files, directly. What it ships back out it encrypts with ChaCha20-Poly1305, so the exfil resists inspection even though the config it leaves on disk does not.

Then the pattern breaks in the honest direction. Running as a fake Finder, Jamf's sample read the clipboard by spawning `pbpaste` -- not once, but on a loop, every ten to thirty seconds for the whole run. A process posing as Finder, running out of Application Support, shelling out to `pbpaste` every few seconds is one of the loudest signatures in the chain, and Jamf flags it for exactly that reason. (ManageEngine's variant did the same theft in-process through `NSPasteboard`, no subprocess, so that noise is Jamf's sample, not a law of the campaign.)

There's a crypto wrinkle worth stating carefully, and it's Jamf's alone. Jamf decrypted the server-side config and found two public Ethereum JSON-RPC endpoints in it, then caught the fake-Finder process actually connecting to one. What it does there, they couldn't say -- they didn't capture the calls, so the purpose stays open: a resilient dead-drop where a value read on-chain points to the next payload, or reconnaissance against the wallets the stealer already hunts. I'm not going to pretend to know which, and neither did they.

* * *

Back to the password. Jamf watched the stealer show an `NSAlert` with a secure text field, styled like a real authorization request -- "Maccy wants to make changes," account name pre-filled -- and validate what you type locally through the PAM API. The contrast with the rest of the field is the point. Other commodity stealers confirm a captured password by shelling out; MacSync checks it with `dscl`, which is a process a defender can catch. A PAM check runs in-process and spawns nothing. Same trade it makes everywhere it counts.

Get it wrong and it re-prompts. Get it right and it shows a decoy: "'Maccy' is damaged and can't be opened. You should move it to the Trash," a near-copy of the real Gatekeeper warning. By then the payload has already run, kept your password, and set up persistence, so the message does one job -- get you to trash the lure and conclude the download was broken. Persistence uses login items, modern and legacy (`SMAppService` and `LSSharedFileList`), and deliberately avoids the LaunchAgents and LaunchDaemons where detection is densest. It masquerades as Finder, wearing the genuine icon, and holds back the Full Disk Access request it makes under that disguise for as long as forty minutes after launch, so the prompt doesn't line up with anything you just did. A permission request that arrives the instant you open an installer is suspicious. The same one forty minutes later is just macOS being macOS.

* * *

Put the chain side by side and the philosophy is consistent, if not absolute. JXA over the Objective-C bridge instead of `curl`. A runtime-loaded `Security.framework` instead of a linked one. A PAM check instead of `dscl`. Login items instead of the LaunchAgents everyone watches. A permission prompt decorrelated from launch. At every stage defenders have historically keyed on, it picks the native path and skips the tell.

> It doesn't erase its footprint. It moves the footprint to where the old detections weren't looking.

That's bigger than one clipboard clone. macOS detection leaned for years on the tells of noisy malware -- shell utilities, process trees, the `dscl` and `osascript` calls that announce themselves -- and that worked because commodity stealers were noisy. This one is commodity, not a nation-state showpiece: ManageEngine ties it to a malware-as-a-service pattern, per-host binary rotation and all, and it's quiet in exactly the places those detections watch. Here's my read, marked as a read and not a vendor finding: this is where the low end is going, because the quiet-native approach is turning up in automated-builder, spray-to-a-lookalike-domain malware, and the low end is where a technique becomes the baseline instead of the exception.

Two cautions before you repeat any of this. First, the deepest internals -- the validate-your-password-through-PAM workflow the name is built on, the server config, the forty-minute Full Disk Access delay -- are Jamf's. ManageEngine independently corroborates the chain but frames PAM differently: as `libpam` linkage the binary could use to intercept `sudo` and system-auth responses, not the validate-before-steal workflow. Both saw PAM; they emphasize different uses, and the behavior this piece opened on is Jamf's account specifically. Second, quieter is not invisible. The technique dodges shell-watching detection; it does not beat defense in depth. Endpoint Security tooling still sees the framework loads, the `codesign` spawn, the login-item registration; the `.config` sits on disk with the C2 URL in plaintext; ManageEngine's sample still tripped a VirusTotal engine at first submission; the C2 still has to talk.

* * *

Here's what none of the native cleverness gets around: PamStealer still needs you to download it from the wrong domain, run a script you shouldn't, and type your password into a prompt you should have questioned. The chain depends on the human failing first, and "just tell users not to click" has never worked and never will. Installing a clipboard manager off a lookalike domain isn't carelessness -- it's a normal act in an environment that makes the fake and the real indistinguishable until it's too late.

So the takeaways are unglamorous. Get software from sources that can't be trivially cloned -- the Mac App Store, or the developer's verified GitHub, not whatever the search result surfaced. Treat an admin password prompt during an install as the highest-friction moment of your day, not the lowest. Be stingy with Full Disk Access, and suspicious of a request for it that arrives well after launch. And if you run detection for a fleet, watch framework loads, `codesign` fired against a bundle in Application Support, login items that resolve to a Finder outside `/System/Library/CoreServices/`, and a "Finder" reading a Chrome database -- rather than waiting for the malware to announce itself by spawning `curl`. The stealers stopped announcing themselves at the front door. They're still noisy inside the house, if your sensors are pointed at the right rooms.
