Convertr / Plausible Analytics Supply-Chain Skimmer

A Brazilian Magecart group tracked by Sansec since July 2024 appended a card skimmer to the tracker script served from Convertr's self-hosted Plausible Community Edition, so every Convertr merchant checkout loaded it. Card number, expiry, CVV, cardholder name and CPF were sent to 1x0.is from 2026-10-24. Cleaned 2026-10-27. Suspected entry CVE-2026-8467 is unconfirmed. Defensive hunt and harden pack, no exploit code.
Threat
Analytics script supply-chain skimmer · Brazilian Magecart group
Severity
SUPPLY CHAIN · CARD + CPF THEFT · REMEDIATED 2026-10-27
Type
Poisoned tracker script, browser skimmer, Base64 POST exfil
Access
Suspected CVE-2026-8467 on Plausible CE 3.0.0 to 3.2.0 (unconfirmed)
Exfil
1x0.is (path /ws)
Version
v0.1 · 2026-11-01
Author
HuntPack
Confidence
Medium (single primary source)
01

Executive Summary

A Brazilian Magecart group that Sansec has tracked since July 2024 planted a payment-card skimmer in the analytics script of Convertr, a Brazilian ecommerce platform that runs checkout for dozens of fashion brands. The group appended an obfuscated block to the stock tracker served from Convertr's own self-hosted Plausible Community Edition server, so every merchant that loads the tracker served the skimmer on its checkout pages. This is the group's first supply-chain attack: until now it compromised stores one at a time (Magento, OpenCart, WooCommerce).

The skimmer activates only on Convertr checkout pages, reads card number, expiry, CVV, cardholder name and the Brazilian CPF taxpayer number, joins them into a pipe-delimited record ending in a short victim tag, Base64-encodes it and POSTs it to 1x0.is. Sansec detected it on 2026-10-24, saw it updated on 2026-10-26 to match Convertr's payment API field names, and confirmed it removed on 2026-10-27 after Convertr cleaned the script and upgraded Plausible. Browsers can cache the infected script for up to 24 hours, so exposure runs to about 2026-10-28.

Entry point is unconfirmed. Sansec suspects CVE-2026-8467 (unauthenticated remote code execution in a component library exposed at /storybook in Plausible CE 3.0.0 through 3.2.0, fixed in 3.2.1) but states it has not seen Convertr's server logs and cannot confirm it.

Defender priority. The skimmer runs in shoppers' browsers, which your endpoint sensor does not see. The value of this pack is (1) finding your own staff, QA and kiosk machines that touched a poisoned checkout or resolved the exfiltration domains (Q1 to Q3), (2) auditing any self-hosted Plausible CE you run (Q4 to Q7, N1, N2), and (3) the hardening that keeps third-party scripts from reading payment fields. If you are a merchant on the platform, ask the platform operator to confirm the tracker is clean and tell your payment provider.
02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry
1 · Vendor researchSansec: Notorious Brazilian Magecart group hijacks Convertr checkouts via outdated Plausible installPrimary source. Skimmer behavior, exfiltration endpoint, 14 earlier domains, 6 Discord webhook IDs, infected-file SHA-256, timeline, suspected CVE (unconfirmed)yes
1 · Vulnerability recordNVD API record for CVE-2026-8467Code injection in the phoenix_storybook library (0.5.0 up to, not including, 1.1.0), CVSS 4.0 base 9.5, published 2026-05-20, status Deferred. NVD does not name Plausible; the Plausible CE version range comes from Sansecyes
2 · News of recordBleepingComputer: Hackers hijack Convertr analytics script to steal payment card dataCorroborates scope, dates, the unconfirmed CVE and the field-name update. Direct fetch was blocked (HTTP 403); only a condensed rendering was saved, so no IOC value is taken from itpartial
3 · NewsSecurityOnline: Brazilian Magecart Group Hits Convertr in Plausible Supply Chain AttackRestates Sansec: eval of an appended character-code array, URL check, Base64 POST to a short Icelandic domain. Names no indicator valuespartial

Decisions: Sansec is the only source for the exfiltration domains, the webhook IDs and the file hash, so every indicator ships at detect, none at prevent or critical (a second independent source is required for those). The victim tracker host analytics.convertr.app.br and Plausible itself are legitimate and are never block candidates. CVE-2026-8467 is presented as a suspected, unconfirmed entry point throughout. A public proof-of-concept gist for the CVE turned up in search; it was not fetched or cited, because this pack carries no exploit code. No fetched source contained instructions directed at the reader.

03

Hunt Brief & Attack Chain

Working hypothesis: an organization either (a) has staff, QA or kiosk machines that loaded a Convertr checkout between 2026-10-24 and about 2026-10-28 and so ran the skimmer, or (b) runs its own Plausible Community Edition and is exposed to the same flaw. In case (a) the browser resolved an exfiltration domain, usually 1x0.is; in case (b) the analytics host may show command execution, a modified tracker file, or unusual outbound traffic.

StepBehaviorTelemetryHunt Angle
1 · Initial access (suspected)Unauthenticated command execution on an outdated Plausible CE via /storybook (T1190). Unconfirmed for this incidentWeb access logs (outside endpoint telemetry); Process creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)N1; Q4, Q5
2 · Tracker poisonedObfuscated character-code block appended to the served tracker file (T1195.002, T1565.001)File integrity monitoring (outside endpoint telemetry); Process creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)N2; Q6
3 · Skimmer loadedEvery merchant checkout loads the tracker; the appended block is decoded and run with eval (T1059.007, T1027)Browser content (outside endpoint telemetry); DNS lookup with the requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DnsQueryResponse)N4; Q3, Q9
4 · Gate on checkoutRuns only when the page URL contains checkout.convertr; field selectors updated 2026-10-26 to the payment API namesBrowser content (outside endpoint telemetry)N4 (CSP, script scan)
5 · CaptureCard number, expiry, CVV, cardholder name and CPF read from the payment form and API requests (T1056)Browser content (outside endpoint telemetry)Known gap
6 · ExfiltrationPipe-delimited record, Base64, POST to 1x0.is/ws (T1041, T1132.001); earlier builds used other short domains, Discord webhooks or planted scripts such as cartao.phpDNS lookup with the requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DnsQueryResponse); proxy URL logsQ1, Q2, Q8; N3
04

Consolidated IOC Table

Every exfiltration indicator below comes from one source (Sansec) and ships at detect. The victim host analytics.convertr.app.br is a legitimate service that was abused: it is exposure context and is never a block candidate. Copy-ready blocks are in section 10.
TypeValueConfActionContext
Domain (exfil)1x0.ismediumdetectCurrent endpoint, path /ws over port 443; also seen a day earlier in a skimmer on another Brazilian store
Domain (exfil, earlier)snf.is, kk.land, nmn.is, kof.one, anl.is, xsox.diy, eqp.is, shy.is, blz.is, tray.is, miorl.net, xwv.is, fasthook.app, antiddos-protection.netmediumdetect14 earlier exfiltration domains of the same group, replaced every few months; may be dormant or reassigned
Discord webhooks1349318491479605268, 1349948669419327508, 1351613753069801583, 1398562090934993036, 1414969348262596778, 1557650706259902495mediumhuntWebhook IDs under discord.com/api/webhooks/; visible only in proxy URL logs. Do not block discord.com
URLhttps://1x0.is/wsmediumdetectPOST target of the skimmer (/ws/<base64> also listed)
Filenamecartao.php, pagamento.phplowhuntPortuguese exfiltration script names; common legitimate page names, so a lead only
Victim host (legitimate)analytics.convertr.app.brhighdetect (context)Tracker host; clean since 2026-10-27. Exposure only, never prevent
Script path/js/script.pageview-props.tagged-events.jshighhuntInfected file path on the tracker host
SHA-256ee4618d24737eff785e5a267b55903d5ef6ff7ecbeb099347a67fee77734d7f7mediumhuntInfected tracker as observed 2026-10-26; one build, the skimmer was updated again
Skimmer stringsindexOf('checkout.convertr'); _s+=String.fromCharCode(_a[_i]);(0,eval)(_s)mediumhuntStrings in the loader; useful for script-content scans
Vulnerable rangePlausible CE 3.0.0 to 3.2.0 (fixed 3.2.1)mediumauditSuspected, unconfirmed entry point (CVE-2026-8467)
05

Affected Surface & Telemetry Matrix

SurfaceRequired TelemetryPriorityGap
Shoppers' browsers (the primary victims)Page content and network requests in the browserCriticalOutside endpoint telemetry and outside your control; only the platform, the merchant and the card issuers can act
Staff, QA and kiosk machines that load storefront checkoutsDNS lookup with the requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DnsQueryResponse)HighLow where the sensor is deployed. Unmanaged and personal devices are invisible
Self-hosted Plausible CE analytics hostsProcess creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); Outbound network connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents); web access logsHighMedium. Access logs and file contents are outside endpoint telemetry
Tracker script as served to customersFile integrity monitoring, CDN and cache captures, CSP reportsHighHigh. No endpoint query sees a write to a non-executable script file
Web proxy and gatewayURL and POST logs for 1x0.is/ws and Discord webhook pathsMediumDepends on TLS inspection and log retention
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
Initial AccessT1195.002 Compromise Software Supply ChainPoisoned tracker served to every merchant checkoutQ3; N2; S12
Initial AccessT1190 Exploit Public-Facing ApplicationSuspected CVE-2026-8467 on Plausible CE (unconfirmed)Q4, Q5, Q7; N1
Impact / Defense EvasionT1565.001 Stored Data Manipulation (closest fit)Tracker file on disk modified to append the skimmerQ6; N2
ExecutionT1059.007 JavaScriptAppended block executes in the browser via evalQ9 (script hosts only); N4
Defense EvasionT1027 Obfuscated Files or InformationArray of character codes decoded at runtimeQ9; N4
CollectionT1056 Input CaptureCard, expiry, CVV, name and CPF read from checkout form and payment API requestsKnown gap; S12 controls
ExfiltrationT1041 Exfiltration Over C2 ChannelPOST to 1x0.is/ws; earlier domains, webhooks, planted scriptsQ1, Q2, Q8; N3
Command and ControlT1132.001 Standard EncodingBase64 of a pipe-delimited recordQ1, Q2; N3
07

Native Audit-Log Hunts

HuntSourceLogicResponse
N1 · Storybook requestsReverse-proxy and Plausible access logsAny request whose path starts with /storybook, any date. Sansec says to search for these on self-hosted Plausible CEAny hit: rebuild the server, rotate its secrets, compare tracker files (N2)
N2 · Tracker integrityFilesystem on the Plausible host; file-integrity monitoringCompare every file in priv/tracker/js/ with the official release of the version you run. Look for an appended block after the stock script and the strings in section 10. Compare the SHA-256 of served copies with the infected valueAny difference: treat the host as compromised and rebuild
N3 · Proxy and WAF exfil trafficWeb proxy, gateway, WAF logsPOSTs to 1x0.is (path /ws), the earlier domains, discord.com/api/webhooks/ with the listed IDs, and requests naming cartao.php or pagamento.phpIdentify the client, then run Q1 and Q2 for that machine
N4 · Checkout script reviewCSP violation reports, synthetic checkout crawl, script inventoryAny script on a payment page that is not on the approved list; fetch the served tracker and scan for fromCharCode followed by eval and for checkout.convertrRemove or block the script; escalate to the platform operator
N5 · Cache exposure windowCDN, reverse-proxy and browser cache settingsConfirm the served tracker hash is no longer the infected one, and purge caches. Browsers can hold the infected file for up to 24 hours after cleanupPurge; extend the exposure window to 24 hours after the purge
N6 · Payment fraud signalsPayment provider and issuer fraud reportsCards used on the affected checkout from 2026-10-24 to about 2026-10-28, and CPF misuse reportsAsk issuers to reissue; follow your payment provider's incident process
08

Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL

Show all queries in
Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
What these queries can and cannot see. The skimmer runs in shoppers' browsers, which no endpoint query observes. Q1 to Q3 find your own machines that touched the infrastructure; Q4 to Q7 cover a self-hosted Plausible host; Q8 and Q9 are low-confidence leads. Q9 sees script content only when it reaches a command line. Event names and fields were checked against the crowdstrike-logscale-v3 references; validate in your tenant before scheduling.
Q1 · DNS lookups for the exfiltration domains (any process)
CONF HIGHFP LOWCOST LOW

Looks for: any DNS request for 1x0.is or one of the 14 earlier exfiltration domains Sansec lists. Accomplishes: catches a staff, QA or kiosk machine whose browser ran the skimmer on a poisoned checkout. The skimmer runs in the shopper's browser, so this only sees machines your sensor covers.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Lookups made by a resolver outside the endpoint, or on a device without the agent, are not seen.
  • PowerQuery assumes: event.dns.request holds the queried name on DNS Resolved events; the vendor docs also name dns.request, so the readiness test is the check.
  • KQL – Defender XDR: Lookups made by a resolver outside the endpoint, or on a device without the sensor, are not seen.
  • KQL – Defender XDR assumes: The queried name is stored under DnsQueryString or QueryName inside AdditionalFields; the readiness test prints the field so a reader sees which key their tenant populates.
// HUNT: DNS lookups for the campaign's exfiltration domains (any process)
// MITRE: T1041, T1132.001 | CONF: high  FP: low  COST: low
// REQUIRES: DnsRequest telemetry from staff, kiosk, QA and test machines that load storefront checkouts
// FALSE POSITIVES: short, generic-looking names (fasthook.app, tray.is) could be reused by unrelated parties after the group stopped using them
// TUNING: 1x0.is is the current endpoint, treat any hit as high priority; for the older names compare the first-seen date with the 2026-10-24 window before escalating
#event_simpleName=DnsRequest
| DomainName=/(^|\.)(1x0\.is|snf\.is|kk\.land|nmn\.is|kof\.one|anl\.is|xsox\.diy|eqp\.is|shy\.is|blz\.is|tray\.is|miorl\.net|xwv\.is|fasthook\.app|antiddos-protection\.net)$/i
| table([@timestamp, aid, ComputerName, DomainName, ContextProcessId_decimal, RequestType], limit=200)
| sort(@timestamp, order=desc)
Q2 · Process attribution for exfiltration-domain lookups
CONF HIGHFP LOWCOST MED

Looks for: the same DNS lookups as Q1, joined to the process that made them. Accomplishes: separates a browser on a staff machine (checkout visit) from a service or script host (server-side activity on a compromised host). The join adds cost, so run Q1 first.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A lookup whose process the agent could not attribute shows empty process columns.
  • PowerQuery assumes: On DNS Resolved events the acting process is src.process.* and its parent is src.process.parent.*.
  • PowerQuery assumes: event.dns.request holds the queried name; the vendor docs also name dns.request.
  • KQL – Defender XDR: A lookup whose process the sensor could not attribute shows empty process columns.
  • KQL – Defender XDR assumes: The queried name is stored under DnsQueryString or QueryName inside AdditionalFields.
  • KQL – Defender XDR assumes: DnsQueryResponse rows populate the InitiatingProcess* columns.
// HUNT: Which process resolved an exfiltration domain (browser vs script host vs server service)
// MITRE: T1041, T1132.001 | CONF: high  FP: low  COST: medium
// REQUIRES: DnsRequest and ProcessRollup2 from the same sensor (join on aid + process id)
// FALSE POSITIVES: security tooling that detonates or resolves threat-intel domains from a sandbox or scanner host
// TUNING: exclude sandbox and scanner hosts by ComputerName; a browser process on a staff machine is the expected shape, a service or script host is not
#event_simpleName=DnsRequest
| DomainName=/(^|\.)(1x0\.is|snf\.is|kk\.land|nmn\.is|kof\.one|anl\.is|xsox\.diy|eqp\.is|shy\.is|blz\.is|tray\.is|miorl\.net|xwv\.is|fasthook\.app|antiddos-protection\.net)$/i
| rename(field=ContextProcessId_decimal, as=TargetProcessId_decimal)
| join(query={#event_simpleName=ProcessRollup2}, field=[aid, TargetProcessId_decimal], mode=left, include=[FileName, CommandLine, ParentBaseFileName, UserName])
| table([@timestamp, aid, ComputerName, DomainName, FileName, CommandLine, ParentBaseFileName, UserName], limit=200)
| sort(@timestamp, order=desc)
Q3 · Exposure: lookups of the victim tracker host or a checkout host
CONF MEDFP HIGHCOST LOW

Looks for: machines that resolved analytics.convertr.app.br or a checkout.convertr host. FP: normal use of the platform; this host is the victim's legitimate infrastructure and is never a block candidate. Accomplishes: builds the list of people to ask whether they paid by card between 2026-10-24 and 2026-10-28.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The CQL lists every distinct domain per host; this query returns one sample and a distinct count because the collect aggregate is not available.
  • PowerQuery assumes: event.dns.request holds the queried name.
  • PowerQuery assumes: contains matches checkout.convertr anywhere in the name, so a name such as xcheckout.convertr.com also matches; the CQL requires a label boundary.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days, so after 2026-11-23 the start of the 2026-10-24 window is no longer queryable here; use the CQL tab.
  • KQL – Defender XDR assumes: The queried name is stored under DnsQueryString or QueryName inside AdditionalFields.
  • KQL – Defender XDR assumes: contains matches checkout.convertr anywhere in the name; the CQL requires a label boundary.
// HUNT: Machines that resolved the victim tracker host or a checkout host (exposure only, not compromise)
// MITRE: T1195.002 | CONF: medium  FP: high  COST: low
// REQUIRES: DnsRequest telemetry from staff and test machines; run over 2026-10-24 through 2026-10-28
// FALSE POSITIVES: ordinary legitimate shopping or merchant-staff use of the platform outside the poisoned window
// TUNING: set the search window to 2026-10-24 00:00 UTC through 2026-10-28 14:00 UTC (cleanup 2026-10-27 12:54 UTC plus up to 24 hours of browser caching); a hit means exposure, so ask the user whether they paid by card, then pivot to Q1 and Q2
#event_simpleName=DnsRequest
| DomainName=/(^analytics\.convertr\.app\.br$|(^|\.)checkout\.convertr)/i
| groupBy([aid, ComputerName], function=[count(as=lookups), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen), collect([DomainName])])
| sort(lastSeen, order=desc, limit=200)
Q4 · Inventory: Linux hosts running Plausible Community Edition
CONF MEDFP LOWCOST LOW

Looks for: BEAM processes whose command line mentions Plausible. Accomplishes: lists which of your hosts could carry the suspected entry point (CVE-2026-8467, reported but unconfirmed for this incident), so version checks and the /storybook log search (N1) start from a list, not a guess.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The CQL collects every parent name per host; this query returns one sample parent.
  • PowerQuery: A BEAM VM started before the window or before the agent was installed is not listed.
  • PowerQuery assumes: tgt.process.name holds beam.smp or beam for the BEAM VM on Linux agents.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days; a BEAM VM started earlier is not listed here.
  • KQL – Defender XDR: A process the sensor did not observe starting is not captured.
  • KQL – Defender XDR assumes: FileName holds beam.smp or beam for the BEAM VM on Linux sensors.
// HUNT: Linux hosts running Plausible Community Edition (scope the exposure to the suspected entry point)
// MITRE: T1190 | CONF: medium  FP: low  COST: low
// REQUIRES: ProcessRollup2 from Linux servers; a lookback longer than the service uptime, because a process started before the window is not listed
// FALSE POSITIVES: any unrelated BEAM (Erlang or Elixir) application whose command line contains the word plausible
// TUNING: widen the search window to cover the last restart; confirm the version on each listed host (3.0.0 through 3.2.0 is the reported vulnerable range, 3.2.1 is the fix)
#event_simpleName=ProcessRollup2
| FileName=/^beam(\.smp)?$/i
| CommandLine=/plausible/i
| groupBy([aid, ComputerName], function=[count(as=starts), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen), collect([ParentBaseFileName])])
| sort(lastSeen, order=desc, limit=200)
Q5 · BEAM VM spawning a shell, download tool or interpreter
CONF MEDFP MEDCOST LOW

Looks for: the Plausible runtime launching sh, curl, wget, nc or an interpreter. FP: helper scripts the service launches itself. Accomplishes: catches command execution through an unauthenticated remote-code flaw on the analytics host, which is how the server side of this attack would look.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A shell started by a process other than the BEAM VM, for example after the attacker pivots, is not caught.
  • PowerQuery assumes: src.process.user holds the account that ran the creator process.
  • KQL – Defender XDR: A shell started by a process other than the BEAM VM is not caught.
  • KQL – Defender XDR assumes: FileName holds the bare binary name on Linux sensors.
// HUNT: Shell, download tool or interpreter spawned by the BEAM VM that runs Plausible (post-exploitation on the analytics host)
// MITRE: T1190, T1059.004 | CONF: medium  FP: medium  COST: low
// REQUIRES: ProcessRollup2 with parent-process context from the Plausible host
// FALSE POSITIVES: operator maintenance tasks, release helper scripts and container health checks that the service launches itself
// TUNING: baseline for 14 days on the Plausible host, then allow-list the exact CommandLine of each known helper; keep curl, wget, nc, socat and interpreter hits unexcluded
#event_simpleName=ProcessRollup2
| ParentBaseFileName=/^(beam\.smp|beam|erl_child_setup)$/i
| FileName=/^(sh|bash|dash|ash|busybox|curl|wget|nc|ncat|socat|python[0-9.]*|perl|ruby|base64|openssl)$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
| sort(@timestamp, order=desc)
Q6 · Commands touching the Plausible tracker script or directory
CONF MEDFP MEDCOST LOW

Looks for: write-capable tools that name script.pageview-props.tagged-events.js or priv/tracker/js. FP: admin upgrades and restores. Gap: no CQL sees a file write to a non-executable .js file Pair with file-integrity monitoring (N2).

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The CQL also reads synthetic process-start events that the agent can raise for processes it did not see start; this query reads Process Creation only.
  • PowerQuery assumes: The tracker directory is matched as the substring priv/tracker/js or its backslash form.
  • KQL – Defender XDR: The CQL also reads synthetic process-start events; this query reads real process creation only.
  • KQL – Defender XDR assumes: FileName holds the bare binary name on Linux sensors.
// HUNT: Commands that reference the Plausible tracker script or its directory (tampering with the served file)
// MITRE: T1565.001, T1195.002 | CONF: medium  FP: medium  COST: low
// REQUIRES: ProcessRollup2 command-line telemetry from the Plausible host
// FALSE POSITIVES: administrators upgrading Plausible, restoring backups or comparing tracker files against a release
// TUNING: exclude read-only verification such as sha256sum, ls and diff from known admin accounts; any write-capable tool (sed, tee, cp, mv, dd, curl, wget) acting on the tracker file outside a change window is the signal
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/(script\.pageview-props\.tagged-events\.js|priv[\/\\]tracker[\/\\]js)/i
| FileName=/^(sed|tee|cp|mv|dd|curl|wget|echo|printf|perl|python[0-9.]*|node|vi|vim|nano|sh|bash)$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
| sort(@timestamp, order=desc)
Q7 · BEAM VM outbound to non-private addresses
CONF LOWFP MEDCOST MED

Looks for: the Plausible runtime connecting to public addresses, rare destinations first. FP: SMTP relay and managed database. Accomplishes: a low-confidence lead for a reverse shell or staging download after exploitation.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: IPv6 destinations are not excluded by the listed ranges, and the CQL reads IPv4 events only.
  • PowerQuery assumes: IP Connect records connections the process initiated, so inbound connections are not in the population.
  • PowerQuery assumes: dst.ip.address holds an IPv4 address for these connections.
  • KQL – Defender XDR: Failed connection attempts are not counted, and IPv6 destinations are not excluded by the listed ranges.
  • KQL – Defender XDR assumes: ConnectionSuccess is the ActionType for an established outbound connection.
  • KQL – Defender XDR assumes: RemoteIP holds an IPv4 address for these connections.
// HUNT: BEAM VM on the Plausible host connecting outbound to non-private addresses
// MITRE: T1190, T1071.001 | CONF: low  FP: medium  COST: medium
// REQUIRES: NetworkConnectIP4 with initiating-process context from the Plausible host
// FALSE POSITIVES: outbound SMTP relay, geolocation database or version-check downloads, and externally hosted databases
// TUNING: allow-list the remote addresses of your SMTP relay and managed database; rare destinations (low conns) on unusual ports are the interesting rows
#event_simpleName=NetworkConnectIP4
| ContextBaseFileName=/^(beam\.smp|beam)$/i
| !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "169.254.0.0/16"])
| groupBy([aid, ComputerName, RemoteAddressIP4, RemotePort], function=[count(as=conns), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen)])
| sort(conns, order=asc, limit=200)
Q8 · Command lines naming cartao.php or pagamento.php
CONF LOWFP MEDCOST LOW

Looks for: curl, wget, PHP or shell commands that name the group's exfiltration script names. FP: common legitimate page names on Brazilian stores. Accomplishes: a lead on compromised stores or admin hosts, never an alert on its own.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The CQL also reads synthetic process-start events; this query reads Process Creation only.
  • PowerQuery assumes: tgt.process.name holds the bare binary name.
  • KQL – Defender XDR: The CQL also reads synthetic process-start events; this query reads real process creation only.
  • KQL – Defender XDR assumes: FileName holds the bare binary name.
// HUNT: Commands naming the group's Portuguese exfiltration script names (cartao.php, pagamento.php)
// MITRE: T1041 | CONF: low  FP: medium  COST: low
// REQUIRES: ProcessRollup2 command-line telemetry, typically from web servers or administrator workstations
// FALSE POSITIVES: both names are ordinary Portuguese words (card, payment) and legitimate Brazilian stores often ship pages with these exact names
// TUNING: treat a hit as a lead only; correlate with Q1 and Q2 on the same host, and require the command to POST to an external host before escalating
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/(cartao|pagamento)\.php/i
| FileName=/^(curl|wget|php[0-9.]*|python[0-9.]*|perl|sh|bash|powershell|pwsh)(\.exe)?$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
| sort(@timestamp, order=desc)
Q9 · Script host decoding a character-code array and calling eval
CONF MEDFP LOWCOST LOW

Looks for: node, deno, bun, cscript or wscript run with fromCharCode and eval in the command line. Accomplishes: the skimmer loader's decode-and-eval pattern wherever script content reaches a command line. The shopper-side copy runs inside a browser, which endpoint telemetry cannot see.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The CQL also reads synthetic process-start events; this query reads Process Creation only.
  • PowerQuery: Code run inside a browser is not visible to a command-line hunt.
  • PowerQuery assumes: tgt.process.name holds the bare binary name.
  • KQL – Defender XDR: The CQL also reads synthetic process-start events; this query reads real process creation only.
  • KQL – Defender XDR: Code run inside a browser is not visible to a command-line hunt.
  • KQL – Defender XDR assumes: FileName holds the bare binary name.
// HUNT: Script host run with an inline character-code decode followed by eval (the skimmer's loader pattern)
// MITRE: T1059.007, T1027 | CONF: medium  FP: low  COST: low
// REQUIRES: ProcessRollup2 command line for node, deno, bun or Windows script hosts; browsers are NOT covered
// FALSE POSITIVES: developers testing obfuscation or running minified code with -e, and security researchers reproducing the sample
// TUNING: exclude developer build hosts by ComputerName; a production server or kiosk running this is unusual
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^(node|nodejs|deno|bun|cscript|wscript)(\.exe)?$/i
| CommandLine=/fromCharCode/i
| CommandLine=/eval/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
| sort(@timestamp, order=desc)
09

Detection Rules by Platform

CrowdStrike: Custom IOA

IOA namePromotesPatternExclusionsDeployment
Exfil domain lookupPromote Q1, Q2Domain Name rule matching 1x0.is and the 14 earlier domains (Q1 regex)Sandbox and scanner hostsDetect only. Single-sourced (Sansec), so no prevent until a second independent source confirms
Plausible runtime spawns shell or download toolPromote Q5Process Creation, parent image beam.smp or erl_child_setup, child sh, bash, curl, wget, nc, interpretersKnown helper command lines after a 14-day baselineDetect on the Plausible host group only
Tracker script touched by a write-capable toolPromote Q6Process Creation, command line naming script.pageview-props.tagged-events.js with sed, tee, cp, mv, dd, curl or wgetChange-window admin accountsDetect only
No IOA or IOC in this pack is set to prevent. The exfiltration domains are single-sourced. The victim tracker host and Plausible are legitimate and must never be blocked. Q3 stays an exposure hunt, and Q4, Q7, Q8 and Q9 stay hunts.

SentinelOne: custom detection rules

RuleSource queryRun asNotes
Exfil domain lookupQ1, Q2, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
Plausible runtime spawns shell or download toolQ5, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written
Tracker script touched by a write-capable toolQ6, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"

Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.

Microsoft Defender: custom detection rules

RuleSource queryRun asNotes
Exfil domain lookupQ1, Q2, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself
Plausible runtime spawns shell or download toolQ5, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written
Tracker script touched by a write-capable toolQ6, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself

Q3, Q4, Q7, Q8 and Q9 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.

10

Machine-Readable IOC Appendix

Import posture. Every row is detect. Severity is high for the current endpoint, medium for the earlier domains and informational for the victim's legitimate tracker host (exposure context only). Nothing is at prevent or critical.

Grouped IOC Quick-Copy

Falcon IOC Management CSVbulk import, all detect
type,value,action,severity,expiration,description,tags
domain,1x0.is,detect,high,2027-05-01,Skimmer exfiltration endpoint (Sansec only; path /ws on port 443),campaign:Convertr-Plausible-Skimmer
domain,snf.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,kk.land,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,nmn.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,kof.one,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,anl.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,xsox.diy,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,eqp.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,shy.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,blz.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,tray.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,miorl.net,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,xwv.is,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,fasthook.app,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,antiddos-protection.net,detect,medium,2027-05-01,Earlier exfiltration domain of the same group (Sansec only),campaign:Convertr-Plausible-Skimmer
domain,analytics.convertr.app.br,detect,informational,2027-05-01,VICTIM legitimate tracker host. Exposure context only. NEVER prevent,campaign:Convertr-Plausible-Skimmer
# sha256 ee4618d24737eff785e5a267b55903d5ef6ff7ecbeb099347a67fee77734d7f7: the infected tracker script on the victim server on 2026-10-26 (a JavaScript file; use it for file-integrity and proxy comparison, not as a Falcon execution IOC)
Microsoft Defender indicators CSVSettings, Endpoints, Indicators, Import: audit only
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert
DomainName,1x0.is,Audit,Skimmer exfiltration endpoint,Skimmer exfiltration endpoint (Sansec only; path /ws on port 443),2027-05-01T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,snf.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,kk.land,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,nmn.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,kof.one,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,anl.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,xsox.diy,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,eqp.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,shy.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,blz.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,tray.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,miorl.net,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,xwv.is,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,fasthook.app,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,antiddos-protection.net,Audit,Earlier exfiltration domain of the same group,Earlier exfiltration domain of the same group (Sansec only),2027-05-01T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
DomainName,analytics.convertr.app.br,Audit,VICTIM legitimate tracker host. Exposure context only. NEVER,VICTIM legitimate tracker host. Exposure context only. NEVER prevent,2027-05-01T00:00:00.0Z,Informational,Investigate the device and pivot into the pack's queries,,,,True

Column names and allowed values are from Microsoft's documented parameter table. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).

SentinelOne IOC importnot included

This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.

Discord webhook IDsproxy / URL log hunt
# Discord webhook IDs used for exfiltration (Sansec only). Hunt in web proxy or gateway URL logs.
# Endpoint DNS cannot see the path. Do not block discord.com itself.
discord.com/api/webhooks/1349318491479605268
discord.com/api/webhooks/1349948669419327508
discord.com/api/webhooks/1351613753069801583
discord.com/api/webhooks/1398562090934993036
discord.com/api/webhooks/1414969348262596778
discord.com/api/webhooks/1557650706259902495
Exfil URLs, paths and file hashproxy / WAF / FIM
https://1x0.is/ws
https://1x0.is/ws/<base64>
cartao.php
pagamento.php
# Infected tracker (victim host, legitimate service, detect only):
https://analytics.convertr.app.br/js/script.pageview-props.tagged-events.js
sha256 ee4618d24737eff785e5a267b55903d5ef6ff7ecbeb099347a67fee77734d7f7  (infected copy observed on 2026-10-26)
Behavioral signaturesscript content scan
# Strings Sansec lists in the skimmer (search served tracker copies, CDN caches, WAF captures)
indexOf('checkout.convertr')
_s+=String.fromCharCode(_a[_i]);(0,eval)(_s)
# Record shape: card | month | year | cvv | cpf | name | short victim tag (ctr for Convertr)
# Body is Base64 of the pipe-delimited record, sent by POST with mode no-cors and keepalive
# Structural check: the stock Plausible tracker followed by an appended block of character codes
Patch and exposure auditself-hosted Plausible CE
# Reported affected: Plausible Community Edition 3.0.0 through 3.2.0
# Fixed in: 3.2.1 (upgrade to 3.2.1 or later)
# CVE-2026-8467 (phoenix_storybook 0.5.0 up to, not including, 1.1.0): suspected entry point, NOT confirmed
# Search reverse-proxy and application access logs for requests to /storybook
# Any hit: rebuild the server, rotate its secrets, compare priv/tracker/js against the official release
11

Detection Validation Gates

GateCheck
Telemetry readyDNS lookup with the requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DnsQueryResponse) flows from staff, QA and kiosk machines; Process creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) and Outbound network connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents) flow from the Plausible host. Browser content is outside endpoint telemetry
Benign baselineRun Q1 and Q2 over 30 days: expect zero hits. Run Q5 to Q7 on the Plausible host for 14 days and record legitimate helpers and destinations to build the TUNING exclusions
Positive testsIn an isolated lab only: resolve one listed domain with nslookup from a sensored host (Q1, Q2, no connection is made). On a lab Linux host, copy a shell to a file named beam.smp and have it start sh -c id (Q5). Never browse to a listed domain or run the sample
Field checkConfirm ParentBaseFileName and ContextBaseFileName are populated on your Linux sensor events, and that the Plausible BEAM command line contains the word plausible (Q4)
PromotionQ1 and Q2 to scheduled alerts after a clean baseline; Q5 and Q6 as detections on the Plausible host group after tuning; Q3, Q4, Q7, Q8, Q9 stay hunts
12

Hardening: Tiered & Deployable

Immediate actions remove the suspected entry point and confirm the served script is clean. Near-term and strategic controls stop a third-party script from reading payment fields, which is the underlying failure.

Immediate (this week, low compatibility risk)
  • Upgrade self-hosted Plausible CE to 3.2.1 or later (MITRE M1051 Update Software; vendor release notes, no formal benchmark: best-practice). Verify: the image tag or version string, and that /storybook now returns 404 (Playbook 1).
  • Search access logs for /storybook and verify tracker files against the official release (M1047 Audit; Sansec guidance). Any hit means rebuild, not patch in place (Playbook 1, N1, N2).
  • Block /storybook at the reverse proxy until the upgrade lands (M1037 Filter Network Traffic; best-practice). Verify with a request that returns 404 or 403 (Playbook 2).
  • Alert on the exfiltration domains at the DNS resolver or gateway (M1031 Network Intrusion Prevention). Alert, not block, until a second source confirms the list; do not block the victim tracker host.
  • Purge CDN and proxy caches of the tracker and confirm the served hash (M1051). Browsers can keep the infected file for up to 24 hours.
Near term (1 to 4 weeks, pilot first)
  • Content-Security-Policy on payment pages with a strict script-src and connect-src allowlist (M1021 Restrict Web-Based Content; PCI DSS v4.0 requirement 6.4.3). Deploy in report-only mode first (Playbook 3). Verify: violation reports arrive and an unlisted host is reported.
  • Inventory and authorize every script on payment pages, with integrity checks and tamper detection (PCI DSS v4.0 requirements 6.4.3 and 11.6.1; M1047). Verify: a script that changes hash raises an alert.
  • File-integrity monitoring on the Plausible tracker directory (M1047; Linux auditd watch, Playbook 4). Verify with ausearch -k after a test touch in a lab.
  • Restrict outbound egress from the analytics host to an allowlist (M1037). Verify: a test connection to an arbitrary external host fails (Q7 logic).
Strategic (1 to 3 months)
  • Use payment provider hosted fields or an iframe so card data never exists in the merchant page DOM (M1048 Application Isolation and Sandboxing). Removes the skimmer's data source even if a script is poisoned.
  • Remove third-party and analytics scripts from payment pages or serve them from a sandboxed origin (M1021, M1048). Analytics on the checkout rarely justifies the risk.
  • Patch SLA and exposure management for self-hosted tooling, including developer components accidentally exposed to the internet (M1051, M1016 Vulnerability Scanning). A 2025 install that was never updated is the reported root cause pattern.
  • Network segmentation and secrets rotation for analytics servers (M1030 Network Segmentation; M1026 Privileged Account Management).
13

Deployable Playbooks

Playbook 1: Verify and upgrade a self-hosted Plausible CE (Docker Compose)

Prerequisites: shell access to the Plausible host, Docker Compose, a backup or snapshot of the ClickHouse and Postgres volumes, and a maintenance window. Container and path names vary; the steps discover them.

Reboot required: No. The Plausible container restarts.

Rollback: restore the saved compose file and image tag and restore the volume snapshot. Do not leave a 3.0.0 to 3.2.0 instance reachable from the internet; keep the Playbook 2 block in place if you roll back.

# 1. Record the running version and save the compose file (rollback evidence)
docker compose ps
docker compose images
cp docker-compose.yml docker-compose.yml.before-upgrade
# 2. Search reverse-proxy and application logs for storybook requests (any hit: rebuild, rotate secrets)
grep -rIn "/storybook" /var/log/nginx/ /var/log/caddy/ 2>/dev/null | head -50
# 3. Locate the tracker directory inside the running container and hash it
docker compose exec plausible sh -c 'find / -type d -path "*priv/tracker/js" 2>/dev/null'
docker compose exec plausible sh -c 'cd "$(find / -type d -path "*priv/tracker/js" 2>/dev/null | head -1)" && sha256sum *.js' | tee /root/tracker-running.sha256
# 4. Hash the same directory from a fresh container of the SAME tag you currently run, then compare
#    (replace TAG with the tag from step 1)
docker run --rm --entrypoint sh ghcr.io/plausible/community-edition:TAG -c 'cd "$(find / -type d -path "*priv/tracker/js" 2>/dev/null | head -1)" && sha256sum *.js' | tee /root/tracker-official.sha256
diff /root/tracker-running.sha256 /root/tracker-official.sha256 && echo "tracker files match the official release"
# 5. Upgrade: set the image to v3.2.1 or later in docker-compose.yml, then
docker compose pull && docker compose up -d
# 6. Verify the fix: the storybook path must no longer answer
curl -s -o /dev/null -w "%{http_code}\n" https://YOUR-PLAUSIBLE-HOST/storybook

Playbook 2: Block /storybook at an nginx reverse proxy (stopgap)

Prerequisites: nginx in front of Plausible and permission to reload it.

Reboot required: No. nginx -s reload applies it.

Rollback: remove the location block and reload nginx.

# Add inside the server block that proxies to Plausible
location ^~ /storybook {
    return 404;
}
# Apply and verify
nginx -t && nginx -s reload
curl -s -o /dev/null -w "%{http_code}\n" https://YOUR-PLAUSIBLE-HOST/storybook

Playbook 3: Content-Security-Policy in report-only mode for payment pages

Prerequisites: control of the response headers for the payment pages, a CSP report endpoint, and an inventory of the script and API hosts the page legitimately uses. The hosts below are placeholders.

Reboot required: No.

Rollback: delete the header. Report-only mode blocks nothing, so rollback is also safe to do last.

# nginx example: report-only first, enforce after one clean review cycle
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://scripts.example.com; connect-src 'self' https://payments.example.com; report-uri /csp-report" always;
# Verify a violation is reported: from a lab browser, load the page with a test script from an unlisted host
# and confirm the report arrives at /csp-report. Enforce by renaming the header to Content-Security-Policy.

Playbook 4: auditd watch on the Plausible tracker directory (Linux)

Prerequisites: auditd installed on the Plausible host and the real path of the tracker directory (Playbook 1, step 3). If Plausible runs in a container, watch the volume or overlay path that holds the files, or use your FIM product.

Reboot required: No. Add the rule to /etc/audit/rules.d/ to persist it across reboots.

Rollback: auditctl -W TRACKER_DIR -p wa -k plausible_tracker and remove the persisted rule.

TRACKER_DIR=/path/to/priv/tracker/js
auditctl -w "$TRACKER_DIR" -p wa -k plausible_tracker
auditctl -l | grep plausible_tracker
# After a test touch in a lab:
ausearch -k plausible_tracker -i | tail -20
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateIf a Plausible host shows a Q5, Q6 or N1/N2 hit, network-isolate it through EDR host containment. Alert on or sinkhole the exfiltration domains at DNS. For a merchant: take the analytics script off payment pages (tag manager or CSP) until the operator confirms it is cleanSOC / IRContainment timestamp, change ticket
Isolate: by platformCrowdStrike: network-contain the host. SentinelOne: network quarantine (Disconnect from Network; confirm the label in your console version). Microsoft Defender: Isolate device (full network isolation).SOC / IRContainment timestamp, change ticket
Isolate: by platformCrowdStrike: network-contain the host. SentinelOne: network quarantine. Microsoft Defender: isolate device. Keep the host powered on for evidence on all of themSOC / IRContainment timestamp
ScopeRun Q1 to Q3 fleet-wide for 2026-10-24 through 2026-10-28; list machines and users that resolved the tracker or checkout hosts; ask whether they paid by card; run N3 on proxy logsSOC L2Host list, user list
PreserveCopy the tracker files, access logs and a disk snapshot before any cleanup; record the SHA-256 of every file in the tracker directoryIREvidence archive with hashes
EradicateRebuild the Plausible host from a clean image at 3.2.1 or later rather than patching in place; rotate application secrets, database and mail credentials and any API keys stored on it; purge CDN and proxy cachesIR + ITRebuild record, rotation log, served-hash check
NotifyTell the payment provider and card issuers; follow your breach-notification duties (the platform operator reports it is notifying merchants, payment providers and authorities). Advise affected shoppers to ask their bank to replace the card and to watch for CPF misuseLegal / FraudNotification log
RecoverReturn the host after a clean 7-day Q4 to Q7 baseline; deploy Playbooks 1 to 4 and the CSP in enforce modeIT / Detection EngClean query results, sign-off
15

Detection Coverage Map

TechniqueBehaviorCQLIOACrowdStrike coverageSentinelOneDefender XDR
T1195.002Poisoned tracker served to checkoutsQ3nonePartial exposure only; the script itself is outside endpoint telemetryPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1190Suspected CVE-2026-8467 entry (unconfirmed)Q4, Q5, Q7IOA 2Partial HTTP requests to /storybook are outside endpoint telemetry (N1)Partial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1565.001Tracker file modifiedQ6IOA 3Partial command-line evidence only; file writes need FIM (N2)Partial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1059.007 / T1027Character-code array decoded and run with evalQ9noneGAP in the browser; Q9 sees script hosts onlyGAP: same limit as CrowdStrikeGAP: same limit as CrowdStrike
T1056Card, CVV and CPF captured in the browsernonenoneGAP outside endpoint telemetryGAPGAP
T1041 / T1132.001POST of Base64 record to exfiltration domainsQ1, Q2, Q8IOA 1Partial sensored machines only; shoppers' devices are not coveredPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike

Known gaps: the skimmer executes in shoppers' browsers, so the people most harmed are outside your endpoint telemetry. Endpoint hunts cover your own machines and the analytics host. Webhook IDs and URL paths need proxy or gateway logs. Validation gates (section 11): telemetry ready, 14 to 30 day baseline, lab positive tests, then promotion.

16

Hunt Summary Ticket

TITLE:        Hunt - Convertr / Plausible analytics supply-chain skimmer (Brazilian Magecart group)
SEVERITY:     High (card, CVV and CPF theft at checkout; remediated by the operator 2026-10-27; entry point unconfirmed)
SCOPE:        Staff, QA and kiosk machines that loaded a Convertr checkout 2026-10-24 to about 2026-10-28;
              any self-hosted Plausible Community Edition 3.0.0 to 3.2.0; web proxy and gateway logs
HYPOTHESIS:   A machine of ours ran the skimmer and resolved 1x0.is, or our Plausible host was reachable
              through /storybook and its tracker file was modified.
QUERIES RUN:  Q1 exfil DNS | Q2 process attribution | Q3 exposure | Q4 Plausible inventory | Q5 BEAM child
              | Q6 tracker file commands | Q7 BEAM outbound | Q8 cartao/pagamento | Q9 eval pattern
              + Native N1-N6
DO FIRST:     Run Q1 and Q2 over 30 days; run Q3 for 2026-10-24 to 2026-10-28 and ask each user about card
              use; search access logs for /storybook (N1); verify tracker files (N2).
FINDINGS:     <pending analyst execution>
GAPS:         Browser-side skimmer execution and shopper devices are outside endpoint telemetry; file writes
              to the tracker need FIM; webhook IDs and URL paths need proxy logs; all indicators single-sourced.
ACTIONS:      Upgrade Plausible to 3.2.1+, rebuild on any hit, rotate secrets, CSP report-only then enforce,
              payment-page script inventory, alert on exfil domains (detect only), notify provider if exposed.
OWNER:        HuntPack
VERSION:      v0.1 2026-11-01
17

Changelog

v0.12026-11-01Initial pack. Built from Sansec's report (published 2026-10-26, updated 2026-10-27) with the NVD record for CVE-2026-8467, BleepingComputer and SecurityOnline as corroboration. 9 CQL hunts, 6 native hunts, 16 domain rows (all detect), 6 Discord webhook IDs, 1 file hash. Entry point shown as suspected and unconfirmed. Source snapshots saved in Convertr-Plausible-Magecart-Hunt-sources/. SentinelOne PowerQuery and Microsoft Defender KQL tabs added to Q1-Q9: SIEM Query Rosetta release 0.1.0 (public-beta; static-reviewed translations).
18

References

TierSourceUsed ForAccess Date
1Sansec: Notorious Brazilian Magecart group hijacks Convertr checkouts via outdated Plausible installBehavior, timeline, all atomic indicators, suspected CVE2026-11-01
1NVD: CVE-2026-8467Vulnerability description, affected library versions, CVSS2026-11-01
2BleepingComputer: Hackers hijack Convertr analytics script to steal payment card dataCorroboration of narrative only (condensed text saved; no IOC values)2026-11-01
3SecurityOnline: Convertr supply-chain attack via PlausibleCorroboration of behavior (no IOC values)2026-11-01

Source snapshots are saved next to this file in Convertr-Plausible-Magecart-Hunt-sources/. MITRE ATT&CK technique and mitigation identifiers and PCI DSS requirement numbers are cited by ID only.