---
title: "Malware vs malware: a ClickFix kit at war for your screen"
description: "Two malware kits were fighting for the same screen on the same hacked WordPress pages. One of them wrote the whole war into its code."
url: https://adamnet.works/blog/a-clickfix-loader-rewritten-to-get-past-a-rival-s-page-cleaner/
index: https://adamnet.works/llms.txt
---

[All Security Research](https://adamnet.works/blog/security-research.md)

Security Research · ClickFix · WordPress · OverlayWar · GuardFrame

# A ClickFix loader rewritten to get past a rival's page cleaner

Two ClickFix kits fought over the same screen on hacked WordPress sites. One wrote the fight into its code, in Russian, down to the moment the browser broke.

Kirk | ADAMnetworks • October 13, 2026 • Updated October 15, 2026

![Two fake CAPTCHA checkbox panels overlapping in a browser window, with arrows between them and a clipboard holding a command prompt below](https://adamnet.works/uploads/pasted-image-1791903001874.webp)

## Key takeaways

- A ClickFix kit we track as GuardFrame installs as a rogue WordPress plugin and was rewritten to get past a rival kit’s page cleaner. We captured it on 33 hacked sites between 24 September and 14 October 2026.
- The rival, which we track as OverlayWar, ships a cleaner that deletes any full-screen overlay without a trusted name. The cleaner ran on 526 sites between 7 September and 14 October 2026, beside a lure loaded through Polygon smart contracts.
- GuardFrame borrows two names the cleaner trusts, hides its panel one level below where the cleaner looks, and suppresses rival screens with styles only. Its first version, which deleted rivals, set off 3,372 page changes in a single page load.
- A code comment quotes an operator’s complaint about the overlay failing on first visits, and debug logging left on in every copy narrates the fight to the browser console.
- The overlay frames a Windows and macOS ClickFix lure from `27-april-tag[.]com`, branded with each hacked site’s own logo. It removes itself on any failure, so site owners see a working site while visitors get a lure they cannot close.

## Background

A ClickFix lure is a fake check, usually dressed as a CAPTCHA. It tells the visitor to open PowerShell or Terminal, paste a command, and press Enter. The command then fetches and runs the next stage. Only one lure can hold the screen, and that is the one whose command gets pasted.

Hacked pages often carry more than one kit. [Earlier research on derp.ca](https://www.derp.ca/research/ta2726-wordpress-malware-launchpads/) covering 1,509 compromised WordPress sites found one page carrying four separate ClickFix script blocks. The kits in this post compete for the same visitors on the same pages:

| Name | What it is | Seen |
| - | - | - |
| GuardFrame | A rogue WordPress plugin that injects `wp-security.js`, a “Security Guard” script, and an iframe holding a ClickFix lure | 33 sites, 24 September to 14 October 2026 |
| OverlayWar | A cleaner script, settings object `CFG_CONFIG`, that protects a ClickFix lure loaded through Polygon smart contracts | 526 sites, 7 September to 14 October 2026 |
| TimberCheck | The Windows and macOS lure that GuardFrame frames, served by `27-april-tag[.]com` | Inside GuardFrame’s iframe |

GuardFrame’s author studied the cleaner’s rules, found a way to pass as one of its own, and then wrote a guard to clear rival lures off the page. The code comments say the first version of that guard set off “a war of observers”. Figure 1 shows how GuardFrame passes each of the cleaner’s checks.

![Two columns of four boxes. Left: the OverlayWar cleaner’s trusted-name check, MutationObserver, 150 ms sweep, and click handler. Right: GuardFrame’s borrowed names, late class change, nested panel, and click pass-through, each answering the check beside it.](https://adamnet.works/uploads/clickfix-page-cleaner-figure-1-cleaner-checks-vs-guardframe.svg)

_Figure 1: OverlayWar’s cleaner checks and the GuardFrame technique that passes each one._

The author was writing for a customer. One comment quotes an operator’s complaint, translated from Russian: “first visit - empty, reload - it’s there”. The fix was to start the overlay as soon as the page body exists, instead of waiting for the page’s load event. Rival scripts on the same pages were holding that event back by 10 to 30 seconds on a first visit.

Everything below comes from page bodies and scripts collected by our scanners from hacked WordPress sites, matched against our YARA rules. Figure 2 shows the comments in which GuardFrame’s author describes the problem.

![Russian comments naming CFG_CONFIG and describing the cancelled iframe requests and the class-change workaround](https://adamnet.works/uploads/pasted-image-1791903043473.webp)

_Figure 2: Comments in the GuardFrame loader, collected 29 September 2026. Comments in every figure are translated from Russian beneath it._

```text
PRE-ARM: the positioning class (.wp-security-root - fixed, z, size) is NOT applied yet.

On rigquip, someone else's "cleaner" (CFG_CONFIG) checks EVERY added DOM node immediately (MutationObserver, microtask), and body.children every 150 ms (a sweep): "fixed + z >= 9990 + >= 90% of the screen" = "foreign overlay" -> display:none !important + removeChild.

The root, when added with its positioning class, was killed before the iframe finished navigating (in Network: canceled at 3 ms / 74 ms).

Therefore: a neutral class (display:none), a neutral wrapper (not a direct child of body - the sweep does not see it), and the class is applied in a macrotask AFTER their microtask. Their observer watches only childList; it is blind to class changes.
```

## Initial access: a rogue plugin built to survive caching

GuardFrame installs as a WordPress plugin with a randomized name in the pattern `wp-<word>-<word>-<4 characters>`, such as `wp-security-optimizer-bncm` or `wp-cache-shield-jv4t`. We have seen 25 names. The plugin adds four parts to every page:

- A stylesheet, `assets/css/wp-security.css`.
- An inline “Security Guard” script in the page head.
- An inline settings object in the footer, with a randomized global name.
- The loader, `assets/js/wp-security.js`, tagged `ver=1.2.2-` plus a four-character suffix.

The loader is 26,828 bytes and the guard 14,889 bytes. Both are identical on every site apart from the settings object’s name.

The page HTML holds only the static settings. The loader builds the iframe and generates a 16-character one-time token in the browser with `crypto.getRandomValues`. A comment explains why: full-page caches such as LiteSpeed would otherwise freeze one token into the cached page for every visitor. The iframe also carries the `data-no-lazy`, `data-skip-lazy`, and `data-lazyloaded` attributes, which lazy-loading plugins treat as instructions to leave an element alone.

The iframe points to `27-april-tag[.]com` and passes the token, the site’s host name, a timestamp, and the site’s own WordPress icon as the logo for the lure.

## Technical analysis

### OverlayWar’s cleaner deletes anything shaped like a full-screen overlay

The cleaner’s first check is whether an element carries a trusted name. It joins the element’s ID and class names into one string and matches that string against a short list. In most copies the list holds a random label from the cleaner’s own loading spinner, plus any name containing `cfm`. A separate skip pattern covers `media-modal`, `elementor`, and `adminbar`. Anything that matches is left alone regardless of its size. We have seen three spinner labels: `uS1j3xMa60p9uwhK`, `l98luimoo7724mcv`, and `ckrj3vw143eg5hip`.

Elements without a trusted name are measured instead. The cleaner acts on an element that is fixed to the browser window, carries a z-index of 9990 or more, and covers at least 90 percent of the window’s width and height. The z-index decides which of two overlapping elements is drawn on top. The cleaner removes the element’s mouse input, hides it with `display:none !important`, and deletes it from the page. The panel’s text is never examined.

These checks run from three places:

- A MutationObserver, the browser feature that notifies a script when the page changes, inspects each element as it is added.
- A timer re-scans the top level of the page body every 150 milliseconds until five seconds after load.
- A click handler inspects whatever the visitor clicks, along with every element enclosing it.

The observer and the click handler keep running for as long as the page is open. GuardFrame originally inserted its panel with the full-screen styling already applied, and the cleaner removed it before the embedded page finished loading. Figure 3 shows the cleaner’s rules.

![Cleanup configuration, trusted-name test, and full-screen panel size test](https://adamnet.works/uploads/pasted-image-1791903078774.webp)

_Figure 3: OverlayWar cleanup code collected 2 October 2026, with whitespace changed for readability. An [archived page in the Moncha repository](https://github.com/Thinaakar/moncha/blob/48a42dba4c9dba08f251010444b2e327473443a3/apps/worker/.data/evidence/tenant_moncha_internal/cmuko0dp7000zg5f4jt3cdjh1/dom-5befb6a67462abe3.html) preserves an exact copy._

The cleaner protects OverlayWar’s own lure. Its loader asks a smart contract on the Polygon blockchain for a list of delivery servers, decodes the reply, and fetches `/js/all.min.js?m=1` from one of them. The addresses come from the blockchain, and the script comes from an ordinary web server. The same spinner label appears in the cleaner’s settings and in this loader. The earliest page in our collection carrying both is `engetrina.com.br` on 7 September 2026 at 14:40 UTC, with the cleaner, the loader, the spinner label `uS1j3xMa60p9uwhK`, and the first contract on one page.

[Field Effect](https://fieldeffect.com/blog/clickfix-cluster-observed-activity-recent-campaigns) described a Polygon-backed loader fetching `/js/all.min.js` on 24 August 2026, and Unit 42’s [2 July JokerStat research](https://github.com/PaloAltoNetworks/Unit42-timely-threat-intel/blob/main/2026-07-02-ClickFix-campaign-utilizing-MaaS-kit-with-Blockchain-C2.txt) and [technical figures](https://www.linkedin.com/posts/clickfix-blockchain-ugcPost-7479981271008583680-Xyu8/) show the same `getServers` lookup.

Once downloaded, the script waits for a click, then sends browser, device, and page details to `/first_allow_host`, an endpoint [ANY.RUN’s 7 September ruleset changelog](https://www.linkedin.com/pulse/anyrun-suricata-ids-ruleset-changelog-27-any-run-n8dre) flags as TerminalFix-related. The server replies with an ID, a command, and a decision on whether to show the lure. An approved Windows visitor gets a full-screen panel with the ID `cfm`, and the page title changes to “Just a moment…”. Clicking the checkbox copies the command, and the instructions tell the visitor to open PowerShell or Terminal, paste, and press Enter. Every five seconds after that, the script asks `/check_allow_host` whether to keep the lure up, and a negative answer hides the panel and reloads the page. [Gelato’s 28 September analysis](https://gelato07.github.io/posts/EtherHiding-Blockchain-ClickFix/) documents this controller and the decoder for its delivery addresses.

The cleaner is tied to three Polygon contracts, created on [3 September](https://polygonscan.com/tx/0x29bc3254fe10678b7843c3d3b32e926fe9847594eb877f7b451dfe107cd3627f), [14 September](https://polygonscan.com/tx/0x907c29aadf6aec6ba1b4acecd1e055988333577bb5d73c024669d2ac341cfd33), and [21 September](https://polygonscan.com/tx/0x0469d69cd651f377f68d6e3ef354f0643f8d5e84d2c651b1d7e84df960500816). Each serves its own group of sites over overlapping dates:

| Contract | Sites with the cleaner | First seen |
| - | - | - |
| `0x91639826046fd209ef96c76cf9089e6dde2d408a` | 320 | 2026-09-07 |
| `0xb90658783fda2e726c5756f94a461291c0cef691` | 131 | 2026-09-16 |
| `0xa1cb583820efe60c840498aada3f741ab6869cbe` | 29 | 2026-09-22 |

The removal rules are identical across the three groups. Two retained verification scripts of 69,051 bytes each differ only in two copies of the spinner label.

### Twelve copies of the cleaner delete their own CAPTCHA

The cleaner’s exemptions vary between copies. Our scanners recorded it in 560 visits to 526 sites between 7 September and 14 October 2026. In 548 of those visits it trusts `cfm`. The other 12, on eight sites, keep only the spinner label and the skip pattern.

In those 12, the cleaner’s own lure meets every removal condition once it appears: the `cfm` panel is fixed, fills the window, and sits at a z-index of 2147483647. The earliest cleaner we hold, from 7 September, is this variant, and copies that trust `cfm` appear from 8 September. Both variants were still live on 12 October.

### Stage 1: GuardFrame borrows two names and hides its panel one level down

GuardFrame’s author dealt with the name check first. The panel’s class list includes `og-cfm`, which contains `cfm`, and `media-modal`, which is on the skip pattern. The panel is exempt in copies of the cleaner that trust `cfm` and in the 12 that do not.

The size check took more work, because each of the cleaner’s three checks sees a different moment of the panel’s arrival. The loader builds the panel, `wp-security-root`, inside a plain container, `wp-security-shell`, that has no positioning and no z-index. At that point the panel carries a neutral class and none of the full-screen styling. When the container is inserted, the 150-millisecond sweep and the observer both see only the plain shell, because neither looks inside an added element.

The full-screen classes are then applied on a zero-delay timer, after the observer has finished. A class change neither adds nor removes an element, so the observer stays quiet. The author’s note reads: “Their observer watches only childList; it is blind to class changes.”

The click handler needed one more step. Until the embedded page confirms it has loaded, the loader sets `pointer-events:none` on its iframe, so a click falls through to the site underneath and the cleaner, walking up from the clicked element, finds only the site’s own elements. After confirmation, the borrowed names cover the click handler as well.

The loader does not copy the spinner label because, as its comment says, that label is regenerated and “would give away the overlap.” The three labels in our captures bear this out. Figure 4 shows the shell, the delayed class change, and the borrowed names.

![Loader comments and code showing the plain wrapper, deferred positioning, and borrowed class names](https://adamnet.works/uploads/pasted-image-1791903116144.webp)

_Figure 4: GuardFrame loader code collected 29 September 2026._

```text
og-cfm: a safeguard against other people's "overlay killers" (CFG_CONFIG and similar): they treat any element whose id/class contains "cfm" as "their own." They will leave our root with this class alone.

Neutral wrapper: the other script's sweep iterates over body.children - the wrapper (position:static, no z) is not an overlay as far as it is concerned, and the sweep does not see the root inside it at all.

Macrotask: by this point, MutationObserver (their CFG_CONFIG) has already processed the "addition" of the neutral nodes (microtask) and found nothing foreign. Changing the class does not create a childList mutation.

We clear pointer-events on the iframe only in arm() - until the signal is confirmed, clicks must pass through to the site (see the comment above).

media-modal: a class from the other "cleaner's" skipRe (CFG_CONFIG.skip = "media-modal|elementor|adminbar", static).

isTrusted() checks id+class for a match against skipRe - our root becomes "one of theirs": the sweep, MutationObserver and click-capture handler no longer touch it.

We do not add the random trusted token (uS1j3xMa60p9uwhK) - it is regenerated and would give away the overlap.
```

### Stage 2: The guard hides rival panels by their shape

Getting past the cleaner left other fake CAPTCHAs on the same sites free to draw over GuardFrame’s panel. The guard script in the page head handles them. Its comments name the targets as foreign loading spinners, `cfm-*` CAPTCHAs, and other fake Cloudflare-style screens that cover or delete its panel.

The guard matches on shape. Any visible, non-empty panel fixed over at least 90 percent of the window in both directions qualifies if its z-index is 900 or more, a threshold far below the cleaner’s 9990. The guard’s own panel is exempt through its ID and a `data-og-self` marker that also covers everything inside it.

One comment gives an example: `interseq[.]at` with the marker `data-cfmd-instr`, which [PhishEye’s 15 September investigation](https://phisheye.com/blog/clickfix-bun-runtime-fileless-loader) documented along with `interseqf[.]com` serving fake-verification JavaScript. That component draws its screen inside a shadow DOM, a block of page elements sealed inside one host element, and hiding the host hides everything within it. `interseq[.]at` was already on a page beside the OverlayWar cleaner on 8 September. A third injection with its own Polygon contract, `0xb29927b7d854b232d5e58cfc201866376e68c676`, appears on 14 of the cleaner’s sites, so at least three kits compete on these pages. Figure 5 shows the guard’s targeting rules.

![Guard comments describing foreign fake CAPTCHAs and the interseq example](https://adamnet.works/uploads/pasted-image-1791903169328.webp)

_Figure 5: Comments in the GuardFrame guard, collected 30 September 2026, naming the competing screens it suppresses._

```text
Whether the element is a foreign full-screen fixed overlay.

Criteria: position:fixed, z-index >= 900, visible area >= 90% of the viewport on both axes, not empty (has content), and not ours.

Foreign anti-bot overlays (their spinner and the foreign CAPTCHA with cfm-* classes) meet these criteria and are suppressed: these are precisely what we hide. Our own overlay is protected separately - by its id and the data-og-self attribute (isSelf), rather than by the criteria.

Gentle suppression of foreign full-screen layers: styles only, without removeChild. Called only by the timer (boot, 3-second interval, 30-second final pass) - NOT in the MutationObserver callback.

Synchronous mutations in the callback trigger a cascade with the other observer (a war, 3372 mutations on a live site).

A foreign host with a shadow DOM (for example interseq.at data-cfmd-instr) meets the isForeignOverlay criteria: fixed + z MAX_INT + >= 90% of the viewport + non-empty; display:none on it also hides its shadow-DOM contents.
```

### Stage 3: Deleting rivals set off 3,372 page changes

Deleting an element is itself a page change, and other scripts’ observers react to it. The guard’s header says an earlier version, which deleted rival panels, woke the competing observers and produced 3,372 page changes in a single page load on a live site. The comments call this a “war of observers.”

The same loop ran from the other side when the guard put its own panel back inside its observer callback, which “instantly woke the other observer.” The two scripts then cut each other down within a single tick, and the page never got a chance to redraw. The author sums up the result as “the browser broke.”

From version 1.2.3, the guard’s header says, `killForeign()` only sets styles. Every deployed copy is still tagged `1.2.2` in the plugin’s file version, so the guard’s own version number runs ahead of the plugin’s. The function applies `display:none` to hide the panel and `pointer-events:none` to block clicks, both with `!important`, and never calls `removeChild()`. The visitor sees the rival screen vanish, and the rival’s observer, which watches only for added and removed elements, has nothing to react to.

The guard’s own work moved onto timers at the same time: once at start, every three seconds after that, and once more at 30 seconds. A comment says the interval used to be one second. Re-adding its own panel now waits 300 milliseconds on a timer, with a 400-millisecond cooldown. Figure 6 shows the suppression function.

![The guard’s style-only suppression function and its author’s note on the war of observers](https://adamnet.works/uploads/pasted-image-1791903196130.webp)

_Figure 6: The GuardFrame guard’s suppression function, collected 30 September 2026._

```text
Gentle suppression: styles only, NO removeChild. removeChild wakes up the other MutationObservers (childList) - a war of observers (3372 mutations on a live site). Changing styles wakes only observers watching the style attribute, and the other script's sweep generally does not maintain those. For a foreign host with a shadow DOM, display:none on the light-DOM host hides the contents too - that is enough.
```

All 77 distinct page bodies we hold with the guard, from 34 visits, use the style-only design. The deletion-based version survives only in the comments.

### Stage 4: The guard props its own panel back up on every pass

Each pass forces the guard’s panel back to full screen with `display:flex`, a fixed position, and a z-index of 2147483647. While the panel is waiting or active, the pass also restores its visibility and opacity, and turns mouse input back on when the panel is meant to block the page. The comment explains the display reset: “the other killer sets display:none !important, and neither visibility nor opacity can override it.”

The guard also moves its shell to the end of the page body on every pass. When two panels share a z-index, the one later in the page is drawn on top, and the comment names interseq’s host, which loads late, as the competitor. Figure 7 shows the restoration code.

![The guard marking its own panel, moving its shell last, and restoring display and position](https://adamnet.works/uploads/pasted-image-1791903222550.webp)

_Figure 7: The GuardFrame guard’s restoration code, collected 30 September 2026._

```text
Always reapply display:flex: the other killer sets display:none !important, and neither visibility nor opacity can override it.
```

### Stage 5: Its recovery code watches the wrong level

If another script deletes the panel, the guard is supposed to put it back. Its observer watches only the top level of the page body and checks each removed element for the ID `wp-security-root`. Live pages record the panel one level down, at `body > div.wp-security-shell > div#wp-security-root`. Figure 8 shows a page record from 30 September with the panel inside the shell, carrying the borrowed names, the `data-og-self` marker, and the forced styles.

![Saved page tags showing wp-security-root nested inside wp-security-shell, with borrowed names and forced full-screen styles](https://adamnet.works/uploads/pasted-image-1791903280151.webp)

_Figure 8: Page structure recorded 30 September 2026, with the opening tags wrapped for readability._

If the panel itself is deleted, the observer, watching one level too high, never hears about it. If the shell is deleted instead, the observer sees a removal with the wrong ID. If the observer did fire, it would put the panel back directly under the page body, outside the shell that hides it from the cleaner’s sweep.

The file’s own comments disagree on the layout. The observer setup says the panel is “a direct child of body,” while the shell-moving code places it inside the shell and relies on that mismatch, because moving the shell registers as a removal with a different ID and so “the cascade will not start.” The wrapper that gets the panel past the cleaner also stops the guard from reacting to its own moves, and it leaves the recovery code watching a spot the panel never occupies.

### Stage 6: The framed lure serves Windows and macOS commands

The page inside GuardFrame’s iframe is a second ClickFix lure, TimberCheck, titled “Just a moment…” with one command for Windows and one for macOS. `27-april-tag[.]com` returns an empty 403 response to a direct request and serves the lure only inside the overlay, with a fresh token and the hacked site’s name and logo. The page was rebuilt in early October, from 152 KB on 30 September to 63 KB from 5 October, and kept both commands.

Clicking its checkbox copies a command, and a copy handler substitutes that command whenever the visitor copies anything else on the page. The Windows command fetches and runs remote PowerShell. The macOS command, once its nested encodings are stripped, installs a LaunchAgent configured to start on load and keep running, then reads a Polygon contract for an address and pulls more AppleScript from it. That contract, `0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0`, is the one [Have I Been Squatted documented on 6 July](https://haveibeensquatted.com/blog/from-typosquatting-to-macos-backdoor-clickfix-blockchain-c2), and [UnderDefense](https://underdefense.com/blog/inside-a-clickfix-attack-how-veilovpn-hid-its-command-server-inside-a-polygon-smart-contract/) and [Bolster](https://bolster.ai/blog/fake-captcha-real-malware-macos) have since traced the same macOS chain arriving through other delivery routes.

The lure and the loader talk through `postMessage` messages keyed `__overlayGate`, each carrying the token and a status, and accepted only from `https://27-april-tag[.]com`. The statuses `ready` and `alive` switch the overlay on. `empty` counts as a failure and removes it, and `close` removes it. This branch has its own command to keep on screen and runs separately from OverlayWar’s Polygon-loaded `cfm` controller.

### Stage 7: Built to fail quietly and hold the visitor

The overlay starts inactive, and any failure removes it “rather than blocking the site”. Numbered fallbacks in the code cover three cases: the embedded page fails to answer within 10 seconds, the iframe fails to load within eight, or the whole process passes 15. A visitor who fails twice within an hour is marked with a cookie and left alone for 60 minutes. The cookie names default to `og_fail` and `og_suppress` and are randomized per site. A broken page would draw the site owner’s attention. A lure that quietly skips a visitor draws none.

Once the lure is showing, the visitor cannot dismiss it. The settings disable closing with Escape and clicking outside the panel, and the page’s scrolling is locked. A handler for the browser’s back-forward cache restarts the overlay with a new token and clears the failure cookies. The comment says the CAPTCHA must be impossible to get rid of “by refreshing or by history navigation”.

Figure 9 shows the overlay’s life cycle.

![A flow from start inactive, to loading the lure, to waiting for the lure’s handshake, to armed. Failure paths from loading and waiting lead to a box listing the 8, 10, and 15 second limits and the 60-minute skip. A back or forward navigation from the armed state restarts the overlay.](https://adamnet.works/uploads/clickfix-page-cleaner-figure-9-guardframe-overlay-lifecycle.svg)

_Figure 9: The GuardFrame overlay’s life cycle. Every failure removes the overlay, and a confirmed lure cannot be dismissed._

The operator keeps an exit. A `panicKey` setting lets Ctrl+Shift+Q remove the overlay, and `window.overlayGateDismiss()` does the same.

### Stage 8: Debug logging narrates the fight to the console

Every copy we collected ships with `"debug":true`. A visitor who opens the browser console on an infected page watches the fight in Russian, in lines prefixed `[Security]` and `[Guard]`. They report the content confirmed with the overlay active and a foreign overlay displaced with styles. Figure 10 shows a run of the collected scripts.

![Browser console lines prefixed Security and Guard: the loader blocks the site, frames the lure, receives its ready handshake and confirms the overlay, then the guard displaces a rival cfm-overlay and the lure keeps sending alive messages until the guard stops watching at 30 seconds](https://adamnet.works/uploads/clickfix-page-cleaner-figure-10-guardframe-debug-console.webp)

_Figure 10: Console output from the GuardFrame loader and guard collected from soleresult[.]com, run in an isolated test page with the network blocked, a local stand-in for the lure’s handshake, and a stand-in rival overlay. Repeated `alive` messages are grouped._

```text
[Security] markers: og_fail=0, suppression=no
[Security] blocking the site while content loads
[Security] iframe started: https://27-april-tag.com/?og_token=...
[Security] waiting for a signal from the page, limit 10000 ms
[Guard] protection active
[Security] iframe loaded, waiting for a signal
[Security] message received: https://27-april-tag.com {"__overlayGate":1,"token":"...","status":"ready"}
[Security] content confirmed, overlay active
[Guard] foreign overlay displaced (styles): cfm-overlay
[Guard] observation finished (30 s)
```

Other traces of the author show through. The iframe’s title and the dialog’s accessible label are the Russian phrase for “Additional content”, which screen readers announce on English-language sites. The guard’s header says its functions are written as plain declarations so they can be checked in Node without a browser, in a test script named `test_guard.js`.

## Infrastructure

The hacked sites carrying GuardFrame include technology, news, casino, and Turkish construction company sites. A public scan recorded the loader on `soleresult.com` on 23 September 2026, and our earliest captures came the next day, from `soleresult.com`, `technologymiles.com`, `tradingriser.com`, and `technofee.com`. Across 24 September to 14 October, the loader appeared in 26 visits to 25 sites and the guard in 34 visits to 33 sites.

`27-april-tag[.]com` served three builds of the framed lure, on 30 September, 5 to 8 October on five sites, and 13 October.

OverlayWar’s `cfm` controllers answer at `/first_allow_host`. `humanwebpage[.]online` and `webpagesafe[.]online` were still active on 15 October 2026, along with `interseq[.]at`.

## Detection opportunities

The plugin and page markers below are stable across every GuardFrame copy we hold:

- A plugin directory matching `wp-<word>-<word>-<4 characters>` that serves `assets/js/wp-security.js?ver=1.2.2-<suffix>` (26,828 bytes) and `assets/css/wp-security.css`.
- Inline elements `<style id="wp-security-inline-css">` and `<script id="wp-security-js-before">` holding a settings object with a `contentUrl` field.
- The page structure `div.wp-security-shell > div#wp-security-root` with the classes `og-cfm` and `media-modal` and the attribute `data-og-self`.
- Browser console lines prefixed `[Security]` and `[Guard]`.
- Window messages keyed `__overlayGate`, and cookies named `og_fail` and `og_suppress`.

OverlayWar’s cleaner is identifiable by a `CFG_CONFIG` settings object with the skip pattern `media-modal|elementor|adminbar` beside a Polygon `getServers` loader that fetches `/js/all.min.js?m=1`.

For site owners, a plugin directory in the GuardFrame naming pattern that you did not install means the site is compromised.

## Indicators of compromise (IOCs)

### Network indicators

| Indicator | Type | Context |
| - | - | - |
| `27-april-tag[.]com` | Domain | GuardFrame content host serving the TimberCheck lure, last seen 13 October 2026 |
| `humanwebpage[.]online` | Domain | OverlayWar `cfm` controller (`/first_allow_host`), last seen 15 October 2026 |
| `webpagesafe[.]online` | Domain | OverlayWar `cfm` controller, last seen 15 October 2026 |
| `simplecheck[.]online` | Domain | OverlayWar `cfm` controller, last seen 29 September 2026 |
| `webpagehelp[.]online` | Domain | OverlayWar `cfm` controller, last seen 29 September 2026 |
| `hi-tuffglass[.]com` | Domain | OverlayWar `cfm` controller, last seen 3 October 2026 |
| `interseq[.]at` | Domain | Rival fake CAPTCHA named in the GuardFrame guard, last seen 15 October 2026 |
| `interseqf[.]com` | Domain | Rival fake CAPTCHA, last seen 24 September 2026 |

### Polygon contracts

| Indicator | Type | Context |
| - | - | - |
| `0x91639826046fd209ef96c76cf9089e6dde2d408a` | Contract | OverlayWar loader, 320 sites, last seen 14 October 2026 |
| `0xb90658783fda2e726c5756f94a461291c0cef691` | Contract | OverlayWar loader, 131 sites, last seen 13 October 2026 |
| `0xa1cb583820efe60c840498aada3f741ab6869cbe` | Contract | OverlayWar loader, 29 sites, last seen 9 October 2026 |
| `0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0` | Contract | macOS stage address lookup in the TimberCheck command |

### Host artifacts

| Indicator | Type | Context |
| - | - | - |
| `wp-content/plugins/<name>/assets/js/wp-security.js?ver=1.2.2-<suffix>` | File path | GuardFrame loader |
| `wp-security-optimizer-bncm`, `wp-cache-shield-jv4t`, `wp-kit-security-n4a4`, `wp-guard-core-gdnm`, `wp-kit-helper-zspg` | Plugin names | Examples from 25 GuardFrame plugin names seen |
| `wp-security-root`, `wp-security-shell`, `data-og-self` | Page markup | GuardFrame overlay |
| `__overlayGate` | postMessage key | GuardFrame and TimberCheck handshake |

## MITRE ATT\&CK mapping

| Tactic | Technique ID | Technique | Observed behavior |
| - | - | - | - |
| Resource Development | T1584.004 | Compromise Infrastructure: Server | Rogue plugins installed on hacked WordPress sites |
| Initial Access | T1189 | Drive-by Compromise | Overlay and lure injected into pages visitors browse to |
| Execution | T1204.004 | User Execution: Malicious Copy and Paste | Visitor pastes the copied command into PowerShell or Terminal |
| Execution | T1059.001 | Command and Scripting Interpreter: PowerShell | Windows command fetches and runs remote PowerShell |
| Execution | T1059.002 | Command and Scripting Interpreter: AppleScript | macOS stage pulls and runs AppleScript |
| Persistence | T1543.001 | Create or Modify System Process: Launch Agent | macOS command installs a LaunchAgent that starts on load |
| Command and Control | T1102.001 | Web Service: Dead Drop Resolver | Polygon contracts return delivery server addresses |

## Every stage still needs a network connection

Whichever panel ends up on top, the scripts still have to load from delivery servers, OverlayWar’s branch still has to reach its controller for a command, GuardFrame’s iframe still has to reach `27-april-tag[.]com`, and the pasted command still has to download its payload.

ADAMnetworks’ [adam:ONE](https://adamnet.works/how-it-works.md) applies Don’t Talk To Strangers (DTTS), which denies IP connections by default unless the destination was first requested through DNS and allowed by policy. Loader, controller, lure, and payload connections to disallowed destinations fail under that rule, whatever the page shows.

The [ClickNix browser extension](https://support.adamnet.works/t/clicknix-browser-extension/1664) checks page HTML locally against YARA rules for ClickFix content, and its rules match all 26 GuardFrame loader captures and the framed lure page. ClickNix inspects the page in the browser, and DTTS covers the connections that follow it. Our [PasteSwitch investigation](https://adamnet.works/blog/hbo-max-ads-exposed-the-pasteswitch-clickfix-operation.md) followed one lure from the browser to the command. This post covers the step before that, where kits compete over whose instructions the visitor reads.

![Kirk Bio Picture](https://adamnet.works/uploads/pasted-image-1791906333143.webp)

About the author

## Kirk | ADAMnetworks

Security Researcher

Kirk is a security researcher known for reverse engineering of ransomware, malware and web threats.

[Author profile](https://www.linkedin.com/in/matt-kirkland/)

## Related reading

Security Research

### [HBO Max ads exposed the PasteSwitch ClickFix operation](https://adamnet.works/blog/hbo-max-ads-exposed-the-pasteswitch-clickfix-operation.md)

September 13, 2026

Security Research

### [Brevo delivered ClickFix through its own infrastructure](https://adamnet.works/blog/brevo-delivers-kongtuke-clickfix-to-customer-sites.md)

September 16, 2026

Podcast

### [Inside DNS Threat Intelligence: Privacy, Security & Innovation](https://adamnet.works/podcast/tdl-009-inside-dns-threat-intelligence-privacy-security-innovation.md)

November 14, 2025

## 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.

[Request a Demo](https://adamnet.works/contact.md) [How adam:ONE® Works](https://adamnet.works/how-it-works.md)
