General
12 min readApril 15, 2026

What the AppsFlyer Supply-Chain Attack Means for Apps Using Shared CDN Attribution

On March 10, 2026, the mobile marketing industry woke up to a nightmare.

For 48 hours, one of the most trusted scripts on the internet — the AppsFlyer Web SDK, embedded in over 100,000 applications — was silently serving obfuscated JavaScript designed to intercept and swap cryptocurrency wallet addresses.

This wasn't a leaked password. It wasn't a misconfigured dashboard. It was a domain registrar compromise that bypassed every traditional perimeter defense: WAFs, firewalls, endpoint agents, allowlists. All of them useless, because the attack came from inside the house.

We started building Grovs partly because we saw a version of this coming. Back when Firebase Dynamic Links shut down and everyone scrambled to migrate to the same handful of providers, the concentration risk was obvious. One SDK, shared across tens of thousands of apps, served from one CDN. A single point of failure that the entire industry agreed to ignore because it was convenient.

We've spent the last month pulling apart the March attack vector, reading the independent payload analyses, and asking a question that nobody in the MMP space seems to want to answer: why does this keep happening?

Here's what we found.


The anatomy of a "trusted" hijack

The attack was elegant in its simplicity.

Sometime around March 9, 22:45 UTC, attackers gained access to the domain registrar controlling websdk.appsflyer.com. They redirected the domain to serve a modified version of the AppsFlyer Web SDK from a malicious CDN. Because most Content Security Policies allow *.appsflyer.com, browsers saw the poisoned script as trusted. No warnings. No blocks. Just execution.

The payload did two things at once.

First, it continued doing what the real SDK does: tracking clicks, installs, page views. Normal analytics behavior. This was deliberate. If the SDK suddenly stopped working, someone would have noticed immediately.

Second, it monitored DOM inputs for patterns matching cryptocurrency wallet addresses — Bitcoin, Ethereum, Solana, Ripple, and TRON. When a user pasted or typed a wallet address, the script swapped the destination with an attacker-controlled address. In the background, it also exfiltrated metadata: page URLs, timestamps, user agents, all sent via covert HTTP POST requests to attacker infrastructure.

The malicious code was heavily obfuscated. Runtime string decoding, anti-analysis techniques, modular design that could be updated on the fly. It was built by people who knew what they were doing.

Feroot Security disclosed the compromise on March 11. Profero Security independently confirmed the payload. AppsFlyer acknowledged a "domain registrar incident" that "temporarily exposed the Web SDK to unauthorized code." The ShinyHunters group has been linked to prior AppsFlyer-related breaches, including the Match Group breach earlier in 2026.

No confirmed crypto theft has been publicly reported as of this writing. But 48 hours of silent wallet-swapping across 100,000+ websites is not the kind of thing where you find out the damage quickly.


Why traditional security didn't help

Here's the part that should keep you up at night.

Every security tool your team relies on was blind to this attack. Not because the tools are bad, but because the attack was designed to be invisible to tools that operate on the assumption that your vendors are trustworthy.

WAFs and firewalls don't inspect JavaScript served by allowlisted domains. AppsFlyer was on the allowlist. Of course it was — it had been serving the SDK for years.

Subresource Integrity (SRI) is supposed to catch exactly this kind of tampering. A hash of the expected script, checked by the browser before execution. But SRI only works if the script doesn't change. AppsFlyer, like most MMPs, serves auto-updating scripts. The whole point of their delivery model is that the SDK updates without you doing anything. That means no static hash. That means no SRI protection.

Endpoint agents saw the script load from a domain that was already categorized as "analytics." Nothing suspicious there.

Behavioral detection should have caught the exfiltration, but the malicious code was careful. It hooked into existing network request patterns. The extra data looked like normal SDK telemetry to anything monitoring outbound traffic at a surface level.

As Reflectiz noted in their analysis: the attack originated from a trusted source already allowlisted by organizations. Standard defenses had no baseline to compare against, because the compromised code was new and obfuscated.

One compromised script provider. Thousands of websites. 48 hours.


The structural problem nobody wants to talk about

The security community treated this as an incident. Fix the registrar access, rotate the keys, publish a post-mortem, move on. That misses the point entirely.

The AppsFlyer attack wasn't an anomaly. It was a predictable consequence of how the entire attribution industry is architected.

Here's what we mean. Legacy MMPs — AppsFlyer, Branch, Adjust, Singular, Kochava — are built on shared infrastructure. One SDK, one CDN, thousands of apps. That's the business model. Economies of scale. You load their script, it phones home to their servers, and they track attribution across their entire network.

This architecture is the reason probabilistic attribution works at all. Cross-app data, fingerprinting, shared device graphs — all of it depends on a centralized system that sees traffic from many apps simultaneously.

But centralization is a double-edged sword. The same shared CDN that enables cross-app tracking is a single point of failure for every app that uses it. Compromise one domain, and you compromise them all. Not theoretically. Actually. That's what happened on March 9.

Think about it this way: if every company in the world used the same lock on their front door, and someone duplicated the key, how many break-ins would you expect?

The alternative is first-party architecture. Isolated infrastructure per customer. Your attribution data flows from your app to your dashboard — not through a shared CDN that serves 100,000 other apps. If one customer's infrastructure gets hit, nobody else is affected. The blast radius is one, not one hundred thousand.

Here's how these two models compare in practice:

Shared CDN (Legacy MMPs)

First-Party (Isolated)

Blast radius

100,000+ apps

1 app

Update model

Auto-update (no SRI possible)

Versioned releases (SRI enforced)

DNS control

Standard registrar lock

Registry-level lock

Code auditability

Closed source, obfuscated

Open source, every commit public

Data path

App → Vendor CDN → Vendor servers

App → Your dashboard

This isn't a new idea. It's how most security-conscious infrastructure works. We just forgot to apply it to attribution.


Technical deep-dive: how the attack chain worked

Skip this section if you're not technical. The takeaway above is the important part.

The registrar vector

Domain registrar compromise is not new, but it's unusually effective against CDN-served scripts. Here's the chain:

Attacker compromises domain registrar account
  (likely via phishing or session hijacking)
    ↓
Attacker changes nameservers for websdk.appsflyer.com
    ↓
DNS propagates — CDN now serves attacker's payload
    ↓
Every site loading the SDK gets the malicious version
    ↓
Browsers trust it (CSP allows *.appsflyer.com)
    ↓
Payload executes with full page access

The critical insight: a registrar compromise gives you control over what a CDN endpoint serves. You don't need to break into the CDN itself. You don't need to compromise the build pipeline. You just need the registrar credentials, and DNS does the rest. Within minutes, the entire world downloads the attacker's version.

The payload mechanics

The injected code established hooks into DOM events and network requests. Specifically:

  • Input monitoring: Regex matching against known wallet address formats for BTC, ETH, SOL, XRP, and TRON. The patterns were broad enough to catch standard address lengths and prefixes.

  • Address replacement: When a match was found, the script dynamically swapped the value in the input field. The replacement happened on a slight delay to avoid triggering change-detection in frameworks like React.

  • Clipboard interception: Secondary attack vector monitoring copy-paste operations for wallet addresses.

  • Exfiltration: Original wallet addresses and page metadata sent via covert POST requests, likely for later targeted phishing or to track successful swaps.

  • Self-preservation: The code maintained all legitimate SDK functionality. Analytics calls continued normally. Event tracking kept working. To an observer watching network traffic, nothing looked wrong unless you deobfuscated the payload.

Why SRI couldn't save you (the honest version)

There's a tension here that most security advice glosses over: you cannot have a script that is both auto-updating and SRI-protected.

Legacy MMPs serve auto-updating scripts like sdk.appsflyer.com/latest.js. They want the ability to push updates without requiring their customers to change anything. That's convenient for them, and it means faster bug fixes and feature rollouts. But it also means the hash changes every time they push an update. If you pinned an SRI hash, your site would break the next time they deployed.

The fix is versioned, immutable releases. A URL like cdn.example.com/v1.2.3/sdk.min.js that never changes. You generate the hash once, pin it in your HTML, and the browser will kill the script if a single byte is different. You update when you're ready, after reviewing the changelog and auditing the diff.

The trade-off is real: you give up automatic updates. You have to manage version bumps yourself. But the alternative is trusting that your vendor's infrastructure will never be compromised. After March 9, that's a hard argument to make.


A 3-step audit for your marketing stack

Regardless of which attribution provider you use, your engineering team should do this today.

1. Audit your Content Security Policy

Stop using wildcard directives like *.vendor.com. Pin specific subdomains. Better yet, use sha256- hashes for any third-party script that supports versioned releases. If your CSP currently looks like script-src *.appsflyer.com, you're trusting every subdomain that registrar controls — and that registrar is the exact thing that got compromised.

2. Monitor outbound drift

Set up alerts for when a trusted SDK starts making network calls to domains you haven't seen before. This was the smoking gun on March 10 — the poisoned SDK was POSTing to attacker infrastructure that had never appeared in the script's network behavior before. Tools like Reflectiz, Feroot, or even custom Content Security Policy reporting can catch this.

3. Demand SRI support from your vendor

If your MMP doesn't offer versioned, hashable releases of their Web SDK, ask them why. If their answer is "we need to push updates automatically," understand what you're agreeing to: you're giving them (and anyone who compromises their infrastructure) the ability to run arbitrary code on your users' browsers, with no verification step.


What we did differently at Grovs

The decisions below aren't theoretical. They're the architectural choices we made specifically because we assumed the large providers would eventually be targeted. March 9 proved us right sooner than we expected.

Versioned, hashable SDK releases

We don't serve auto-updating scripts. Our Web SDK is open-source on GitHub — you can inspect our release workflow and see exactly how we version and publish each build. Every release is immutable. You can read every line, build it from source, generate your own SRI hash, and host it on your own infrastructure if you want true zero-trust.

You pick a version, pin the hash, and your site is locked. You update on your schedule, after you've reviewed the diff.

Yes, this means you manage version bumps yourself.

To make that painless, we publish changelogs with every release and provide a CLI check that flags new versions with the updated SRI hash, so you can drop it into your CI/CD pipeline without manually computing anything. One command, one hash, done.

Registry-level domain locks

A registrar compromise — the exact attack vector used against AppsFlyer — usually works because someone phishes a login or hijacks a session, then changes nameservers instantly.

We use registry-level locking on our core infrastructure. This sets our domain status to clientUpdateProhibited and clientTransferProhibited at the actual registry. Even if someone stole a laptop and bypassed 2FA, they could not redirect our DNS. Any change requires manual, out-of-band verification — a phone call to a pre-verified individual, a passphrase that isn't stored online.

This makes us slower. Changing our DNS takes hours and a phone call instead of seconds and a click. We're fine with that trade-off.

Open source as a security audit trail

Most MMPs are black boxes. When AppsFlyer's SDK changed on March 9, nobody on the outside could tell what was added until security researchers reverse-engineered the obfuscated payload two days later.

Our attribution engine is open source. Every commit is public. If something changes in our SDK, you can see exactly what changed, who changed it, and why. Transparency isn't a marketing angle — it's the mechanism that makes "trust but verify" actually possible.


The uncomfortable question

The attribution industry was built in an era when nobody thought twice about loading third-party JavaScript from a shared CDN. Data sharing was the default. Vendors grew by seeing more apps, more data, more cross-app signals.

That era is ending. Not because of regulations — though GDPR and ATT helped — but because attackers figured out that shared infrastructure is the highest-leverage target on the internet. One domain. One hundred thousand apps. Forty-eight hours.

The question to ask your attribution vendor isn't "do you have good security?" Everyone says yes. The question is: if your infrastructure is compromised, how many other apps go down with mine?

If the answer is more than one, you have a structural problem that no amount of patching can fix.


Have questions about the attack or about how first-party attribution works? Reach out — happy to talk through the technical details.


Sources