Skip to content

About

A macOS forensic case study in mutable trust, user-scoped persistence, modular collection, and a Polygon dead-drop resolver

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

36 Commits

Folders and files

Repository files navigation

ClickFix on macOS: Forensic Report

Documentation trust-chain exposure, user-scoped persistence, modular data-collection capability, and a Polygon dead-drop resolver

On 25 June 2026, routine browsing of the public redhat-developer/lsp4ij README followed an article link to idetools.dev. Incident evidence records that navigation path redirecting to a ClickFix lure that imitated a human verification step, wrote an obfuscated command to the clipboard, and instructed the user to paste it into Terminal.

The command initiated a multi-stage macOS intrusion built from shell and AppleScript components. The recovered chain established a user-scoped LaunchAgent, displayed a deceptive hidden-input dialog, resolved command-and- control infrastructure through a Polygon smart contract, and polled a resident bot. The full collection module (smodule) was active during the incident. The light, Ledger, and shell module paths were investigated later; the first two were retrieved from the server, while the shell request returned no payload.

This repository is the main forensic report and evidence map for that incident. It contains recovered payloads for static inspection, stage-by-stage analysis, the incident chronology, defensive guidance, and 16 versioned infrastructure observations through 10 July 2026.

Executive findings

  • The trusted path was real. The public LSP4IJ README contained two outbound idetools.dev article links. The first was added in June 2024 and the Debug Adapter Protocol article was added in March 2025. Both remained present in an immutable README snapshot from 30 July 2026.
  • The observed mechanism matches ClickFix. A fake verification interface copied a command and induced manual execution in a command interpreter, matching MITRE ATT&CK T1204.004: Malicious Copy and Paste.
  • Persistence did not require root. A plist in the user's ~/Library/LaunchAgents directory used RunAtLoad and KeepAlive to start and restart the resident script in the user's session.
  • The password prompt was deception, not an authentication bypass. The malware used AppleScript display dialog ... with hidden answer, which masks typed characters but returns the entered text to the script. A credential staging file existed locally before the incident alert. The recovered full collector contains code that would copy it into an archive staging tree, but execution of that staging step and successful upload are not independently proven by the available evidence.
  • The resolver is a blockchain-backed implementation of an established pattern. The malware issued a read-only Polygon eth_call, decoded the returned ABI string, and treated it as a C2 hostname. This maps to T1102.001: Dead Drop Resolver.
  • The recovered module boundary is explicit. The investigation retrieved the full and light collection modules and a 34-byte Ledger stub. The dispatcher exposed an openshell command, but the server returned an empty response when the shell module was requested; no reverse-shell payload was recovered.
  • The infrastructure changed after individual disruptions. The versioned observation series recorded three successive contract-resolved domains through 10 July 2026. That supports resolver-enabled endpoint rotation; it does not by itself establish actor identity, intent, victim count, or deliberate targeting of LSP4IJ users.

Evidence standard

Claims in this report are separated by source class and publication boundary:

Class Meaning
Public repository evidence A tracked sample, screenshot, monitoring record, or other artifact that readers can inspect in this repository
Private incident provenance Transcript, task log, browser history, or host record used to reconstruct events but excluded from publication because it contains local or personal context
Code-validated Behavior present in the recovered shell or AppleScript source
Externally verified Confirmed through an authoritative specification or immutable upstream source
Inferred An explanation consistent with multiple observations but not directly proven
Unknown A material question the available evidence cannot resolve

Historical infrastructure states are always dated. A domain's state in a monitoring record is not represented as its current state. Private account details, local identifiers, credentials, and workstation paths are excluded from the public report. File paths and SHA-256 digests in the sample manifest provide stable public evidence identifiers; private provenance is described by source class without exposing its location.

Incident and trust chain

flowchart TD
    A[Public lsp4ij README] -->|outbound article link| B[idetools.dev]
    B -->|incident-time redirect| C[ClickFix verification lure]
    C -->|clipboard write + instructions| D[User-pasted Terminal command]
    D --> E[Shell and AppleScript launcher]
    E --> F[User LaunchAgent persistence]
    F --> G[Resident bot]
    G --> H[Polygon eth_call]
    H --> I[ABI-decoded C2 hostname]
    I --> J{Operator command token}
    J --> K[Full collection module]
    J --> L[Light collection module]
    J --> M[Ledger stub]
    J --> N[Shell path advertised; payload not recovered]
Loading

The documentation link is the observed arrival path. The available evidence does not show modification of LSP4IJ source code, dependencies, builds, releases, or update infrastructure. This report therefore uses documentation trust-chain exposure, not “software supply-chain attack.” It also does not classify the activity as an APT or claim that the operator intentionally selected LSP4IJ's audience.

ClickFix lure captured during the incident

Initial verification state Instructions displayed after interaction
Minimal fake verification interface with a Prove you're Human checkbox Fake verified state instructing the user to open Terminal and paste clipboard content
The page presents a deliberately sparse human-verification control. The completed state converts the browser interaction into explicit keyboard instructions for opening Terminal and pasting the clipboard payload.

The screenshots document the social-engineering interface, not execution by themselves. Execution is established separately by the recovered clipboard sample and private incident provenance.

Response chronology

The high-confidence early response times come from the incident transcript and tool records:

UTC Event Evidence boundary
14:50:28 User raised the active-malware alert Start of the measured response interval
14:50:29 First recorded response warning advised against approving credential prompts The credential file already existed; the warning did not prevent its initial capture
14:50:44 Active malicious process identified Bounded process observation
14:52:09 LaunchAgent persistence identified Filesystem and launchd evidence
14:53:08 Initial containment checks found no remaining process or persistence artifact in the tested locations End of the 160-second interval; not proof of exhaustive eradication
15:06:15 Credential rotation was recorded as completed Post-containment response; deeper forensic investigation continued

The 160-second interval measures the alert through the documented initial containment verification. It is not infection-to-containment time, proof of exhaustive eradication, or the duration of the broader investigation. See Incident Response Timeline for the detailed historical chronology and evidence boundaries.

Recovered malware architecture

Component Repository sample Code-validated role Recovery boundary
Clipboard command Incident Clipboard Payload Base64-wrapped shell execution and remote retrieval Exact incident clipboard sample
Launcher Stage 1 Launcher Source Decodes and invokes the AppleScript chain Recovered outer shell; embedded content retains an encoded wrapper
Persistence layer Stage 2 Persistence Source Writes the resident script and user LaunchAgent with restart semantics Recovered and decoded
On-chain resolver Polygon Resolver Source Queries Polygon RPC endpoints with eth_call and decodes the returned string Recovered, still partially obfuscated
Resident bot Resident C2 Bot Source Fingerprints the host, polls C2, and dispatches command tokens Recovered; identifiers and string construction remain obfuscated
Full collector Full Collector Source Contains browser/wallet, Notes, local-file, credential-staging, archive, and upload logic An smodule delivery process was active during the incident; this repository sample was retrieved later during investigation
Light collector Light Collector Source Executes a narrower browser/wallet collection path with ZIP staging and upload logic Retrieved from C2 during investigation
Ledger module Ledger Stub Source Only redirects output to /dev/null Retrieved as a 34-byte stub; no operational replacement behavior recovered
Shell module Not present Dispatcher contains an openshell delivery path Server returned an empty response; implementation unknown

Sample integrity manifest

These digests identify the exact public artifacts analyzed by this README. Where the public record does not preserve an acquisition timestamp, that gap is stated instead of reconstructed from later filesystem or Git metadata.

Evidence ID Path Public acquisition record Classification Bytes SHA-256
CF-SCREEN-01 Initial Browser Capture Incident browser capture; exact UTC not recorded publicly Repository visual evidence 8,474 21a451e471d193753ed97690f269c15779524697a6d9cb6b3aa3e031dbd54093
CF-SCREEN-02 Verified Browser Capture Incident browser capture; exact UTC not recorded publicly Repository visual evidence 19,398 1dbecc8397f63edb17a6a7fbc0c2833e22cb99255936015523cc412904ef55dd
CF-CLIPBOARD-01 Incident Clipboard Payload Incident clipboard; 25 June 2026, exact UTC not recorded publicly Repository text evidence 107 c6e3eee3a38fd38e313640aa9be4338e4d129ac705d02293d1cf18d02516504b
CF-STAGE1-01 Stage 1 Launcher Source Derived during investigation; exact UTC not recorded publicly Partially decoded derivative 19,501 3c01affccb4c3480c8e17124500411688c5d88e8fa5479cf7486332616922b10
CF-STAGE2-01 Stage 2 Persistence Source Derived during investigation; exact UTC not recorded publicly Decoded derivative 14,596 c34436a61ac8507824107765af7fc6903eba1c02faefa10c5b61ee6cfdf050dd
CF-RESOLVER-01 Polygon Resolver Source Derived during investigation; exact UTC not recorded publicly Partially deobfuscated derivative 10,254 ee56fb83bb2902ca99d9a79f6e0445dd0268d09ce59dc08cb26ac8cee965f6e9
CF-BOT-01 Resident C2 Bot Source Derived during investigation; exact UTC not recorded publicly Partially deobfuscated derivative 31,757 155c48004f3cb1a11d85d69e993dd26bf98385e3a2d8320b45e24510d45b8698
CF-SMODULE-01 Full Collector Source Investigation-time C2 retrieval; exact UTC not recorded publicly Repository derivative of retrieved response 195,558 41bad2af1225bca6c1c553ee7e18585df49bee8cb7f30f2fde855638fbc8364e
CF-LMODULE-01 Light Collector Source Investigation-time C2 retrieval; exact UTC not recorded publicly Repository derivative of retrieved response 168,358 992588fdf36fcf74c3a82bfc07a26c41d0277356152ff8fc37bfdf56b16b3c4c
CF-LEDGER-01 Ledger Stub Source Investigation-time C2 retrieval; exact UTC not recorded publicly Repository derivative of retrieved response 34 ef94799c12f00235920699106e27d9a45595549845724f3c9dede02aa5b96a06

Persistence and credential capture

The persistence plist is placed in the user's LaunchAgents directory and runs a resident shell script with both RunAtLoad and KeepAlive. Apple's launchd documentation confirms that per-user agents are loaded from the user's Library/LaunchAgents directory and execute in that user context. This is user-scoped persistence, not a root LaunchDaemon.

The resident code also displays an AppleScript hidden-input dialog styled as a system-related prompt. Apple's own scripting documentation shows that hidden answer merely replaces visible characters with bullets and returns the plain entered text to the script. The recovered code stores that value locally; the full collector contains logic that reads it into a staging directory and provides both primary-domain and hardcoded-IP upload commands.

The defensible impact statement is therefore:

  1. credential capture on the host is observed;
  2. credential staging for archive upload is a code-validated capability, not an observed completed step;
  3. the full collector was active during the response; and
  4. completion of the final archive upload remains unknown.

Modular collection capability

The two recovered collectors target overlapping but different data sets. The full module contains broader local-file and application collection logic; the light module is narrower. Both contain browser and cryptocurrency-wallet extension targeting, archive staging, and upload paths. Detailed code-level analysis is available in:

Any discussion of why an operator would choose one module over another is an analyst hypothesis unless the decision logic appears in recovered C2 material.

Polygon dead-drop resolver

The resolver separates the location of a C2 endpoint from the installed sample:

  1. the malware sends JSON-RPC eth_call requests to multiple public Polygon endpoints;
  2. the call targets contract 0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0 with selector 0x2686ecea;
  3. the returned bytes are decoded as a dynamic ABI string; and
  4. the resulting hostname becomes the bot's C2 endpoint.

An eth_call locally executes or simulates a contract call against a selected block state and returns the result without creating an on-chain transaction. The contract's state can be changed through separate state-changing transactions, allowing the pointer to change without replacing the installed malware. This increases operational resilience, but it does not make domain disruption useless: disabling the current domain interrupts that endpoint until another usable value is published and resolved.

The investigation did not identify a verified contract source or ABI. The selector's function name must not be guessed from the four-byte value alone.

Longitudinal infrastructure observations

The repository contains 16 timestamped observation documents under monitoring/. They record DNS, HTTP, TLS, registration, hosting, and on-chain states collected after the incident.

Indicator Function Repository observation boundary
idetools.dev Outbound documentation destination Incident evidence records the redirect chain; later observations record different redirect behavior. Expiration, takeover mechanism, and actor control remain unresolved
295e5cd2.sessionaquirecheck.pages.dev ClickFix landing page Recorded as unavailable in later repository observations
maccf9c.jetbet4.online Initial payload host Later recorded as blocked, degraded, or parked depending on observation date
sj98xe4.xyz First decoded C2 hostname Later recorded under registrar hold
apdhlhs3.xyz Second decoded C2 hostname Later recorded as unavailable/NXDOMAIN
j9af4sr.guru Third decoded C2 hostname Last repository observation: 10 July 2026
62.60.226.50 Hardcoded fallback endpoint in full collector Availability varied across observations
Polygon contract C2 pointer Persisted beyond individual domain-state changes

The sequence is compatible with endpoint rotation through the contract pointer. It does not prove that changes were responses to this investigation, that a specific actor controlled every related domain, or that every returned hostname served an operational C2 when queried.

Observed impact and unresolved questions

Question Supported conclusion
Was code executed? Observed. The user-pasted command launched the recovered shell/AppleScript chain
Was persistence installed? Observed and code-validated. A user LaunchAgent and resident script were created and active
Was a credential captured? Observed. The credential staging file existed before the alert; the value is excluded from this repository
Was the credential prepared for upload? Capability established; execution unknown. The full collector contains code that would copy it into the archive staging tree, but the available evidence does not prove that step completed
Did the archive upload complete? Unknown. Upload logic and an active collector are present, but no independent completion evidence is available
Were browser and wallet files targeted? Code-validated. Both recovered collectors enumerate browser profiles and wallet-extension data
Were protected browser secrets decrypted? Not established. The incident record includes denied Safe Storage requests; successful decryption is not proven
Were Notes, Desktop, or Documents contents collected? Capability established; result unknown. The collector contains code for those sources, but the available evidence does not establish which steps completed or the contents of any archive
Was a working Ledger replacer recovered? No. Only a 34-byte stub was returned
Was a reverse-shell payload recovered? No. The dispatcher exposes the command path, but the module request returned empty
Were other victims identified? No conclusion. This is a single-host forensic record
Was the actor an APT or deliberately targeting developers? Unresolved and not claimed. The evidence does not support attribution or an APT classification

Repository structure

.
├── README.md                          # Main forensic report and evidence map
├── analysis/                          # Recovered payloads and partially deobfuscated derivatives; static inspection only
│   ├── stage1_launcher.sh
│   ├── stage2_persistence.applescript
│   ├── stage2b_inner_osascript.applescript
│   ├── stage3_c2_bot.applescript
│   ├── stage4_smodule.applescript
│   ├── stage4_lmodule.applescript
│   └── stage4_ledger.sh
├── docs/                              # Stage analysis, IOCs, chronology, method, and rewritten agent transcripts
│   ├── 01_attack_vector.md
│   ├── 02_stage1_persistence.md
│   ├── 03_stage2_blockchain_c2.md
│   ├── 04_stage3_c2_bot.md
│   ├── 05_stage4_smodule.md
│   ├── 06_stage4_lmodule.md
│   ├── 07_iocs_and_remediation.md
│   ├── 08_incident_timeline.md
│   ├── 09_infrastructure_analysis.md
│   ├── 10_threat_intelligence_methodology.md
│   ├── 11_reasoning_trace_publication_method.md
│   ├── 12_rewritten_incident_response_trace.md
│   ├── 13_rewritten_longitudinal_research_trace.md
│   ├── 14_agent_safety_taxonomy.md
│   ├── references/
│   │   └── safety-taxonomy-[REDACTED].md
│   ├── transcripts/
│   │   ├── README.md
│   │   ├── 01_incident-response-rewritten.md
│   │   └── 02_longitudinal-investigation-rewritten.md
├── forensic_evidence/                 # ClickFix screenshots and clipboard sample
└── monitoring/                        # 16 versioned post-incident observations

Technical documentation

Document Coverage
Attack Vector and Documentation Trust Chain Upstream navigation, redirect evidence, ClickFix interface, and clipboard mechanics
Stage 1: Persistence and Credential Prompt Launcher, resident script, LaunchAgent fields, restart behavior, and deceptive prompt
Stage 2: Blockchain Dead-Drop Resolver Polygon RPC calls, ABI decoding, contract pointer, and defensive implications
Stage 3: Resident C2 Bot Fingerprinting, polling protocol, dispatch tokens, and recovered-versus-advertised capabilities
Stage 4: Full Collection Module Full collector: wallet/browser targeting, local files, Notes, staging, and upload logic
Stage 4: Light Collection Module Light collector and code-level differences from the full module
Indicators and Remediation Guidance IOC inventory, ATT&CK mapping, response guidance, and maintainer recommendations
Incident Response Timeline Evidence-bounded incident response, credential response, arrival-path reconstruction, and later module retrieval chronology
Longitudinal Infrastructure Analysis Consolidated 16-record infrastructure ledger with observation, inference, and attribution boundaries
Threat Intelligence Methodology Interactive and scheduled collection modes, prospective-record audit, hypothesis status, and future prediction schema
Rewritten Transcript Publication Method Source commitments, transformation rules, exclusions, and epistemic labels for the publication corpus
Incident Response Reasoning Overview Concise reconstruction from active process discovery through the first public evidence map
Longitudinal Research Reasoning Overview Concise reconstruction of post-incident research, active probing, scheduled observation, hypothesis evaluation, and publication boundaries
Agent Safety Taxonomy Applied to the ClickFix Workflow Context-dependent refusals, decision collapse, harness trust gaps, and orchestration controls
Complete Rewritten Incident Response Transcript Dense chronological reconstruction of live containment, acquisition, analysis, delegation, and documentation
Complete Rewritten Longitudinal Investigation Transcript Dense chronological reconstruction of the primary session, four subagents, tool calls, scheduler behavior, sixteen observations, and hypothesis evaluation
Safety Classifier Taxonomy English editorial edition of the researcher-supplied four-class framework used to analyze agent safety outcomes
Dated Monitoring Records Individual observation records through 10 July 2026

Core indicators

These indicators are provided for defensive correlation. Infrastructure may have changed since the dated observations.

Type Indicator Role
Domain idetools.dev Documentation destination and incident-time redirect origin
Host 295e5cd2.sessionaquirecheck.pages.dev ClickFix landing page
Domain maccf9c.jetbet4.online Initial payload host
Domain sj98xe4.xyz First decoded C2 hostname
Domain apdhlhs3.xyz Second decoded C2 hostname
Domain j9af4sr.guru Third decoded C2 hostname in the repository series
IP 62.60.226.50 Hardcoded fallback endpoint in the full collector
Contract 0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0 Polygon dead-drop resolver
Selector 0x2686ecea Read-only contract call data used by the sample

Host artifacts are documented generically in Indicators and Remediation Guidance. Incident- specific account names, identifiers, and credentials are not reproduced.

Defensive implications

  1. Documentation links are mutable trust dependencies. A safe destination at merge time can change independently of the repository. External-link review should include redirects, ownership changes, and unexpected content, not only HTTP availability.
  2. ClickFix detection should connect browser activity to terminal execution. The relevant signal is not merely a suspicious download; it is a browser or lure followed by user-pasted, often obfuscated code in a command interpreter.
  3. User LaunchAgents are a high-value persistence surface. New or randomized plist labels, RunAtLoad/KeepAlive, and shell or AppleScript children merit investigation in context.
  4. Hidden-input dialogs are not proof of authentication. A familiar-looking prompt can return plain text directly to an untrusted script without invoking a genuine privileged authorization flow.
  5. Resolver and endpoint controls are complementary. Domain action can disrupt the active endpoint; defenders should also monitor the contract, selector, RPC request context, originating process, and later pointer changes.
  6. Capability must not be confused with impact. Collection and upload code establish functionality and technical capability; successful theft requires separate evidence.

Upstream link evidence

The immutable LSP4IJ README snapshot cited above contained both idetools.dev links on 30 July 2026. That upstream snapshot establishes the repository-side link exposure; the incident-time redirect chain is established separately by the local forensic evidence.

Safe handling

  • Treat every file under analysis/ as malware source material.
  • Do not execute samples on a normal workstation.
  • Use an isolated analysis environment for dynamic work and preserve hashes, provenance, and collection timestamps.
  • Do not interact with suspected C2 infrastructure beyond authorized, read-only validation.
  • Do not contact possible victims or attempt to access third-party systems.
  • Review the detailed remediation guide before adapting commands to a host; broad process-killing or permission-reset commands can disrupt legitimate work.

External references

Research attribution and reuse

Incident response, payload reconstruction, longitudinal observation, and forensic analysis by Alefita (@aleffita).

About

A macOS forensic case study in mutable trust, user-scoped persistence, modular collection, and a Polygon dead-drop resolver

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages