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.
- The trusted path was real. The public LSP4IJ README contained two outbound
idetools.devarticle 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/LaunchAgentsdirectory usedRunAtLoadandKeepAliveto 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
openshellcommand, 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.
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.
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]
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.
The screenshots document the social-engineering interface, not execution by themselves. Execution is established separately by the recovered clipboard sample and private incident provenance.
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.
| 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 |
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 |
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:
- credential capture on the host is observed;
- credential staging for archive upload is a code-validated capability, not an observed completed step;
- the full collector was active during the response; and
- completion of the final archive upload remains unknown.
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.
The resolver separates the location of a C2 endpoint from the installed sample:
- the malware sends JSON-RPC
eth_callrequests to multiple public Polygon endpoints; - the call targets contract
0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0with selector0x2686ecea; - the returned bytes are decoded as a dynamic ABI string; and
- 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.
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.
| 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 |
.
├── 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
| 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 |
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.
- 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.
- 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.
- User LaunchAgents are a high-value persistence surface. New or randomized
plist labels,
RunAtLoad/KeepAlive, and shell or AppleScript children merit investigation in context. - 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.
- 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.
- Capability must not be confused with impact. Collection and upload code establish functionality and technical capability; successful theft requires separate 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.
- 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.
- LSP4IJ README at immutable commit
83f1c6e - MITRE ATT&CK T1204.004: Malicious Copy and Paste
- MITRE ATT&CK T1102.001: Dead Drop Resolver
- Microsoft Threat Intelligence: ClickFix technique and macOS variants
- Ethereum JSON-RPC:
eth_call - Solidity ABI specification
- Apple: Creating Launch Daemons and Agents
- Apple: Prompting for hidden text with AppleScript
Incident response, payload reconstruction, longitudinal observation, and forensic analysis by Alefita (@aleffita).

