Fake interviews, a Terraform lock file and $292 million: inside TraderTraitor’s developer-first playbook

In April 2026, an attacker stole roughly $292 million in about an hour from a crosschain bridge, after spending six weeks inside the infrastructure of the protocol that secured it. The way in was a fake job interview and a Terraform project.

SentinelOne published new analysis of that campaign in September 2026, and the part worth reading twice is not the theft. It is that the same tooling was found on a second machine, at an IT services company in India with no cryptocurrency business at all [1]. One engineer’s laptop, one cloned interview repository, and two macOS backdoors that sat dormant for eleven days before anything happened. This post sets out the whole chain, the mechanism that makes a Terraform lock file an execution primitive, the tradecraft inside both implants, and what the actors did with the access once the door was open.

It draws on the SentinelOne Labs report, the LayerZero Labs incident report published after the theft, SentinelLABS’s earlier analysis of the first implant family, Google’s reporting on a parallel DPRK developer campaign, and the government advisories that have tracked this actor since 2020.

The campaign in one table

FieldDetail
ActorTraderTraitor, tracked as UNC4899 and also known as PUKCHONG, Jade Sleet and Pressure Chollima. Assessed as aligned with North Korea’s Reconnaissance General Bureau, with financial theft as the primary mission [2]
Documented in this waveTwo intrusions sharing one toolset: the LayerZero Labs compromise behind the April 2026 theft, and a second victim in the IT services sector with no cryptocurrency ties [1][2]
Initial accessSocial engineering of developers through fake job interviews, ending in a weaponised GitHub repository [1][2]
Execution primitiveA modified .terraform.lock.hcl naming an attacker-controlled provider registry that imitates HashiCorp domains; terraform init downloads and runs the provider [1][2]
ImplantsFLATROOF (tracked by SentinelLABS as macOS.Gaslight), a Rust backdoor with Telegram command and control, and ROOFDECK, a Rust backdoor that resolves its command and control through the Nostr protocol [1][3]
Access obtainedFiles and session material from the developer endpoint, then cloud and source control access using the victim developer’s own sessions. At LayerZero, SSO and mandatory MFA were enforced throughout [2]
EscalationLateral movement into GKE clusters, then in-memory patching of running op-geth processes so forged crosschain responses would be attested [2]
Outcome116,500 rsETH, approximately $292 million, unlocked on 18 April 2026. At the second victim, no theft at all [1][2]
AttributionCrowdStrike and Mandiant attribute the attack to DPRK with high confidence, and to UNC4899 specifically with medium confidence. Independent researchers tanuki42 and tayvano reached the same conclusion from on-chain analysis [2]
Ecosystem contextThe same month, a different DPRK cluster compromised the axios npm package through a maintainer account. Developer targeting is not one group’s tradecraft, it is the ecosystem’s [4]
Attack chain from fake interview to $292 million theft

Figure 1. The LayerZero case end to end. Note where the EDR, the SSO and the MFA all sat: in the middle of the chain, not at the start.

The actor, and why developers are the target

The FBI named TraderTraitor in February 2025 when it attributed the theft of approximately $1.5 billion in virtual assets from the exchange Bybit to North Korea, and described the actor’s tradecraft as ongoing and rapid, with stolen assets converted and dispersed across thousands of addresses on multiple blockchains [9]. The LayerZero report notes that UNC4899 was responsible for the Safe{Wallet} compromise behind that theft, and describes the group as possessing espionage capability while being focused primarily on state-backed financial gain [2].

That focus has been visible in government reporting for years. A joint advisory from the FBI, CISA and the Treasury in 2022 described the same cluster under the names Lazarus Group, APT38, BlueNoroff and Stardust Chollima, and documented the pattern this post examines: intrusions beginning with recruitment-themed messages sent to employees of cryptocurrency companies, often those working in system administration or software development and IT operations, inviting them to run trojanised applications [5]. The malware family in that advisory was AppleJeus, pushed through fake cryptocurrency trading software [5].

Two other campaigns fill in the picture, because they show the same thesis being executed by different teams with different tooling.

  • Contagious Interview. Unit 42 has tracked a cluster it calls CL-STA-0240 since 2023, where actors pose as recruiters on job platforms, move the conversation to an interview, and persuade the candidate to run code. The payload has been the BeaverTail downloader, which in turn delivers the InvisibleFerret backdoor, compiled for both macOS and Windows [6].
  • The axios npm compromise. On 31 March 2026, Google’s Threat Intelligence Group observed a malicious dependency named plain-crypto-js introduced into axios releases 1.14.1 and 0.30.4 after the maintainer account was compromised and its email address changed to an attacker-controlled address. The dropper deployed the WAVESHAPER.V2 backdoor on Windows, macOS and Linux. Google attributes that operation to UNC1069, a separate financially motivated DPRK actor active since at least 2018 [4].

The distinction matters for defenders. This is not one group with one malware family. It is several North Korean clusters, with different naming conventions and different implants, converging on the same conclusion: the fastest route into a well-defended organisation runs through the machine of the engineer who already has the access you want.

Stage 1: the interview is the intrusion

SentinelOne describes contact from a recruiter who is targeting job seekers at the company that is ultimately compromised. The GitHub profiles of the targeted job seekers it identified fall into DevOps or cryptocurrency and FinTech engineering work, and the repositories used as interview tasks are themed as infrastructure engineering projects for the company the actor is posing as [1]. In the LayerZero case, the developer who was approached, referred to in the incident report as Developer1, was part of the team responsible for running and maintaining RPC nodes [2].

The repositories SentinelOne identified include gtn-candidate-repo, which was the repository named in the LayerZero report, along with Northwind-IAC, novacart-interview and terraform-candidate-repo [1]. Two further brand names appear, Northwind and Novacart, and SentinelOne notes it is unclear whether those were fabricated companies or real ones being impersonated, with one Northwind description presenting it as an ecommerce company launching in the near future [1].

Two details make this more than generic phishing. The first is that the projects are built to be opened: the LayerZero repository weighed 1.37 GB, so the malicious provider is a small part of a plausible pile of infrastructure code [2]. The second is that the attack asks the candidate to do something entirely normal. Running terraform init on an interview project is exactly what the task is for.

There is also a data point that argues for training over tooling. In one repository, the candidate being interviewed added a note saying they had removed a typosquatted provider from the project’s original lock file, which SentinelOne suggests means the candidate believed the interviewer was testing their security awareness [1]. The person did the right thing for the wrong reason, and it worked.

Stage 2: why a Terraform lock file executes code

The malicious component is a file most reviewers treat as bookkeeping: .terraform.lock.hcl. Four facts about Terraform explain why it is a viable execution primitive [7][8].

Why a Terraform lock file can execute code

Figure 2. The mechanism in four facts. The last one is the important one: the integrity check confirmed the tampering rather than catching it.

First, a Terraform provider is not a library. It is a plugin binary that Terraform downloads for your platform and executes as a subprocess, and installing a provider means running a third party’s code on the workstation [7][8]. Second, a provider’s source address in the configuration and the lock file has the form HOSTNAME/NAMESPACE/TYPE, and the hostname defaults to registry.terraform.io when it is omitted, which means naming a different registry host is supported behaviour rather than a malformed file [8]. Third, the lock file is created and updated by terraform init and is intended to be committed to version control, so it functions as the record of exactly which provider version and which package hashes Terraform will accept [7]. Fourth, and this is the crux, those hashes verify integrity rather than trust: they confirm the package matches what the registry advertised. An attacker who controls the registry also controls what it advertises.

The implementation follows directly from that. The LayerZero repository, hosted at github[.]com/pi2infra-can-4/gtn-candidate-repo.git, defined a malicious registry at registry.hashicorp-aws[.]com and requested a provider named hashicorp/awsbeta, so the full address was registry.hashicorp-aws[.]com/hashicorp/awsbeta [2]. On terraform init or terraform plan, Terraform downloaded and executed terraform-provider-awsbeta_v1.0.0, and the provider then fetched a second file from diagnose.hashicorp-aws[.]com, a path deliberately shaped to look like a gRPC schema diagnostic endpoint [2].

SentinelOne found three variants of the same idea across the repositories it reviewed, using registry.hashicorp-aws[.]com, registry.hashicorp-aws[.]io and registry.hashicorp-terraform[.]io [1]. Every one of them depends on a reader recognising that the real domain is registry.terraform.io and that anything else is a decision, not a default.

Stage 3: the two backdoors, and the tradecraft inside them

Both implants are written in Rust, compiled for ARM64 macOS, and arrived on disk at the LayerZero developer’s machine within a seven minute window on 6 March 2026 [1][2]. Neither was detected by the EDR running on that device [2].

FLATROOF: the first foothold

FLATROOF is deployed as SystemUpdate inside ~/Library/com.apple.iTunesCloud/, a path that borrows Apple’s naming conventions so that a casual glance at a process list looks unremarkable [1]. Its command set is small, which is typical for a first-stage implant: identify itself, run a shell command through execvp or posix_spawnp, kill a process by identifier, upload a file, stop [1].

The collection module is broader than the command set suggests. It gathers browser data from Chrome, Brave, Firefox and Safari, terminal command histories, installed application listings, a running-process snapshot, a system profile, and a raw copy of login.keychain-db, the macOS keychain database, and it exfiltrates over Telegram using an embedded bot token [1]. It also does one piece of preparation that matters enormously and takes one line of shell: it strips the com.apple.quarantine attribute from the second implant and sets the executable bit on it, so the next binary runs with neither a signature check nor a user prompt [1].

SentinelLABS analysed this family separately under the name macOS.Gaslight, and the detail that attracted the most attention is a payload aimed at defenders’ tooling rather than at the operating system. The implant embeds about 3.5 KB of instructions presented as 38 fabricated system messages, designed to make an LLM-assisted triage pipeline abandon or refuse its own analysis [3]. Its command and control is a Telegram Bot API polling loop carrying AES-GCM payloads over certificate-pinned TLS, and it redacts its own bot token in its runtime output, which denies the token to anyone who later captures logs or crash artefacts [3]. When two copies of the same bot poll at once, Telegram returns a Conflict error, and the implant treats that as a single-instance lock and terminates the second copy [3].

For a security team with any automated triage in the pipeline, that first detail is a new class of problem. Malware triage consumes attacker-controlled text. The implant is making an argument to whatever is reading it.

ROOFDECK: the second stage, and the dead drop

ROOFDECK is the more capable implant and is designed to be deployed after the first foothold exists, because it needs a configuration file containing at least a Nostr public key before it will connect for the first time [1]. That requirement is the interesting part.

Instead of shipping a command and control address, ROOFDECK resolves one. It pulls a live list of relays from api.nostr[.]watch, merges it with a hardcoded set of well known relays including relay.damus[.]io, nos[.]lol, nostr[.]mom and relay.snort[.]social, searches those relays for an operator profile identified by the public key in its configuration, and reads the website field of that profile to obtain its command and control URL [1]. The operator can therefore move the backend by editing a profile on a public social protocol, with no need to update the implant and nothing in the binary to block [1]. SentinelOne notes the actor has used Nostr before [1].

The rest of the implant is a full remote access toolkit, and the shape of it will be familiar to anyone who has read malware analysis of North Korean tooling: it reimplements common shell commands rather than shelling out for everything, a pattern seen in Lazarus tooling such as LightlessCan [1]. The command set covers session management, live configuration changes that persist to ~/.config/.repl_history, background tasks with pending and running states, a persist command that installs a LaunchAgent, self-update, a destroy command that uninstalls everything behind a confirmation token, arbitrary program execution, an interactive PTY shell, a reverse shell, directory and file operations, AES-encrypted zip handling for staging, upload and download, host fingerprinting including a geo-IP lookup, and clipboard read and write [1]. Clipboard write is worth pausing on: the ability to read copied secrets is expected, and the ability to plant content is how a copied wallet address becomes a different wallet address.

Commands are signed server-side by an attacker key and verified inside the implant against an embedded public key before execution, and the transport uses a pinned TLS certificate [1]. That certificate is where the operator made a mistake. It was generated with mkcert using default settings, which embedded the developer’s own environment details in the subject: mkcert development CA, with an organisational unit and common name of ub@ub-Standard-PC-Q35-ICH9-2009, the default machine name of a QEMU virtual machine running as the user ub [1]. SentinelOne used that fingerprint to pivot across the actor’s infrastructure and identified a long list of domains presenting the same certificate, including galaxy-royal[.]online, heyhay[.]online, mactroubleshoots[.]pro, tinklify[.]com and anesthesiaschool[.]com [1].

Persistence is a LaunchAgent plist in ~/Library/LaunchAgents, with a label beginning com. and a hardcoded –type=renderer argument so the process looks like a browser helper, and RunAtLoad set [1]. The binary lives at ~/Library/com.apple.internal.ck/iSync, and the implant communicates with its helper through a pipe at /private/tmp/.pipe-airway [1]. Apple’s XProtect flagged behaviour associated with the sample under rules covering browser activity, outgoing network connections and shell profile persistence [2].

The rebuild, and the anti-forensics

On 20 April 2026, the day after LayerZero published its first public statement about the incident, the actor deployed a new build of ROOFDECK to ~/Library/com.apple.appleaccountd/loginwindow, functionally identical to the previous version but stripped of symbols and debug information, and used it to delete both original implants from disk [1]. It then beaconed to a new command and control domain until 1 June, and the binary was moved to the Trash on 17 June [1]. SentinelOne describes the timing as interesting without claiming causation, which is the right posture. Either way, the operational lesson is that losing detection on the first build does not mean the analysis environment stays the same: the actor was iterating on evasion while the investigation was starting.

Stage 4: how a developer laptop became $292 million

The LayerZero incident report sets out the rest of the chain precisely, and its most uncomfortable finding is what did not fail [2]. SSO and mandatory MFA were enforced across the organisation at the time of the intrusion. No DVN signing key was compromised. The GitHub repositories showed no evidence of implanted code, and outside the RPC environment no other infrastructure was found to be compromised [2]. The attacker did not defeat authentication. They borrowed a session that already had it.

The mechanics, in the order they happened: session keys were harvested from the infected laptop; from 30 March the attacker used them to reach GCP and GitHub environments, routing through ExpressVPN, NordVPN and Mullvad; between 16 and 18 April they moved laterally into GKE clusters running RPC nodes and patched the running op-geth process in memory [2]. That patch is worth understanding, because it is the part that defeats intuition about integrity. Two artefacts were recovered from crash dumps. The first was an ELF position-independent launcher, which allocated memory for two shared libraries, applied relocations, resolved symbols and executed them, hardcoding the paths /usr/lib/librpcd.so and the musl C library [2]. The second was the monitor itself, recovered with the internal name librpcmon.so, which uses the open source funchook library to hook the Go runtime’s Syscall and Syscall6 functions and intercept read, write, close and sendto inside the running process, so it can parse HTTP and JSON and decide what to modify, deferring that decision to yet another library that was not recovered [2]. The node answered monitoring tools honestly and lied only to the verifier.

Then the attacker removed the ability to disagree. On 18 April at 16:30 they launched a denial of service attack against external RPC providers, which forced LayerZero’s failover logic to depend exclusively on the two poisoned internal nodes; at 17:35, 116,500 rsETH was unlocked [2]. The application owner had configured the channel with a single required verifier, having previously been at two [2].

LayerZero’s own analysis of where the stack failed is worth reproducing as a table, because it separates the controls that held from the controls that did not [2].

LayerOwnerWhat happened on 18 April
Number of independent signing services requiredApplicationFailed. Configured to one, so no second verifier existed to disagree with the forged attestation
Source chain confirmation depthVerifier operatorNot bypassed, but rendered meaningless: the attacker fabricated a confirmed event rather than defeating the depth check
Signer key custodyVerifier operatorHeld. Keys were not compromised and no exfiltration was found
RPC quorum, IAM and payload validation around the signing serviceVerifier operatorFailed. Internal nodes were patched in memory and external nodes were forced offline
On-chain quorum checkContractHeld. The signatures were valid against the registered signer set, because they were produced by the legitimate keys

Read that table again as a defender, because the failure is not a broken control. It is a control that worked exactly as designed on data that had already been falsified. The cryptographic guarantees were intact; the inputs to them were not. LayerZero’s own conclusion is the same: the signing service assumed its RPC layer was honest, and the assumption was violated by an attacker with the capability to violate it [2].

The intrusion that earned nothing

The second victim in the SentinelOne report never made the news, and the reason is instructive. The organisation is an IT services provider in India with no cryptocurrency connection, and the intrusion involved a single Apple Silicon MacBook belonging to a DevOps engineer who ran Terraform and Ansible against AWS, OVH and OpenStack on most working days [1]. The machine held cloud credentials and source control access, which is precisely why it was targeted: as SentinelOne puts it, the value of the target is whatever their laptop can reach [1].

The backdoors were present on disk by 18 March 2026 and dormant until 29 March, when the engineer opened a workspace in Cursor and the IDE’s integrated terminal spawned login shells and Node hosts. Within a minute the implants were running [1]. From then until 19 April the beaconing was gated on Cursor sessions: the implants were quiet whenever the editor was closed, which means the activity profile of this intrusion was shaped to look like normal developer behaviour [1]. On 20 April the actor pushed the stripped ROOFDECK build and used it to remove the earlier implants, exactly as at LayerZero, and the last command and control activity was observed on 1 June [1].

SentinelOne’s assessment is that this victim was small enough to yield insufficient value to sustain the intrusion [1]. That is a useful corrective to incident-driven threat reporting. The same actor, the same implants and the same delivery mechanism produced a nine-figure theft in one case and nothing in another, and the difference was the blast radius of the laptop, not the sophistication of the attack.

Timeline

Date (2026)Event
6 March, 12:59 to 13:06A LayerZero RPC team developer clones a malicious interview repository. FLATROOF and ROOFDECK land on the MacBook. The EDR does not alert [2]
18 MarchBoth implants present on the second victim’s MacBook according to telemetry, delivery method not proven [1]
29 March, 05:00The second victim opens a Terraform workspace in Cursor. The IDE spawns login shells and Node hosts, then launches both implants seconds later. Quarantine attributes are stripped and the implants beacon [1]
30 March to 16 AprilAt LayerZero, session keys are used to access GCP and GitHub, routed through commercial VPN services. Reconnaissance and further persistence [2]
13 AprilThe second victim’s developer clones terraform-candidate-repo through GitHub Desktop, showing the lures were still circulating after the first intrusion [1]
16 to 18 AprilGKE lateral movement and in-memory poisoning of op-geth processes on two clusters in two regions [2]
18 April, 16:30Denial of service against external RPC providers forces the failover that leaves only the poisoned internal nodes [2]
18 April, 17:35116,500 rsETH unlocked, approximately $292 million at the time [2]
19 to 20 AprilLayerZero publishes its first statement. The next day the actor deploys a stripped ROOFDECK build and uses it to delete both original implants on both victim machines [1]
1 JuneLast command and control activity observed on the second victim’s host [1]
17 JuneThe implant binary is moved to the Trash on the second victim’s machine [1]

What this means for a security programme

LayerZero’s response included changes that are more interesting than the usual hardening list. The organisation rebuilt the compromised cloud environment rather than patching it, carrying over no legacy credentials or service accounts; privileged access now requires just-in-time elevation with short-lived credentials, multi-person review for IAM changes, and both device and session validation on every administrative request [2]. Separately, its verifier now refuses to participate in a channel where it would be the sole required attestor, and will not sign on its own. The protocol is unchanged and still enforces whatever an application configures; what changed is what this particular operator is willing to be part of [2].

Three generalisable conclusions follow from the whole chain, and the companion post on this site covers the detection and mitigation detail for each.

  1. The developer workstation is an identity-bearing asset. It holds credentials for cloud, source control and pipelines, and the tools it runs are designed to fetch and execute third-party code on demand. Treating it as a managed laptop rather than a privileged system is the root error [1][2].
  2. Removing standing credentials changes the economics of session theft. The attacker’s advantage came from holding a valid session, not from breaking authentication. Short-lived, device-bound credentials with just-in-time elevation turn a stolen token into a token that expires elsewhere [2].
  3. Verification needs independence, and monitoring needs to not share a failure mode with the thing it watches. One additional required verifier would have forced the attacker to forge agreement between systems that do not share a data source [2]. That is a configuration decision, and it is cheap.

Sources

  1. SentinelOne Labs, “Don’t Call Us, We’ll Call Your APIs: TraderTraitor Backdoors Resurface on Victim With No Crypto Ties” (18 September 2026), the report this post extends, and the source for the second victim, the interview repositories, the implant mechanics and the indicator set. · https://www.sentinelone.com/labs/dont-call-us-well-call-your-apis-tradertraitor-backdoors-resurface-on-victim-with-no-crypto-ties/
  2. LayerZero Labs, “KelpDAO Incident Report” (18 May 2026), the primary account of the March to April 2026 intrusion, including the attack timeline, the RPC poisoning analysis and the security changes made afterwards. · https://layerzero.network/publications/kelpdao-incident-report.pdf
  3. SentinelLABS, “macOS.Gaslight: Rust Backdoor Turns Prompt Injection on the Analyst, Not the Sandbox” (23 June 2026), the technical analysis of the FLATROOF implant, its Telegram command channel and its prompt-injection payload. · https://www.sentinelone.com/labs/macos-gaslight-rust-backdoor-turns-prompt-injection-on-the-analyst-not-the-sandbox/
  4. Google Threat Intelligence Group, “North Korea-Nexus Threat Actor Compromises Widely Used Axios NPM Package in Supply Chain Attack” (1 April 2026), covering the parallel developer-supply-chain campaign attributed to UNC1069. · https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package
  5. FBI, CISA and US Treasury, joint Cybersecurity Advisory AA22-108A, “TraderTraitor: North Korean State-Sponsored APT Targets Blockchain Companies”, documenting the recruitment-lure tradecraft and the actor aliases used by the US government. · https://www.cisa.gov/news-events/cybersecurity-advisories/aa22-108a
  6. Unit 42, “Contagious Interview: DPRK Threat Actors Lure Tech Industry Job Seekers to Install New Variants of BeaverTail and InvisibleFerret Malware”, for the longer-running fake-recruiter campaign and its malware families. · https://unit42.paloaltonetworks.com/north-korean-threat-actors-lure-tech-job-seekers-as-fake-recruiters/
  7. HashiCorp, “Dependency Lock File (.terraform.lock.hcl)”, the documentation describing what the lock file records, how terraform init maintains it and how package hashes are used. · https://developer.hashicorp.com/terraform/language/files/dependency-lock
  8. HashiCorp, “Provider Requirements”, the documentation defining the provider source address format of hostname, namespace and type, and the default registry hostname. · https://developer.hashicorp.com/terraform/language/providers/requirements
  9. FBI Internet Crime Complaint Center, PSA I-022625-PSA, “North Korea Responsible for $1.5 Billion Bybit Hack” (26 February 2025). · https://www.ic3.gov/PSA/2025/PSA250226

The pattern worth internalising

Strip out the cryptocurrency context and the shape of this attack is a hiring workflow. Someone you want to hire sends you code, asks you to run it, and it runs with everything your account can reach. The reason it works is not that developers are careless. The reason it works is that the request is legitimate, the tooling is designed to execute remote code by design, and the credentials that make the engineer productive make the laptop valuable.

Published for defensive education. Technical details are attributed to the sources listed above, and indicators should be validated against your own telemetry rather than treated as durable blockers, since this actor rebuilds and re-stages its tooling within days of public disclosure [1][2].