TraderTraitor — FLATROOF / ROOFDECK macOS Backdoors via Weaponized Terraform Lock Files
.terraform.lock.hcl that pulls a malicious Terraform provider, dropping Rust-based macOS backdoors FLATROOF and ROOFDECK. ROOFDECK resolves its C2 via the decentralized Nostr protocol.Executive Summary
TraderTraitor, the DPRK state-sponsored Lazarus subgroup also tracked as UNC4899, PUKCHONG, Jade Sleet, and Slow Pisces, continues its Contagious-Interview playbook of posing as recruiters and hiring managers on GitHub to get developers to run attacker-controlled code. The objective is not the developer's laptop for its own sake — it is what the laptop can reach: cloud credentials, source control, and CI/CD pipelines. SentinelOne's 2026-09-18 report shows the group has moved beyond direct cryptocurrency targets (the USD 292M KelpDAO/LayerZero theft in April 2026) to a small IT-services shop with no crypto exposure at all, confirming this is now a general-purpose developer-endpoint intrusion vector, not a crypto-only campaign.
The technique that matters most defensively is the weaponized .terraform.lock.hcl file: victims
clone a fake "technical assessment" repository, run terraform init, and Terraform silently downloads
and executes a malicious provider binary from an attacker-controlled registry (registry.hashicorp-aws[.]com
and lookalikes). This is the earliest, highest-fidelity, and most preventable point in the entire chain —
before either backdoor (FLATROOF or ROOFDECK) ever touches disk. Everything downstream (Gatekeeper bypass,
LaunchAgent persistence, Telegram/Nostr C2, credential theft, cloud-key abuse) is harder to catch cleanly and
easier for the attacker to vary between victims.
ROOFDECK's C2 resolution is architecturally novel: instead of a hardcoded C2 address, it queries public Nostr
relays for an attacker-controlled profile and reads that profile's website field as its live C2 URL
— a dead-drop resolver built entirely on decentralized, difficult-to-take-down infrastructure that a
defender cannot simply sinkhole.
macOS.Gaslight, first analysed by SentinelLABS in June 2026 and already covered by the published
macOS-Gaslight-Hunt.html pack. FLATROOF itself is therefore not new. What is new in this
pack, and absent from that earlier one, is the second backdoor ROOFDECK, the weaponized
.terraform.lock.hcl initial-access vector, and the Nostr dead-drop C2 resolution
mechanism. The two packs share no hashes and no C2 infrastructure — this is a distinct September 2026
intrusion against a non-crypto IT-services victim. Run both: use the Gaslight pack for the June 2026
FLATROOF/BONZAI sample set and Telegram C2, and this one for the Terraform delivery chain, ROOFDECK, and the
current infrastructure.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| TIER 1 | SentinelLABS — "Don't Call Us, We'll Call Your APIs" (2026-09-18) | Primary technical report: full IOC set, infection timeline, FLATROOF/ROOFDECK internals, Nostr C2 mechanics, weaponized-repo list, TLS cert details. Authors: Albert Priego, Alex Delamotte, Matej Havranek. | Yes — primary source for all sections |
| TIER 1 | The Hacker News — "Jade Sleet Linked to Indian IT Provider Breach" (2026-09-21) | Corroborates actor aliases, FLATROOF/ROOFDECK capability summary, infection timeline, and the Bybit/Safe{Wallet} 2025 history for the actor. | Yes — corroboration + actor history |
| TIER 2 | GBHackers — "Hackers Weaponize Terraform Lock Files" (2026-09-21) | Corroborates 3 of 3 SHA1 hashes, 3 of 3 C2 domains, and the primary C2 IP; adds independent hardening guidance (registry allowlisting, LaunchAgent monitoring). | Yes — IOC corroboration + hardening |
| TIER 2 | CyberPress — "ROOFDECK Backdoor Uses Nostr Profiles" (2026-09-21) | Independent confirmation of the Nostr dead-drop C2 mechanism and 2 of 3 SHA1 hashes. | Partial — used for Nostr-mechanism confirmation only |
Prompt-injection check: all four fetched sources were reviewed for embedded instructions directed at an automated reader. None were found — every match for imperative-sounding phrasing (e.g. "override") was legitimate technical prose describing the malware's own config-override behavior, not an attempt to redirect this pipeline. No content from any source was treated as an instruction.
Confidence assessment: Overall confidence is high — this is a
vendor-named, three-researcher-credited report picked up by three outlets within 72 hours of publication.
Those three outlets are derivative coverage of the same SentinelLABS analysis, not independent
observation — so this campaign effectively rests on one first-party source, and no indicator in
this pack clears the two-independent-source bar for a prevent action. Gaps: the ROOFDECK staging IP,
two of three C2 IPs, the Nostr relay list, the TLS certificate details, and the exact on-disk binary/config paths
appear in the SentinelOne report alone — those ship as hunt/enrich. Everything
else ships as detect; nothing ships as prevent.
Perishability: C2 IPs and domains for a publicly burned campaign are typically rotated within weeks of
disclosure (this report published 2026-09-18); treat network IOCs as high-confidence for roughly 90 days from
that date, not indefinitely.
Hunt Brief & Attack Chain
Hunt Hypotheses (ordered by fidelity)
- H1 (high): Developer endpoints resolve DNS for attacker-registered Terraform provider registries before any backdoor lands — the earliest network-visible step. Covered by Q1.
- H2 (high): FLATROOF/ROOFDECK execute from non-standard, fake Apple framework-support paths that no legitimate process uses. Covered by Q2 / Q5.
- H3 (high): Persistence is installed as a LaunchAgent whose spawned process masquerades as a
Chrome renderer (
--type=renderer) but is parented directly bylaunchd, which a real browser renderer never is. Covered by Q3. - H4 (high): Confirmed C2 infrastructure (IPs/domains) receives outbound connections from developer endpoints. Covered by Q4.
- H5 (high): A new executable is written to one of the three fake Apple support paths before first execution. Covered by Q5.
- H6 (high/medium FP): Gatekeeper's quarantine attribute is programmatically stripped via
xattrimmediately ahead of execution. Covered by Q6. - H7 (medium): A host process resolves public Nostr relay infrastructure — ROOFDECK's dead-drop C2 lookup mechanism. Covered by Q7.
- H8 (medium, investigate-only):
terraform initexecutes inside a workspace whose naming matches the interview/candidate-assessment lure pattern. Covered by Q8.
Attack Chain
| Step | Telemetry | Hunt Angle |
|---|---|---|
| 1. Social-engineering contact via fake recruiter/interviewer outreach (GitHub/LinkedIn) | None (pre-endpoint) | GitHub org & recruiting-pipeline awareness — no EDR angle |
2. Candidate clones a weaponized "technical assessment" repo containing a poisoned .terraform.lock.hcl | Process (git/IDE clone) | Native hunt: lock-file provenance review (§7) |
3. Candidate runs terraform init | ProcessRollup2 | Q8 |
| 4. Terraform resolves the custom provider registry (attacker domain) | DnsRequest | Q1 |
| 5. Malicious provider module downloaded and executed | ProcessRollup2 / NewExecutableWritten | Q2, Q5 |
6. FLATROOF strips Gatekeeper quarantine + chmod +x on ROOFDECK | ProcessRollup2 | Q6 |
7. Both implants launched via IDE integrated terminal with nohup … --type=renderer | ProcessRollup2 | Q2, Q3 |
8. LaunchAgent persistence installed (Chrome-renderer masquerade, RunAtLoad=true) | ProcessRollup2 (subsequent launchd-spawned runs) | Q3 |
| 9. FLATROOF beacons via Telegram; ROOFDECK resolves C2 via Nostr dead-drop, then beacons direct | DnsRequest / NetworkConnectIP4 | Q4, Q7 |
| 10. Recon, browser/keychain credential theft, system & process enumeration | Partial — no dedicated browser-data-theft event | GAP — native hunt (§7) + EDR file-read telemetry where licensed |
| 11. Lateral escalation into cloud environment (AWS/OVH/OpenStack) using stolen API keys | Outside Falcon endpoint telemetry | GAP — requires cloud-native audit logs (CloudTrail etc.); see Hardening §12 |
Consolidated IOC Table
| Type | Value | Confidence | Action | Context | Expiry |
|---|---|---|---|---|---|
| sha1 | 02df07a173ab03b82a4fb6a08973fff8b1467f28 | high | detect | FLATROOF backdoor binary (SystemUpdate) — 3-source corroborated | 2027-03-21 |
| sha1 | c491d477dbe0ae04e9aed9dbe237144c03f73ec4 | high | detect | ROOFDECK backdoor binary (iSync) — 3-source corroborated | 2027-03-21 |
| sha1 | 5728b11d30586bbfc1d8bd12df1c722a06e767a2 | high | detect | Stripped ROOFDECK binary (loginwindow) — 2-source corroborated | 2027-03-21 |
| domain | technicais.sytes.net | high | detect | FLATROOF C2 — 2-source corroborated | 2027-03-21 |
| domain | storage.hubpage.cloud | high | detect | ROOFDECK C2 — 2-source corroborated | 2027-03-21 |
| domain | grenight.com | high | detect | ROOFDECK stage-3 C2 — 2-source corroborated | 2027-03-21 |
| ipv4 | 176.97.114.232 | high | detect | FLATROOF C2 IP for technicais.sytes.net — 2-source corroborated | 2027-03-21 |
| ipv4 | 45.11.59.140 | medium | hunt | ROOFDECK C2 IP for storage.hubpage.cloud — single-sourced (SentinelOne only) | 2026-12-21 |
| ipv4 | 85.137.56.245 | medium | hunt | ROOFDECK C2 IP for grenight.com — single-sourced | 2026-12-21 |
| ipv4 | 85.137.56.10 | medium | hunt | ROOFDECK staging server — single-sourced | 2026-12-21 |
| domain | registry.hashicorp-aws.com | high | detect | Weaponized Terraform provider registry — 3-source corroborated | 2027-03-21 |
| domain | registry.hashicorp-aws.io | high | detect | Weaponized Terraform provider registry — 2-source corroborated | 2027-03-21 |
| domain | registry.hashicorp-terraform.io | high | detect | Weaponized Terraform provider registry — 2-source corroborated | 2027-03-21 |
| filename | SystemUpdate | medium | hunt | FLATROOF binary at ~/Library/com.apple.iTunesCloud/SystemUpdate — single-sourced path | 2026-12-21 |
| filename | iSync | medium | hunt | ROOFDECK binary at ~/Library/com.apple.internal.ck/iSync — single-sourced path | 2026-12-21 |
| filename | loginwindow | medium | hunt | Stripped ROOFDECK binary at ~/Library/com.apple.appleaccountd/loginwindow — single-sourced path | 2026-12-21 |
| sha256 | 4b2d3e8ccce8920a6d01e7d02b84236545a20e5f754b3eec253f8b416b731daa | low | enrich | mkcert dev-CA TLS certificate used by ROOFDECK C2 — single-sourced | 2026-10-21 |
| other | 1cd6d13ff15adbf7a42025d10ec99b4a | low | enrich | Serial number of the above TLS certificate — single-sourced | 2026-10-21 |
| domain (x9) | see §10 Behavioral Signatures block | medium | pivot | Public Nostr relay set used for ROOFDECK C2 dead-drop lookup — legitimate shared infrastructure, do not block; hunt via Q7 | 2026-10-21 |
| url (x6) | see §10 Named Tooling block | medium | pivot | Weaponized interview-lure GitHub repositories — single-sourced | 2026-12-21 |
| behavioral | LaunchAgent Chrome-renderer masquerade | high | hunt | ~/Library/LaunchAgents/*.plist, Label prefix com., ProgramArguments contains --type=renderer, RunAtLoad=true | 2027-03-21 |
| behavioral | Config/IPC/lock-file artifact set | medium | hunt | $HOME/.config/.repl_history, /private/tmp/.pipe-airway, $TMPDIR/tmp*.lock — single-sourced | 2026-12-21 |
Affected Surface & Telemetry Matrix
| Surface | Telemetry | Priority | Gap |
|---|---|---|---|
| macOS developer endpoints (Apple Silicon) | ProcessRollup2, SyntheticProcessRollup2, DnsRequest, NetworkConnectIP4, NewExecutableWritten | Critical | None — full Falcon macOS sensor coverage if Endpoint Security extension is enabled |
| GitHub organization / candidate-repository review | None (outside EDR) | High | GAP — requires GitHub audit log + branch-protection controls, see Hardening §12 |
| Terraform / IaC CI pipeline | Process/network telemetry only if the CI runner itself carries a Falcon sensor | High | Partial — ephemeral CI runners are frequently unmonitored by EDR |
| Cloud control-plane (AWS / OVH / OpenStack) post-compromise API use | CloudTrail / provider audit logs (outside Falcon scope) | Critical | GAP — requires cloud-native logging; not covered by this pack's CQL |
| Browser & Keychain data (FLATROOF collection target) | FileReadInfo (where licensed) | Medium | Partial — no dedicated "browser credential theft" event in the Falcon data model |
ATT&CK Mapping
| Tactic | Technique ID | Technique Name | Observed Behavior |
|---|---|---|---|
| Reconnaissance | T1593.003 | Search Open Websites/Domains: Code Repositories | Identifying GitHub profiles of DevOps/crypto-engineering job seekers to target |
| Resource Development | T1583.001 | Acquire Infrastructure: Domains | Registered typosquatted Terraform-registry and C2 domains |
| Resource Development | T1608.001 | Stage Capabilities: Upload Malware | Weaponized .terraform.lock.hcl staged in fake interview repositories |
| Initial Access | T1195.001 | Supply Chain Compromise: Compromise Software Dependencies and Development Tools | Malicious Terraform provider delivered via poisoned lock file (Q1) |
| Execution | T1204.002 | User Execution: Malicious File | Candidate runs terraform init against the lure repo (Q8) |
| Execution | T1059.004 | Command and Scripting Interpreter: Unix Shell | nohup-launched implants from the IDE integrated terminal |
| Defense Evasion | T1553.001 | Subvert Trust Controls: Gatekeeper Bypass | xattr -rd com.apple.quarantine + chmod +x (Q6) |
| Defense Evasion | T1036.005 | Masquerading: Match Legitimate Name or Location | Binaries dropped under fake com.apple.* support paths (Q2, Q5) |
| Defense Evasion | T1036.004 | Masquerading: Masquerade Task or Service | --type=renderer flag to mimic a Chrome renderer process (Q3) |
| Defense Evasion | T1027.005 | Obfuscated Files or Information: Indicator Removal from Tools | Stage-3 loginwindow binary stripped of symbols/debug info — GAP, no EDR query |
| Persistence | T1543.001 | Create or Modify System Process: Launch Agent | ~/Library/LaunchAgents/*.plist, RunAtLoad=true (Q3) |
| Command and Control | T1071.001 | Application Layer Protocol: Web Protocols | FLATROOF Telegram C2; ROOFDECK HTTPS /app_version tasking (Q4) |
| Command and Control | T1102.002 | Web Service: Bidirectional Communication (Dead Drop Resolver) | ROOFDECK Nostr relay profile lookup for C2 URL (Q7) |
| Command and Control | T1573 | Encrypted Channel | Custom mkcert TLS certificate for C2 transport |
| Credential Access | T1555.001 | Credentials from Password Stores: Keychain | login.keychain-db collection — GAP, no EDR query |
| Discovery | T1217 | Browser Information Discovery | Chrome/Brave/Firefox/Safari profile enumeration — GAP |
| Collection | T1119 | Automated Collection | System hardware/software profile, running-process snapshot, Terminal history |
| Lateral Movement / Impact | T1078.004 | Valid Accounts: Cloud Accounts | Stolen AWS/OVH/OpenStack API keys used for cloud escalation — GAP, requires cloud-native logs |
Native Audit-Log Hunts
These checks require no CQL and can be run directly on a suspect endpoint (via Falcon RTR) or against a repository / CI pipeline. They cover artifacts the CQL section either cannot reach (filesystem contents, repo diffs) or should be corroborated with before triage.
- ROOFDECK config file:
ls -la "$HOME/.config/.repl_history"— inspect contents for Nostr public keys / relay URLs / C2 hints. This path has no legitimate shell-history purpose. - ROOFDECK IPC pipe:
ls -la /private/tmp/.pipe-airway— should not exist on a clean host; a named pipe here is a strong artifact. - FLATROOF lock file:
ls $TMPDIR/tmp*.lock— review any match for owning process and creation time correlated against Q2/Q5 hits. - LaunchAgent renderer-masquerade sweep:
for f in ~/Library/LaunchAgents/com.*.plist; do plutil -p "$f" 2>/dev/null | grep -q -- '--type=renderer' && echo "SUSPECT: $f"; done - Gatekeeper/quarantine unified-log review:
log show --predicate 'eventMessage contains "quarantine"' --last 30d— cross-reference removals against Q6 process hits. - Fake Apple bundle-ID sweep:
log show --predicate 'process contains "SystemUpdate" or process contains "iSync" or process contains "loginwindow"' --last 30d - Terraform lock-file provenance review (pre-merge, in CI or local):
grep -R "hashicorp-aws\|hashicorp-terraform" **/.terraform.lock.hcl— any hit outsideregistry.terraform.iois a blocker, not a warning.
None of these seven checks require a Falcon macOS sensor and all seven are safe to run against a suspected-clean host as a baseline before rolling detections to prevent mode.
CrowdStrike LogScale CQL Hunt Queries
Looks for: A developer endpoint or CI runner resolving one of the three known weaponized Terraform provider-registry domains during a terraform init.
// HUNT: DNS resolution of attacker-controlled Terraform provider registries // MITRE: T1195.001 // CONF: high FP: low COST: low // REQUIRES: DnsRequest telemetry, any OS running Terraform // FALSE POSITIVES: none expected -- these domains are not legitimate Terraform registries and have no benign use // LOOKBACK: 30d #event_simpleName=DnsRequest | DomainName=/^registry\.hashicorp-aws\.(com|io)$/i or DomainName=/^registry\.hashicorp-terraform\.io$/i | table([@timestamp, aid, ComputerName, UserName, DomainName, ContextProcessId_decimal])
Looks for: SystemUpdate, iSync, or loginwindow executing from one of the three fabricated com.apple.* support directories.
// HUNT: Process execution from fake Apple framework-support directories (FLATROOF/ROOFDECK) // MITRE: T1036.005, T1564.001 // CONF: high FP: low COST: low // REQUIRES: ProcessRollup2, macOS // FALSE POSITIVES: none expected -- these are not genuine Apple framework-support paths // LOOKBACK: 30d #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(SystemUpdate|iSync|loginwindow)$/i | ImageFileName=*com.apple.iTunesCloud* or ImageFileName=*com.apple.internal.ck* or ImageFileName=*com.apple.appleaccountd* | table([@timestamp, aid, ComputerName, UserName, ImageFileName, ParentBaseFileName, CommandLine, SHA256HashData])
Looks for: A process carrying --type=renderer whose parent is launchd directly — real Chrome renderers are never launchd children.
// HUNT: launchd-spawned process masquerading as a Chrome renderer (LaunchAgent persistence trigger) // MITRE: T1543.001, T1036.004 // CONF: high FP: low COST: low // REQUIRES: ProcessRollup2, macOS // FALSE POSITIVES: none expected -- legitimate browser renderer processes are spawned by the browser's own // helper process, never directly by launchd // LOOKBACK: 14d #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^launchd$/i | CommandLine=*--type=renderer* | table([@timestamp, aid, ComputerName, UserName, ImageFileName, FileName, CommandLine])
Looks for: Outbound network/DNS activity to any of the seven confirmed C2 IPs/domains.
// HUNT: Network beacon to confirmed FLATROOF/ROOFDECK command-and-control infrastructure // MITRE: T1071.001, T1105 // CONF: high FP: low COST: low // REQUIRES: NetworkConnectIP4, DnsRequest, macOS // FALSE POSITIVES: none expected -- attacker-registered infrastructure with no legitimate use // NOTE: 45.11.59.140, 85.137.56.245 and 85.137.56.10 are single-sourced (SentinelOne only); treat hits on // those three as high-confidence but corroborate with Q2/Q5/Q6 before declaring compromise // LOOKBACK: 30d #event_simpleName=/NetworkConnectIP4|DnsRequest/ | RemoteAddressIP4=/^(176\.97\.114\.232|45\.11\.59\.140|85\.137\.56\.245|85\.137\.56\.10)$/ or DomainName=/^(technicais\.sytes\.net|storage\.hubpage\.cloud|grenight\.com)$/i | table([@timestamp, aid, ComputerName, UserName, RemoteAddressIP4, DomainName, RemotePort])
Looks for: A new Mach-O executable staged at one of the three fabricated com.apple.* paths before first execution.
// HUNT: New executable written to a fake Apple framework-support path // MITRE: T1036.005, T1105 // CONF: high FP: low COST: low // REQUIRES: NewExecutableWritten, macOS // NOTE: ⚠ validate in tenant -- confirm NewExecutableWritten coverage for Mach-O binaries on your sensor // version; some tenants surface this only via ProcessRollup2 first-execution telemetry (see Q2) // FALSE POSITIVES: none expected -- these are not legitimate Apple framework-support paths // LOOKBACK: 30d #event_simpleName=NewExecutableWritten | TargetFileName=*com.apple.iTunesCloud* or TargetFileName=*com.apple.internal.ck* or TargetFileName=*com.apple.appleaccountd* | table([@timestamp, aid, ComputerName, UserName, TargetFileName, SHA256HashData])
Looks for: xattr removing com.apple.quarantine immediately ahead of executing an unsigned binary.
FP: developer tooling and package managers occasionally strip quarantine on unsigned local builds — see the in-query // TUNING: line.
// HUNT: Gatekeeper quarantine attribute stripped via xattr ahead of execution // MITRE: T1553.001 // CONF: high FP: medium COST: low // REQUIRES: ProcessRollup2, macOS // FALSE POSITIVES: developer tooling and package managers occasionally strip quarantine on unsigned local builds // TUNING: exclude ParentBaseFileName values for approved build/package tooling (e.g. brew, xcodebuild, your CI // runner's helper process) once you have baselined 30 days of benign hits per Validation Gates (Section 11) // LOOKBACK: 30d #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^xattr$/i | CommandLine=*com.apple.quarantine* | table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, ParentProcessId_decimal, CommandLine])
Looks for: A host process resolving one of the nine known public Nostr relay / discovery domains ROOFDECK uses to locate its operator's C2 profile.
FP: legitimate Nostr client software exists and this traffic alone does not indicate compromise — see the in-query // TUNING: line.
// HUNT: Outbound resolution of public Nostr relay infrastructure (ROOFDECK C2 dead-drop lookup) // MITRE: T1102.002, T1568 // CONF: medium FP: medium COST: low // REQUIRES: DnsRequest // FALSE POSITIVES: legitimate Nostr desktop/web clients and crypto-curious end users running their own Nostr apps // TUNING: exclude ParentBaseFileName values for approved/known Nostr client applications in your environment; // treat a hit as a pivot into Q2/Q4/Q6 on the same aid, not as a standalone alert // LOOKBACK: 14d #event_simpleName=DnsRequest | DomainName=/^(relay\.damus\.io|nos\.lol|nostr\.mom|relay\.snort\.social|offchain\.pub|relay\.nostr\.band|nostr\.oxtr\.dev|nostr\.wine|api\.nostr\.watch)$/i | table([@timestamp, aid, ComputerName, UserName, DomainName, ContextProcessId_decimal])
Looks for: Broad pivot query for terraform init executions; intended to be triaged jointly with Q1, not alerted on alone.
FP: this fires on ordinary DevOps work — see the in-query // TUNING: line.
// HUNT: terraform init executed against a candidate/interview-styled workspace // MITRE: T1195.001, T1204.002 // CONF: medium FP: medium COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: legitimate DevOps engineers routinely run terraform init; on its own this is a broad pivot, // not an alert // TUNING: correlate with Q1 (malicious registry DNS) on the same aid within the same session before triage; // scope ImageFileName to known interview/candidate/assessment directory naming in your environment // LOOKBACK: 14d #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^terraform$/i | CommandLine=*init* | table([@timestamp, aid, ComputerName, UserName, ImageFileName, ParentBaseFileName, CommandLine])
CrowdStrike Custom IOA Recommendations
Custom IOA Candidates
Queries Q1, Q2, Q3, Q4, Q5, and Q6 are high-confidence, low-to-medium-FP behaviors with no legitimate business use on a compromised host and are recommended for promotion to Falcon Custom IOA rules once the Validation Gates in Section 11 are satisfied. Q7 and Q8 remain investigate-only (medium confidence, broader legitimate-use surface).
| IOA Name | Pattern | Exclusions | Deployment Path |
|---|---|---|---|
| Fake-Apple-Path Execution | ProcessRollup2 where FileName in {SystemUpdate, iSync, loginwindow} AND
ImageFileName under a fabricated com.apple.* support directory (Q2) | None identified | Build as Custom IOA in Monitor mode for 14 days, then Detect, then Prevent per Section 11 |
| Renderer-Masquerade Persistence | ProcessRollup2 where ParentBaseFileName=launchd AND
CommandLine contains --type=renderer (Q3) | None identified — real browser renderers are never launchd children | Custom IOA, Monitor → Prevent |
| Quarantine-Strip-Then-Exec Chain | xattr removing com.apple.quarantine (Q6) followed within a short window by execution of the same TargetFileName | Approved build/package tooling (brew, xcodebuild, CI
runner helper) — see Q6 // TUNING: | Custom IOA, Monitor → Detect (hold at Detect given medium FP until baseline is clean) |
| Confirmed C2 Beacon | NetworkConnectIP4/DnsRequest to any of the 7 confirmed FLATROOF/ROOFDECK indicators (Q4) | None identified | Custom IOC (not IOA) — import via the Falcon IOC Management CSV in Section 10, action per the two-source-corroboration policy in Section 4 |
Alert & Triage Quick-Reference
Shared triage checklist for any Q1–Q6 hit (alert subject pattern:
[CRITICAL] Detection: TraderTraitor -- <behavior> for Q1/Q2/Q3/Q4/Q5, [HIGH] for
Q6):
- Confirm the endpoint is macOS and record
aid/ComputerName/UserNamefrom the hit. - Pull the live process tree via Falcon RTR (
ps aux,launchctl list) for the flagged PID/plist entry. - Run Q2, Q5, and Q6 scoped to the same
aidto establish full compromise scope (see Pivot A below). - Hash any binary found at the three fake Apple support paths and compare against the Section 4 IOC table.
- Enumerate
~/Library/LaunchAgents/*.plistfor--type=renderermasquerade entries — preserve, do not delete, for forensics. - If confirmed, network-contain the host via Falcon RTR and proceed to the Containment Runbook (Section 14).
- Rotate any AWS/OVH/OpenStack/GitHub credentials reachable from the endpoint.
- Escalate to IR lead with the Hunt Summary Ticket (Section 16) populated.
Pivot Queries
Pivot A — host-scoped timeline
// PIVOT (host-scoped): full process/network/DNS timeline for one endpoint after a Q1-Q8 hit // MITRE: n/a -- investigation pivot // CONF: n/a FP: n/a COST: medium // REQUIRES: ProcessRollup2, DnsRequest, NetworkConnectIP4 // LOOKBACK: 30d -- narrow to the incident window once the timeline is confirmed // NOTE: replace REPLACE_WITH_AID with the aid value from the triggering hit before running #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2|DnsRequest|NetworkConnectIP4/ | aid=REPLACE_WITH_AID | table([@timestamp, #event_simpleName, ImageFileName, ParentBaseFileName, CommandLine, DomainName, RemoteAddressIP4]) | sort(@timestamp, order=asc)
Pivot B — environment-wide blast radius
// PIVOT (environment-wide): blast-radius count across all Q1/Q2/Q4/Q6 indicators, grouped by host // MITRE: n/a -- investigation pivot // CONF: n/a FP: n/a COST: medium // REQUIRES: ProcessRollup2, DnsRequest, NetworkConnectIP4 // LOOKBACK: 30d #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2|DnsRequest|NetworkConnectIP4/ | (FileName=/^(SystemUpdate|iSync|loginwindow|xattr)$/i) or (RemoteAddressIP4=/^(176\.97\.114\.232|45\.11\.59\.140|85\.137\.56\.245|85\.137\.56\.10)$/) or (DomainName=/^(registry\.hashicorp-aws\.(com|io)|registry\.hashicorp-terraform\.io|technicais\.sytes\.net|storage\.hubpage\.cloud|grenight\.com)$/i) | groupBy([ComputerName, aid], function=count(as=Hits), limit=1000) | sort(Hits, order=desc, limit=200)
Machine-Readable IOC Appendix
type,value,action,severity,expiration,description,tags sha1,02df07a173ab03b82a4fb6a08973fff8b1467f28,detect,critical,2027-03-21,FLATROOF backdoor binary (SystemUpdate) -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor sha1,c491d477dbe0ae04e9aed9dbe237144c03f73ec4,detect,critical,2027-03-21,ROOFDECK backdoor binary (iSync) -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor sha1,5728b11d30586bbfc1d8bd12df1c722a06e767a2,detect,critical,2027-03-21,Stripped ROOFDECK binary (loginwindow) -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor domain,technicais.sytes.net,detect,critical,2027-03-21,FLATROOF C2 domain -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor domain,storage.hubpage.cloud,detect,critical,2027-03-21,ROOFDECK C2 domain -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor domain,grenight.com,detect,critical,2027-03-21,ROOFDECK stage-3 C2 domain -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor ipv4,176.97.114.232,detect,critical,2027-03-21,FLATROOF C2 IP for technicais.sytes.net -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor domain,registry.hashicorp-aws.com,detect,critical,2027-03-21,Weaponized Terraform provider registry -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor domain,registry.hashicorp-aws.io,detect,critical,2027-03-21,Weaponized Terraform provider registry -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor domain,registry.hashicorp-terraform.io,detect,critical,2027-03-21,Weaponized Terraform provider registry -- SentinelLABS primary + derivative coverage,campaign:TraderTraitor ipv4,45.11.59.140,detect,high,2026-12-21,ROOFDECK C2 IP -- single-sourced (SentinelOne only) -- see two-source policy in Section 4,campaign:TraderTraitor ipv4,85.137.56.245,detect,high,2026-12-21,ROOFDECK C2 IP -- single-sourced -- see two-source policy in Section 4,campaign:TraderTraitor ipv4,85.137.56.10,detect,high,2026-12-21,ROOFDECK staging server -- single-sourced -- see two-source policy in Section 4,campaign:TraderTraitor
Hash type is sha1 (only SHA1
values were published for these binaries) -- confirm your tenant's IOC Management accepts sha1 before bulk
import, or pivot to Q2/Q5 for hash-agnostic behavioral coverage.
Every row ships at
detect, never prevent, and this CSV matches §4 exactly. All four cited
sources trace back to the single SentinelLABS report of 2026-09-18: The Hacker News, GBHackers and CyberPress
are derivative coverage of that same analysis, not independent observation. "Corroborated" in §4 therefore
means "the value is reproduced consistently across outlets", which is a transcription check, not independent
confirmation. This pack's bar for a prevent-tier action is two genuinely independent sources, so
nothing here meets it. Promote to prevent only after your own tenant telemetry or a second
first-party report confirms the indicator.
# FLATROOF / ROOFDECK behavioral signatures (macOS, ARM64, Rust)
Gatekeeper bypass chain : xattr -rd com.apple.quarantine <path> && chmod +x <path>
Renderer masquerade : launchd-spawned process, CommandLine contains --type=renderer, ParentBaseFileName=launchd
Fake Apple support paths: ~/Library/com.apple.iTunesCloud/SystemUpdate
~/Library/com.apple.internal.ck/iSync
~/Library/com.apple.appleaccountd/loginwindow
LaunchAgent persistence : ~/Library/LaunchAgents/*.plist (Label prefix "com.", ProgramArguments contains
--type=renderer, RunAtLoad=true)
ROOFDECK config file : $HOME/.config/.repl_history
ROOFDECK IPC pipe : /private/tmp/.pipe-airway
FLATROOF lock file : $TMPDIR/tmp*.lock
Example lure workspace : ~/DevOps-Automation/cloudshield (name varies per victim -- illustrative, not durable)
Nostr relay set used for ROOFDECK C2 dead-drop lookup (do NOT block outright -- legitimate shared public
infrastructure; hunt via Q7 for anomalous ParentBaseFileName instead):
relay.damus.io, nos.lol, nostr.mom, relay.snort.social, offchain.pub,
relay.nostr.band, nostr.oxtr.dev, nostr.wine, api.nostr.watch
Malware family : FLATROOF (aka macOS.Gaslight) -- Rust, ARM64 macOS backdoor, Telegram C2 Malware family : ROOFDECK -- Rust, ARM64 macOS backdoor, Nostr decentralized C2 resolution Actor : TraderTraitor (DPRK Lazarus subgroup) aka UNC4899, PUKCHONG, Jade Sleet, Slow Pisces Lure repositories (single-sourced -- pivot/investigation use only, not blocklist material): github.com/exubient0/terraform-candidate-repo github.com/radupopa369/gtn-candidate-repo github.com/chainstacker/Northwind-IAC github.com/RyanLRay/Technical-Assessments github.com/Steed-LHV/assessment github.com/derrix060/novacart_interview Code-signing certificate (single-sourced, enrich only): mkcert development CA, serial 1cd6d13ff15adbf7a42025d10ec99b4a cert SHA-256 4b2d3e8ccce8920a6d01e7d02b84236545a20e5f754b3eec253f8b416b731daa
# Developer-endpoint / repo audit checklist [ ] terraform -version && terraform providers -- confirm no custom registry mirrors outside registry.terraform.io [ ] grep -R "hashicorp-aws\|hashicorp-terraform" **/.terraform.lock.hcl -- flag any hit [ ] spctl --status -- Gatekeeper must report "assessments enabled" [ ] ls -la ~/Library/LaunchAgents/ | grep -i com. -- review for --type=renderer masquerade entries [ ] falconctl stats -- confirm macOS sensor + Endpoint Security extension active [ ] aws iam generate-credential-report -- rotate long-lived static keys on developer/candidate-facing accounts [ ] gh api repos/:owner/:repo/branches/:branch/protection -- confirm required review on .terraform.lock.hcl changes
Detection Validation Gates
| Gate | Requirement | Status |
|---|---|---|
| Telemetry ready | Falcon macOS sensor deployed with Endpoint Security extension and Full Disk Access approved via MDM on the DevOps/engineering cohort | Verify before deploy |
| Benign baseline | Run Q1, Q6, and Q7 in Monitor/Investigate mode for 30 days against the DevOps fleet to characterize legitimate Terraform, package-manager, and Nostr-client usage | Required before Prevent |
| Positive tests | Validate Q2/Q3/Q5 field logic against a non-malicious harness that writes to and
executes from a decoy com.apple.test/ path with a --type=renderer flag — do not
detonate real FLATROOF/ROOFDECK samples | Required before Prevent |
| Promotion criteria | Zero false positives across the 30-day baseline before flipping Q1, Q2, Q3, Q4, or Q5 from Custom IOA Monitor to Prevent; Q6 holds at Detect given medium FP; Q7/Q8 remain investigate-only | Gate before Prevent |
These four gates fold forward into the Detection Coverage Map (Section 15).
Hardening — Tiered
Terraform provider allowlisting. Pin required_providers source + checksum in
every repo; block any provider source outside registry.terraform.io or your internal mirror.
MITRE M1038 (Execution Prevention). No formal CIS benchmark covers Terraform specifically; HashiCorp's own
provider-installation documentation is the authoritative source — flagged
⚠ best-practice, no formal benchmark. Verify: grep -R "source" **/.terraform.lock.hcl | grep -v registry.terraform.io returns nothing.
Enforce Gatekeeper on all developer macOS endpoints. MITRE M1038; verify via
spctl --status (must report "assessments enabled") and MDM configuration profile locking
SystemPolicy settings so a local admin cannot disable it.
GitHub branch protection on assessment/candidate repositories. Require signed commits and
mandatory review for any change to .terraform.lock.hcl or providers.tf. MITRE M1047
(Audit); verify via gh api repos/:owner/:repo/branches/:branch/protection.
Fleet-wide Falcon macOS sensor with Endpoint Security extension for the DevOps/engineering
cohort specifically (this and the prior LayerZero/KelpDAO victim were both targeted via a single DevOps
engineer's laptop). MITRE M1047; verify via falconctl stats.
Short-lived cloud credentials. Replace static AWS/OVH/OpenStack keys on developer endpoints
with STS AssumeRole sessions requiring MFA and capped session duration. MITRE M1026 (Privileged
Account Management); verify via aws iam generate-credential-report — zero long-lived keys on
developer-tagged principals.
Egress filtering for decentralized dead-drop protocols. Block or alert on outbound to the public Nostr relay set (Section 10) from developer endpoints unless a documented business need exists. MITRE M1037 (Filter Network Traffic); verify via firewall/EDR network-rule audit.
Sandboxed "clean room" for take-home / technical-assessment code. Ephemeral VM or container with no mounted cloud credentials and no access to production source control for any code received from an unverified recruiter or interviewer. MITRE M1048 (Application Isolation and Sandboxing).
IaC supply-chain scanning in CI. Gate merges on tfsec/checkov
(or equivalent) provider-source review before any Terraform plan/apply runs. MITRE M1051 (Update Software) /
M1038.
DPRK Contagious-Interview awareness training for the engineering hiring pipeline, tied to CISA/FBI joint advisories on fake recruiter/interview lures. MITRE M1017 (User Training).
Deployable Playbooks
Playbook 1 — Terraform provider registry allowlist
# Playbook 1 -- Terraform provider registry allowlist (local + CI)
# Prerequisites: Terraform >=1.7 CLI; write access to the repo's versions.tf / .terraformrc
# Reboot required: no
# Rollback: remove the provider_installation block from .terraformrc / CLI config; re-run `terraform init` to
# restore default registry resolution (no state is modified by this control)
cat >> ~/.terraformrc <<'EOF'
provider_installation {
network_mirror {
url = "https://registry.terraform.io/"
include = ["*/*"]
}
direct {
exclude = ["registry.hashicorp-aws.com/*", "registry.hashicorp-aws.io/*", "registry.hashicorp-terraform.io/*"]
}
}
EOF
# CI gate (example, add to your pipeline before `terraform init`):
grep -R "registry.hashicorp-aws\|registry.hashicorp-terraform" **/.terraform.lock.hcl && exit 1 || true
Playbook 2 — Enforce Gatekeeper via MDM
# Playbook 2 -- Enforce Gatekeeper via MDM configuration profile (macOS) # Prerequisites: MDM enrollment (Jamf/Kandji/Mosyle/etc.) with custom payload push capability # Reboot required: no (profile applies on install; a logout is recommended but not required) # Rollback: remove the configuration profile via MDM; spctl --master-enable restores interactive Gatekeeper # control to the local admin if the profile is withdrawn # Verify current state before pushing the profile: spctl --status # expected: "assessments enabled" # MDM custom payload (com.apple.systempolicy.control), key fields: # AllowIdentifiedDevelopers : false # EnableAssessment : true # Push via your MDM's custom payload / profile mechanism -- syntax is MDM-vendor specific. # Post-deploy verification on the endpoint: spctl --status && echo "Gatekeeper: OK" || echo "Gatekeeper: NEEDS REMEDIATION"
Playbook 3 — GitHub branch protection for lock-file changes
# Playbook 3 -- GitHub branch protection for Terraform lock-file changes # Prerequisites: gh CLI authenticated with repo admin scope # Reboot required: no # Rollback: gh api -X DELETE repos/:owner/:repo/branches/:branch/protection gh api -X PUT repos/:owner/:repo/branches/main/protection \ -F required_pull_request_reviews.required_approving_review_count=1 \ -F required_status_checks.strict=true \ -F enforce_admins=true \ -F required_linear_history=true # Confirm: gh api repos/:owner/:repo/branches/main/protection
Playbook 4 — Rotate static cloud credentials
# Playbook 4 -- Rotate static cloud credentials for a compromised/at-risk developer principal # Prerequisites: IAM admin access; inventory of the affected principal's access keys # Reboot required: no # Rollback: none required -- credential rotation is one-directional by design; if the NEW key breaks an # automation, issue a fresh key rather than reinstating the rotated-out one aws iam list-access-keys --user-name REPLACE_WITH_USERNAME aws iam update-access-key --access-key-id REPLACE_WITH_KEY_ID --status Inactive --user-name REPLACE_WITH_USERNAME aws iam create-access-key --user-name REPLACE_WITH_USERNAME # Validate the new key works, then after a burn-in window: aws iam delete-access-key --access-key-id REPLACE_WITH_KEY_ID --user-name REPLACE_WITH_USERNAME
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Falcon RTR network-contain the endpoint; disable/rotate any GitHub session tokens the account holds; revoke AWS/OVH/OpenStack credentials reachable from the host | IR / SOC on-call | RTR containment ticket, IAM revocation log |
| Eradicate | Kill and remove SystemUpdate/iSync/loginwindow from the three fake Apple paths; remove the masquerading
LaunchAgent plist; delete .repl_history, .pipe-airway, and $TMPDIR/tmp*.lock
artifacts; re-image if persistence depth or scope is uncertain |
Endpoint engineering | RTR command output, before/after file listing |
| Recover | Rotate every credential/API key/SSH key resident on or reachable from the host; force re-authentication of all cloud sessions; audit CI/CD pipeline runs triggered from the host for unauthorized changes; validate a clean baseline before returning the endpoint to service | Cloud/Platform team + IR | Credential-rotation log, CI audit report, clean-scan attestation |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1195.001 | Weaponized Terraform lock file / malicious provider registry | Q1, Q8 | N (IOC-based) | Good |
| T1204.002 | User executes terraform init in a lure workspace | Q8 | N (investigate-only) | Partial |
| T1036.005 | Backdoor masquerades under fake Apple support path | Q2, Q5 | Y (candidate) | Good |
| T1036.004 | Renderer-flag masquerade on a launchd-spawned process | Q3 | Y (candidate) | Good |
| T1553.001 | Gatekeeper quarantine attribute stripped via xattr | Q6 | Y (candidate, holds at Detect) | Good |
| T1543.001 | LaunchAgent persistence | Q3 | Y (candidate) | Good |
| T1071.001 / T1105 | C2 beacon to confirmed FLATROOF/ROOFDECK infrastructure | Q4 | N (IOC-based) | Good |
| T1102.002 | Nostr relay dead-drop C2 resolution | Q7 | N (investigate-only) | Partial |
| T1027.005 | Symbol/debug stripping on the stage-3 loginwindow binary | — | — | GAP — not observable via process/network telemetry |
| T1555.001 | Keychain credential theft (login.keychain-db) | — | — | GAP — no dedicated Falcon event for keychain-file access |
| T1217 / T1119 | Browser data & system-profile collection | — | — | GAP — no dedicated browser-data-theft event in the Falcon model |
| T1078.004 | Stolen cloud API keys used for lateral escalation | — | — | GAP — requires cloud-native audit logs (CloudTrail/OVH/OpenStack), outside this pack's CQL scope |
Validation gates (Section 11) apply to every row marked as an IOA candidate above: none of them move to Prevent until the 30-day benign baseline and positive-test gates are satisfied.
Hunt Summary Ticket
TITLE: TraderTraitor -- FLATROOF/ROOFDECK macOS backdoors via weaponized Terraform lock files
SEVERITY: CRITICAL (nation-state, active as of 2026-09-18 disclosure)
SCOPE: macOS developer endpoints (Apple Silicon) running Terraform against fake job-interview repos
HYPOTHESIS: A developer cloned a fake technical-assessment repo, ran `terraform init` against a weaponized
.terraform.lock.hcl, and the resulting malicious provider dropped FLATROOF and/or ROOFDECK with
LaunchAgent persistence and Telegram/Nostr C2.
QUERIES: Q1-Q8 (Section 8); Custom IOA candidates Q1-Q6 (Section 9)
DO FIRST: Run Q1 (malicious registry DNS) and Q4 (confirmed C2 beacon) across the full macOS fleet --
these are the two lowest-noise, IOC-grounded starting points.
FINDINGS: REPLACE_WITH_HUNT_FINDINGS
GAPS: Keychain/browser credential theft, binary stripping, and cloud-key lateral movement have no
Falcon-native query coverage (Section 15) -- corroborate via native audit hunts (Section 7) and
cloud-native logs.
ACTIONS: See Containment Runbook (Section 14) and Hardening Playbooks 1-4 (Section 13).
OWNER: REPLACE_WITH_OWNER
VERSION: v0.1 -- 2026-09-21Changelog
References
| Tier | Source | Used For | Access Date |
|---|---|---|---|
| 1 | SentinelLABS -- "Don't Call Us, We'll Call Your APIs" (Priego, Delamotte, Havranek; 2026-09-18) | Primary technical report, full IOC set, execution chain, Nostr mechanics | 2026-09-21 |
| 1 | The Hacker News -- "Jade Sleet Linked to Indian IT Provider Breach" (2026-09-21) | Actor-alias and capability corroboration | 2026-09-21 |
| 2 | GBHackers -- "Hackers Weaponize Terraform Lock Files" (2026-09-21) | IOC corroboration (3 hashes, 3 domains, 1 IP) + hardening guidance | 2026-09-21 |
| 2 | CyberPress -- "ROOFDECK Backdoor Uses Nostr Profiles" (2026-09-21) | Independent Nostr-mechanism confirmation | 2026-09-21 |
| Ref | MITRE ATT&CK | Technique IDs (Section 6) | 2026-09-21 |
Generated 2026-09-21 · HuntPack v0.1 · TraderTraitor / FLATROOF & ROOFDECK