In July 2026, I asked Claude why AltTab on my Mac had become slow. The debugging
session turned into an incident response when a wider process inspection found
an AppleScript interpreter consuming unusual CPU, a hidden root-owned watchdog,
and a binary called AccountsHelper that the watchdog relaunched every second.
A persistence sweep then found mdworker_shared, a cryptocurrency stealer
hidden inside .com.apple.metadata.mds, a directory named to resemble Apple’s
Spotlight indexing service.
Claude surfaced the anomaly. I turned it into a structured investigation. I designed the plan, safety boundaries, decision gates, and evidence standards; AI agents performed most of the concrete process inspection, reverse engineering, analysis scripting, and report drafting. That division of labor is central to this case study.
The technical result matters: we recovered the behavior of a remote-access backdoor and a cryptocurrency stealer, including their command-and-control (C2) logic. C2 is the communication channel through which malware contacts an operator-controlled server to receive instructions or return results and stolen data. We did this without executing either sample during the deep-analysis phase. But the more transferable result is a method for supervising agents when they can produce technical work faster than a human can independently reconstruct every step.
From a slowdown to an incident
The result, in thirty seconds:
AccountsHelperimplemented remote task polling, one-shot shell execution, and an interactive terminal.mdworker_sharedwatched the clipboard, replaced cryptocurrency addresses, recognized wallet-secret-like content, and prepared it for submission to a command server.- The agents recovered the backdoor’s hidden command infrastructure through static analysis of both its Intel and Apple Silicon code. The conclusion was accepted because saved machine-code evidence, a second architecture, and offline checks agreed—not because an agent produced a convincing report.
- Several plausible early claims were wrong. The investigation improved when those claims were narrowed or withdrawn, including claims about a supposed Apple-related domain, system-wide network activity, a configuration “key,” and a packet capture.
The first useful signal was not a malware signature. It was a mismatch between the symptom and the process tree.
The high-CPU AppleScript process belonged to a hidden shell loop running as
root. That loop repeatedly launched AccountsHelper as my user from a hidden
directory named .com.apple.accountsd, imitating an Apple accounts service.
The binary’s ad-hoc signature carried no verified developer identity or Apple
Team ID, and Gatekeeper rejected it. The second persistence chain kept
mdworker_shared running as root from .com.apple.metadata.mds. Apple ships a
legitimate Spotlight worker with the same filename, but this copy lived in a
hidden, user-writable directory and had no Apple Team ID.
Neither XProtect nor a later ClamAV scan flagged the samples. I treated those negative results as evidence about detector coverage, not evidence that the binaries were safe.
Browser history and filesystem timestamps strongly associate the first installation window with a malicious Google advertisement that impersonated Claude and led to a GitLab Pages site. A configuration file appeared about thirty seconds after that visit, followed minutes later by the backdoor and its persistence mechanism. The exact terminal command or intermediate loader was not recovered, so I do not claim a precise execution mechanism or malware operator.
My first consequential decision was to preserve hashes, process state, persistence files, browser evidence, and timestamps before changing the system. I then prioritized three questions: was there another implant, could the malware’s C2 communication protocol be recovered safely, and which credentials had to be treated as exposed? The inspected startup and persistence locations showed no third confirmed implant. I rotated high-value credentials from a clean device and made the command-server question the focus of the reverse engineering.
Designing an auditable investigation
Before the deep-analysis phase, I wrote an investigation contract for the agents:
- Do not contact, scan, or probe the real command servers.
- Do not execute a complete sample entry point; prefer static analysis or interpretation of isolated functions with system effects disabled.
- Keep victim-specific configuration, sessions, browser history, packet captures, and credentials off the remote analysis node.
- Mount samples read-only and non-executable in a container with no network and reduced operating-system privileges.
- Grade important claims as confirmed, high confidence, inferred, or unknown; require machine-code or data-flow evidence for high-impact conclusions.
- Use another architecture or analysis tool where feasible, and record sample hashes, tool versions, commands, and code addresses.
There was one important exception in the chronology. Before this stricter
contract existed, a diagnostic script on the affected Mac started tcpdump, a
packet recorder, to save DNS queries and TCP connection attempts. It tried to
configure pf, macOS’s built-in packet filter, to allow DNS but drop other
outbound TCP traffic. It then ran the complete AccountsHelper program for
twelve seconds, killed it, and saved the packet capture.
A later audit found that the script loaded its rule into a named pf ruleset
but never verified that the live firewall configuration actually used that
ruleset. The capture contained eight SYN packets—the opening packet of a TCP
connection—sent toward the command-server IP. They all had the same source
port and TCP sequence number, so they were retries from one unanswered attempt,
not eight connections. There was no server reply, no completed handshake, and
no application data, but the capture could not prove that the firewall caused
the failure.
I therefore stopped treating it as proof of successful firewall containment or an HTTP request. The deep-analysis plan prohibited the entire run-and-capture procedure: no full sample execution and no contact with the real command servers. This correction is a concrete example of the contract working as intended: when the safety evidence was weaker than the story, the claim became narrower and the method changed.
What the evidence established
Two findings became the main tests of the method: where the backdoor obtained
its command-and-control addresses, and whether mdworker_shared was only a
clipboard replacer. Both conclusions were accepted only after the relevant
machine code, a second architecture, and offline checks agreed.
Recovering the hidden command-server addresses
Ordinary string searches found no command-server address in the backdoor. A high-entropy local configuration file made one early explanation tempting: perhaps the file was a key that decrypted the server address. Direct attempts to decode the file or brute-force the binary’s constant section did not support that story.
The useful evidence came from small functions that built strings one byte at a time. Rather than run the malware, the agents interpreted only those functions as Ghidra P-code, a common low-level representation into which Ghidra translates both Intel and Arm instructions. The purpose-built interpreter supported only the byte and memory operations needed by the string builders. It had no implementation for the malware’s file, process, or network calls, so the interpreted code could not perform those effects. The recovered transformation was:
P-code for a string builder
-> decode hexadecimal-looking text through the malware's lookup table
-> XOR the bytes with a seed derived from an anti-analysis check
-> Base64-style decoding with the malware's own 64-character alphabet
-> plaintext
In plain language, each function assembled a disguised string, and the correct
text appeared only when an anti-analysis check supplied the expected seed. On a
real run, the backdoor would execute an AppleScript that inspected hardware
details for markers such as QEMU, VMware, and KVM. That script exited with
status 100 when it detected an analysis environment and 0 otherwise. The
main program used that number to derive the XOR seed: status 0 produced the
valid hidden strings, while status 100 produced none of the expected values.
The static analysis recovered this AppleScript as text but never executed it.
AI agents implemented the byte-level interpreter. I had proposed and approved the static route, required the two architectures to be recovered separately, kept victim data off the remote node, and reviewed the claim against the evidence standard.
The resulting claim can be audited as a chain rather than accepted as prose:
| Stage | What was checked |
|---|---|
| Claim | The backdoor embeds two command-server endpoint bases; the local configuration value does not decrypt or construct them. |
| Primary evidence | Isolated string builders reconstruct the endpoints, while address-level data flow sends the configuration’s first line unchanged as registration material. |
| Cross-check | Independently analyzed Intel and Apple Silicon machine code produced the same hidden strings and startup behavior. |
| Offline checker | A small checking program verifies artifact hashes, expected values, safety metadata, and agreement between the two exported result sets. It does not rerun the full reverse engineering. |
| Remaining unknowns | The client alone cannot show whether the servers were online, which commands an operator issued, or what data was historically received. |
The recovered endpoint bases are written here in defanged form as
45[.]94[.]47[.]204 and foto[.]gd: replacing each dot with [.] prevents
browsers and security tools from turning them into live, clickable links. The
same evidence set recovered the registration, task-polling, and
interactive-terminal paths. In total, the two architectures agreed on the set
of eighteen hidden strings used by this part of the backdoor.
Figure 1. A concrete claim moves from machine code to a static reconstruction, an architectural cross-check, and a scoped conclusion. The red boundary keeps unanswered incident questions outside the positive result.
This example is what “evidence gate” means in practice. The agent’s report was not the terminal object. The terminal object was a claim linked to inspectable evidence, a different implementation of the same behavior, an automated check with a stated scope, and a list of unanswered questions.
Reclassifying mdworker_shared
mdworker_shared was initially described as a relatively narrow clipboard
replacer. Deeper static analysis forced a more serious conclusion.
Its main loop watched the macOS clipboard roughly once per second and
recognized many cryptocurrency address formats. Matching addresses could be
replaced by built-in or remotely supplied values. Before replacement, however,
the code ran a separate secret detector. It recognized phrases of 12, 15, 18,
21, or 24 words drawn from an embedded 2,048-word BIP39 list. It also recognized
private keys—the secret credentials that control wallet funds—in several
representations: raw hexadecimal text; WIF, a common Bitcoin key format; and
extended keys such as strings beginning with xprv, which can derive an entire
family of wallet addresses. Another path packaged the clipboard content
together with the name of the frontmost application for submission to its
command server.
The wording was deliberately narrow. BIP39, a widely used standard for wallet recovery phrases, also includes a checksum that helps distinguish a valid phrase from an arbitrary sequence of listed words. The code checked word counts and list membership but not that checksum. The report therefore says “BIP39-style” rather than claiming that every match would be a valid wallet mnemonic.
The Intel and Apple Silicon analyses agreed on the component’s hidden strings and core control flow. The Arm build contained nineteen computed jumps—jumps that choose their destination at runtime rather than naming it directly—that Ghidra could not turn into readable control flow. To produce inspectable pseudocode, the agents temporarily replaced each jump inside a disposable Ghidra project with a direct branch whose destination had already been confirmed from the original assembly. The malware file itself was never modified. For every replacement, the analysis saved the address, original and replacement bytes, destination, and untouched instruction range, so the readable result could be traced back to unmodified code. That was enough to replace “clipboard clipper” with a more accurate description: a cryptocurrency stealer combining clipboard hijacking, wallet-secret collection, remote configuration, and event reporting.
Corrections and lessons for agent oversight
The claims that failed are as informative as the capabilities that survived.
| Initial story | Decisive check | Revised claim | Correction source |
|---|---|---|---|
apple.net was a command-server domain | Read the surrounding entries in macOS’s Unified Log, its system-wide event log | It was part of an Apple logging label, not a domain | I challenged the attribution |
Thousands of recent command-line network calls (curl) were malware beacons | Attribute the count to processes and time-correlated activity | The system-wide count included normal software activity and could not be assigned to the sample | I challenged the attribution |
| The high-entropy configuration value decrypted the command-server addresses | Trace the value from file read to its consumer | It was sent unchanged during registration; the endpoints were embedded elsewhere | The agent rejected this local hypothesis during its analysis loop |
| Eight SYN packets proved eight connections and successful firewall blocking | Compare source ports and sequence numbers, then inspect whether the pf ruleset was active | They were retries from one uncompleted attempt; the cause of failure remained unknown | Tightened during the evidence audit |
The pattern is more important than who noticed each error. A plausible clue had been promoted into a larger story before its attribution or data flow was established. The durable response was to make those checks part of the process, so that future claims did not depend on someone having the same moment of skepticism again.
Those corrections changed how I think about agent oversight in three ways.
Autonomy is not authority. The agents were useful precisely because they could execute long technical loops, revise local hypotheses, and produce structured evidence. But permission to perform work was not permission to decide what the incident record should claim.
Review should change the evidence channel. Asking the same agent to “double-check” can preserve its original framing. Review became more useful when it meant reading the log context, following a value to its consumer, comparing another architecture, or checking frozen outputs. A new paragraph of reasoning was weaker than a new object of evidence.
Oversight should be designed for failure. The goal was not to watch every command. It was to ensure that a failed hypothesis produced a revision, not an unsafe network action or an overstated conclusion. Permissions, stop rules, saved intermediate evidence, and explicit unknowns made that possible.
This connects the incident to my J-space causal audit. One project concerned claims about model internals; the other concerned adversarial software. In both, the decisive research move was to withdraw an appealing interpretation when its identifying evidence failed. The common theme is building empirical methods that remain trustworthy when models, tools, or evidence can mislead.
Scope, responsibility, and next steps
The core static analysis is complete for both samples and both architectures, and the stage-specific offline checks pass. The remaining work is release engineering: consolidate the evidence matrix and unknowns, write and test stable detection rules, reconcile stale top-level documents, and build a sanitized public artifact.
The complete incident repository is intentionally private because it contains live samples, victim-specific configuration, browser evidence, packet data, and identifying paths. A public package should contain sanitized reports, analysis scripts, manifests, selected static outputs, and defanged indicators, but no binaries, credentials, sessions, raw browser history, or victim-specific captures.
Several limits remain:
- The malicious-ad timeline is strong, but the exact initial execution command was not recovered.
mdworker_sharedappeared after a backdoor capable of shell execution, but delivery through that backdoor remains an inference.- Static analysis and cross-architecture agreement cannot identify the operator or reconstruct a complete victim timeline.
- Host cleanup or replacement and the public detection package are still being closed out; this is a scoped case study, not a final incident advisory.
The easy version of this story is that an AI found and reverse-engineered malware. The useful version is less cinematic: a capable agent surfaced an anomaly and performed much of the technical execution inside a human-designed system of constraints and evidence gates.
The recovered malware behavior is the immediate result. The reusable result is a way to make agent work auditable: constrain unsafe actions, preserve the objects behind important claims, seek disagreement through another evidence channel, and allow uncertainty to survive into the final account.
Role and acknowledgments
I was the project owner and research decision-maker. I initiated the system debugging, escalated the anomaly into a formal incident response, designed the investigation plan and safety protocol, chose the priorities and decision gates, initiated the major course corrections, reviewed the evidence, and take responsibility for this account. Claude and Codex performed most of the concrete technical execution, including process inspection, decompilation, analysis scripting, isolated-function interpretation, saved-evidence generation, and report drafting. They also proposed small refinements and corrected local hypotheses. Credential rotation and other identity-bound remediation actions were performed by me from a clean device.