Skip to content
16 min read Houdin Research

Detecting ClickFix infrastructure before it goes live

In this article we point out weaknesses in a live ClickFix operator's infrastructure. We showcase a method to preemptively detect threats by monitoring certain points of it.

clickfixthreat-huntingdead-drop-resolveretherhiding

In a recent campaign, we observed threat actors using ClickFix to deliver malware to visitors of compromised WordPress sites. This article presents a method for detecting the malicious landing pages proactively, by monitoring the attackers’ infrastructure in the middle of the delivery chain.

About ClickFix

ClickFix is a lure. The visitor sees a page that looks like a Cloudflare or reCAPTCHA check and is told to paste a command into the Windows Run dialog to prove they are human. The command is already in the clipboard, and running it fetches the malware.

Figure 1 The lure, as staged in the operator's repository
cf-main.js, rendered offline
Fake Cloudflare challenge: One more step, verify you are human, a checkbox with the Cloudflare logo and a Ray ID
CF-win-r.png, shown after the click
Instruction panel: press and hold the Windows key and R, press Ctrl and V in the verification window, press Enter

Someone visiting a compromised website reaches the lure page through the chain in Figure 2.
A script injected into the compromised site, the loader, opens a page from a domain the threat actor controls, the landing page. The landing page displays the lure and fills the clipboard with a malicious command.

The loader does not contain the landing page’s address. When it runs, it asks a third party for the address of the landing page currently in use, which lets the operator move to a new domain without editing the compromised sites again. We call that third party the resolver. In the campaign examined below, the loader reaches it in two steps: the only address it stores is that of a config server, and the config server answers with the location of the resolver, a file on GitHub.

The resolver is the part of the chain we will monitor, for two reasons:

  1. A new landing page has to be written to the resolver before any visitor can reach it.
  2. The loader reads the resolver from a browser without any credentials, so a defender can read it the same way.
Figure 2 The delivery chain, and where to watch it
Monitored hereCompromisedWordPress siteInjected script(the loader)Config serverResolverLanding pageClipboardcommandPayloadAnswers with the address of the current landing pageGitHub filePolygon contract
The loader stores the config server's address, not the landing page's, so every change of landing page is published to the resolver first. On the compromised page examined below, the resolver is a one-line text file in a public GitHub repository. A second loader on the same page stores the address of a smart contract on Polygon and reads it directly, without a config server.

Case study

A good example is martyhigh[.]com, a compromised WordPress site that was detected on 25 July 2026. The website has been compromised multiple times and carries 12 injected ClickFix loaders:

  • 8 copies of a loader with the landing page’s address hardcoded
  • 3 copies of a loader leveraging a GitHub-based resolver
  • 1 EtherHiding loader leveraging a Polygon blockchain resolver

Code and infrastructure similarity suggests that the hardcoded and GitHub-based loaders belong to the same threat actor, as different builds of the same tool.
The EtherHiding loader was seen alongside this GitHub-based loader in 55 prior occurrences, so it likely belongs to the same group.

The GitHub-based resolver is the one that delivered the lure in the 25 July capture.
The EtherHiding resolver is examined later in the article, along with what monitoring each one yields.

Figure 3 The chain recorded on martyhigh[.]com, 25 July 2026
Injected loader
martyhigh[.]com, inline script
asks
Config server
klonfcrtyseaflow[.]com/inline.php
answers om5678774/kt4567/main/land2.txt
Resolver
raw.githubusercontent[.]com/om5678774/kt4567/main/land2.txt
answers chrisparrycollection[.]com
Landing page
chrisparrycollection[.]com full-viewport iframe
loads white.html, then one script
Lure gate
sailboaterists[.]net//host/jquery.js?v=8 triggers only on Windows
Each answer is plain text substituted into the next request. Only the config server's address is stored in the loader; everything after it is fetched when the page loads.

The lure script then draws the fake verification page in Figure 1 and replaces the clipboard with a hidden-window PowerShell command, which downloads the final payload from nationwidely[.]net, hosted on the same IP address as the gate.

The GitHub resolver

The public repository at hxxps://github[.]com/om5678774/kt4567, created on 15 February 2026, serves as the resolver for this attack chain. It contains a goldmine of information that defenders can use to detect threats proactively.

The repository holds the lure widgets of Figure 1, in one directory per landing page, and has a fork, created in late July by a second account, probably kept as a backup.

Figure 4 Root of the repository, 2 September 2026
GitHub file listing of om5678774/kt4567, reduced to six directories named after landing hosts, four PNG instruction panels, cf-main.js, re-main.js and the pointer files land2.txt, landing.txt and landing2.txtLanding page directoriesone per landing host, plus arandom path segment; each holdsthe six widget filesWidget imagesthe instruction panels ofFigure 1, Cloudflare andreCAPTCHA variantsWidget scriptscf-main.js draws the fakeCloudflare check of Figure 1,re-main.js the reCAPTCHA lookPointer filesthe three named by the configservers; each holds the addressof the current landing page
Screenshot of the repository root, reduced to the entries discussed here: 18 older pointer files are left out and re-main.js is shown next to cf-main.js. The three pointer files are the ones the config servers name in the captures. Every directory holds a copy of the six widget files.

As shown in Figure 5, the .txt pointer files are updated regularly to move traffic between landing pages.

Figure 5 Values of landing.txt, February to September 2026
FebMarAprMayJunJulAugSepasionamedrafter[.]com×799 dasionamedrafter[.]com, 17 Feb to 20 Feb, 3 daysasionamedrafter[.]com, 24 Mar to 14 Apr, 21 daysasionamedrafter[.]com, 20 Apr to 21 Apr, 1 dayasionamedrafter[.]com, 22 Apr to 28 Apr, 6 daysasionamedrafter[.]com, 29 Apr to 18 May, 19 daysasionamedrafter[.]com, 11 Jun to 13 Jul, 32 daysasionamedrafter[.]com, 28 Jul to 14 Aug, 17 daysardotcharleybuking[.]com×228 dardotcharleybuking[.]com, 20 Feb to 19 Mar, 27 daysardotcharleybuking[.]com, 28 Apr to 29 Apr, 1 dayclientpilotclosers[.]com1 dclientpilotclosers[.]com, 14 Apr to 15 Apr, 1 daychickpeachix[.]com×26 dchickpeachix[.]com, 15 Apr to 20 Apr, 5 dayschickpeachix[.]com, 21 Apr to 22 Apr, 1 dayfoliclesalonandspa[.]com<1 dfoliclesalonandspa[.]com, 28 Apr to 28 Apr, hoursasioklaydpory[.]com24 dasioklaydpory[.]com, 18 May to 11 Jun, 24 dayschrisparrycollection[.]com15 dchrisparrycollection[.]com, 13 Jul to 28 Jul, 15 daysonl-novo-co[.]digital11 donl-novo-co[.]digital, 14 Aug to 25 Aug, 11 daysasdoci[.]digital6 dasdoci[.]digital, 25 Aug to 31 Aug, 6 daysregisteredagentsingeorgia[.]comCurrentregisteredagentsingeorgia[.]com, 31 Aug to 2 Sep, current
Commit dates from the repository's history. asionamedrafter[.]com is the value the file returns to after every other host.

The investigation uncovered several other active GitHub resolvers operating the same way. They are listed in the Indicators section.

The EtherHiding resolver

EtherHiding stores the next address in a smart contract on a public blockchain. The loader reads it with an eth_call through a public RPC endpoint, and the operator changes it with a transaction. The loader on martyhigh[.]com reads a contract at 0xf5966808a9ECbdb8794F568922809C52b0Fd2446 on Polygon.

In this case the rest of the chain differs slightly: the contract does not name a landing page directly. It names further config servers, from which the scripts for the next steps are fetched.

From June onward, every list written to the contract was scanned publicly within 3 hours of the write, and most within the hour. The technique is relatively new and has received wide attention, so the contract is already watched and reading it yields a shorter lead.

GitHubEtherHiding
Changing a pointer fileA commit. Free.A transaction. Costs gas.
Writes observed22 to landing.txt, February to September, still changing23, 21 May to 1 July, none since
Lead before first public sighting3 to 83 h< 3 h from June on
What the record carriesEvery version, author, timestamp, pushing toolSender address, timestamp, value
State in the 25 July captureDelivered the lureBranch failing; the last 3 servers resolve to one address that does not answer

Advantages of monitoring resolvers

Monitoring the pointer files lets a defender see each new landing page as soon as it is published.

For both the GitHub repository and the Polygon contract, Figure 6 shows the lead between each write and the first public scan of the landing page it names.

Figure 6 Lead time on both resolvers, per write
1 week1 day6 h1 h6 minFirst public sightingWritten this long before itRepositories, GitHubasdoci[.]digital3.5 ddasifoda[.]digital2.4 dloc-calixablanlk[.]digital2.0 dipoweres[.]com27 honl-novo-co[.]digital3.2 hregisteredagentsingeorgia[.]comnot seen yetregisteredagentsinkansas[.]comnot seen yetregisteredagentsindelaware[.]comnot seen yetContract, Polygonexactupdate[.]com +25.1 dautoupdatet[.]com +236 hupdateocean[.]com16 h13 writes, 8 Jun to 1 Jul< 3 h
Time between the write naming a new landing page and its first public scan. Open bars have no public scan as of 2 September.

GitHub-based resolvers also demand more care from the operator, for OpSec reasons that are not detailed here.

Pivoting on resolver files

The method above finds landing pages in advance. It does not yet find the compromised WordPress sites that send visitors to them.

Searching public scan data for the websites that load each landing page finds more infrastructure.
Each compromised website can reveal new loader versions, new config servers, and new resolvers.

Repeating the pivot on every new discovery maps a large part of the infrastructure. Figure 7 shows the loop.

Figure 7 From the file to the chain and back
namesthe landing pagesearch scansfor sites that load iteach callsits config serveranswers withthe path of a filePointer fileone line, publicLanding pagethe lure hostCompromised sitescarry the loaderConfig serverin the loaderrepeat
Clockwise from the top. The search step queries public scan data for the sites that load the landing page. The path the config server answers with is either the file already watched or a new one to add, and the loop runs again from it.

Detection and hunting

In this case study, the GitHub loader is easy to detect on compromised websites.
It carries several unique strings that survive minification and host rotation: the symbol registry key __inline_id_offer__, used for deduplication, and the ktl-show-original and ktl-frame-ready messages each landing page posts back to the loader.

// Excerpt of the loader on martyhigh[.]com, 25 July capture.
var TRUSTED_CONFIGS = [
  { template: "hxxps://raw.githubusercontent[.]com/{id}", useFetch: true }  // {id} = path from the config server
];

var GLOBAL_KEY = (typeof Symbol === "function" && Symbol.for)
  ? Symbol.for("__inline_id_offer__")   // one registry per page, a second copy of the loader exits here
  : "__inline_id_offer__";

window.addEventListener("message", function (event) {
  var data = event && event.data;
  if (data.type === "ktl-show-original") { registry.destroy(); return; }  // landing page asks to drop the iframe
  if (data.type === "ktl-frame-ready")   { registry.reveal();  return; }  // landing page is drawn, show it
  if (data.type === "copy") tryCopy(data.text);                           // clipboard relay
});

On the chain side, a page issuing an eth_call against 0xf5966808a9ECbdb8794F568922809C52b0Fd2446 is reading this operator’s contract, whatever the page looks like.

On an endpoint, the pasted command imports Microsoft.PowerShell.Utility through the wildcard M*.P*.U* and picks its cmdlets by index in the module’s export list. Both are specific to this threat actor.

On GitHub, the tool that stages the lure files commits with the message prefix land CDN:, six commits per batch, one to two seconds apart. A commit search on that prefix allowed us to find more resolvers. Every lure directory ships the same widget scripts, cf-main.js and re-main.js, which call back into __landShotActivate, and the hardcoded loader registers under the symbol __03984783989377444__.

The GitHub loader in a page clickfix_github_dead_drop_loader
rule clickfix_github_dead_drop_loader
{
    meta:
        description = "Injected loader resolving a ClickFix landing page through a config server and a raw GitHub pointer file"
        reference   = "houdin.io/blog/detecting-clickfix-infrastructure-before-it-goes-live"
        date        = "2026-09-03"

    strings:
        // Symbol registry key the loader dedups against
        $key       = "__inline_id_offer__"
        // postMessage protocol between the landing page and the loader
        $msg_show  = "ktl-show-original"
        $msg_ready = "ktl-frame-ready"

    condition:
        any of them
}
The lure widgets, on GitHub or in a landing page clickfix_dead_drop_lure_widget
rule clickfix_dead_drop_lure_widget
{
    meta:
        description = "Fake Cloudflare and reCAPTCHA checkbox widgets (cf-main.js, re-main.js) staged on GitHub for ClickFix landing pages"
        reference   = "houdin.io/blog/detecting-clickfix-infrastructure-before-it-goes-live"
        date        = "2026-09-03"

    strings:
        // Callback into the landing page when the checkbox is clicked
        $callback = "__landShotActivate"
        // Mount attribute and globals of the two widgets
        $mount    = "data-land-mount"
        $cf       = "LandCfMain"
        $re       = "LandReMain"

    condition:
        $callback or ($mount and ($cf or $re))
}
Pasted command on an endpoint clickfix_dead_drop_pasted_command
rule clickfix_dead_drop_pasted_command
{
    meta:
        description = "Clipboard command served by the sailboaterists gate: cmdlets resolved by index into Microsoft.PowerShell.Utility"
        date        = "2026-09-03"

    strings:
        // Wildcard import of the Utility module
        $ipmo = "IPMO 'M*.P*.U*'" ascii nocase
        // Cmdlets picked by position in the module's export list
        $idx  = ".ExportedCommands.Values" ascii nocase
        $pick = /\$[A-Za-z]+\[5[12]\]/ ascii

    condition:
        $ipmo or ($idx and $pick)
}

Indicators

Indicators of compromise 46
Config servers 5
klonfcrtyseaflow[.]com
dakiloifhsnuukka[.]com
remiumholdings[.]com
feiyuwei[.]com
rosrefurboss[.]com
GitHub resolver 5
hxxps://github[.]com/om5678774/kt4567
hxxps://github[.]com/rincom316-316/kt4567
hxxps://github[.]com/habitredso98/main
hxxps://raw.githubusercontent[.]com/om5678774/kt4567/main/land2.txt
hxxps://raw.githubusercontent[.]com/om5678774/kt4567/main/landing.txt
Landing pages 19
registeredagentsingeorgia[.]com
registeredagentsinkansas[.]com
registeredagentsindelaware[.]com
catholicsma[.]com
ipoweres[.]com
dasifoda[.]digital
asdoci[.]digital
onl-novo-co[.]digital
chrisparrycollection[.]com
asionamedrafter[.]com
wolfenbard[.]com
ardotcharleybuking[.]com
asioklaydpory[.]com
chickpeachix[.]com
clientpilotclosers[.]com
foliclesalonandspa[.]com
burutrading[.]com
loc-calixablanlk[.]digital
so-unltd-ulcbi[.]digital
Gate, payload and C2 4
sailboaterists[.]net
nationwidely[.]net
framework-css-styles-js[.]beer
thewrayforward[.]com
Polygon resolver 5
0xf5966808a9ECbdb8794F568922809C52b0Fd2446
0xa70B18ebb5C1cF9Ee533A019975F09Aed4d276b5
waysmakeyourlifebetter[.]com
goodpersonofourcentury[.]com
besthappyfamily[.]com
IP addresses 5
34.196.13[.]28
91.92.241[.]13
158.94.208[.]213
88.80.150[.]25
54.38.220[.]85
Operator identities 3
asionamedraft[@]proton.me
habitredso98[@]atomicmail.io
faridovich2008[@]yandex.ru

Houdin turns any of the observables above, IP, URL, domain or file, into a verdict in about 60 seconds, with every pivot documented. See how it works.

← All posts