Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

A Fake AWS Terraform Provider Was Smuggling Malware Inside .woff Font Files

Zscaler's ThreatLabz published a campaign on October 8, 2026 built around a Terraform provider named terraform-provider-awsbeta_v1.0.0 — a Go binary that looks and behaves like a real AWS provider, complete with a workin

Zscaler's ThreatLabz published a campaign on October 8, 2026 built around a Terraform provider named terraform-provider-awsbeta_v1.0.0 — a Go binary that looks and behaves like a real AWS provider, complete with a working provider scaffold, while a malicious package inside it runs the moment Terraform loads the plugin. The malware itself, once unpacked, isn't especially novel as backdoors go. Where it hid before that point is the part worth studying.

Where it actually hid

The payloads were disguised as .woff web font files — HiraginoSans-Bold.woff, NotoSansCJK-Regular.woff, MalgunGothic-Italic.woff, named and extensioned to look exactly like what a normal build pipeline downloads without a second glance. Each file contains real decoy font data, followed by a marker (@@ENDFONT@@) and an AES-256-CBC-encrypted executable. The loader strips the decoy content, decrypts the payload, and runs it using whichever of Python, Node.js, Perl, or OpenSSL happens to already be installed on the victim's machine — no need to bring its own interpreter when developer workstations already have four to choose from.

Delivery used a dynamic DNS host, a GitHub repository, and a Vercel-hosted site, in that fallback order. None of those platforms were compromised; the attackers just used ordinary accounts on infrastructure that blends in. A request for a .woff file from a GitHub raw URL or a Vercel subdomain is exactly the kind of traffic a CI pipeline generates all day, which is precisely why it was chosen.

What actually runs once it's unpacked

The primary payload is FLATROOF, a Rust-based cross-platform backdoor with platform-specific persistence — a Linux service, a macOS shell logout hook, or a Windows registry Run key, depending on where it landed. Its Python-based stealer components go after Chrome, Edge, Brave, and Firefox credential and cookie databases, shell and command history, running processes, and — on Windows specifically — Windows Credential Manager entries and browser-extension data for MetaMask, Phantom, Trust Wallet, and Rabby. On macOS it collects Safari history and the login.keychain-db file directly.

A second backdoor, ROOFDECK, handles command-and-control discovery in a way built to survive takedowns: it reads an encrypted server address from a Pastebin paste, and if that lookup fails, falls back to querying Nostr profile metadata for the current address instead. Killing one channel doesn't kill the backdoor — it just makes it check the other one.

Why Terraform specifically is a good place to hide this

A Terraform provider executes as a plugin directly on whatever machine runs terraform init, plan, or apply — a developer's laptop, or more consequentially, a CI/CD runner. Both routinely hold cloud credentials, API tokens, git repository access, and infrastructure deployment permissions that go well beyond what a typical employee endpoint carries. A malicious provider doesn't need a separate privilege-escalation step. It simply inherits whatever the person or pipeline invoking it already has standing access to — which, for a DevOps engineer's machine, is usually quite a lot.

This is close to the same shape as the npm malware campaign that rode in on a routine dependency bump: a trusted package manager executing attacker code automatically, during a step nobody treats as a security decision, inside an environment that already has more access than the task in front of it needs.

What I'd actually check

Pin provider sources and versions explicitly, and verify checksums rather than trusting whatever terraform init happens to resolve from the registry that day:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "= 5.XX.X"  # pin the exact version, don't float on a range
    }
  }
}

Run Terraform commands inside something disposable, not on a daily-driver laptop that's also logged into a personal browser profile and a crypto wallet extension. The stealer components in this specific campaign go after exactly those two things, and a machine that's never held either has nothing in it worth exfiltrating.

Treat CI/CD runners with the same suspicion as developer laptops, not less — short-lived credentials, least-privilege IAM roles scoped to what that specific pipeline actually needs, and runners that don't carry state or secrets forward between unrelated jobs. A privileged pipeline running an unverified provider is a worse outcome than a single compromised laptop, not a better one.

The rule

A plugin that executes automatically the moment a trusted tool loads it is a privilege-escalation vector by default — whether it's an npm postinstall script, a VS Code extension, or a Terraform provider. The defense doesn't change across ecosystems: assume the code runs, and control what's reachable from wherever it runs before that happens, not after you find out it already did.

I'm Rohit, founder of Krova Cloud — disposable VMs with no browser sessions, no saved wallets, and nothing of standing value sitting around for exactly this class of malware to go looking for. More debugging stories at debugly.dev.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.