§ CM

FileFix Spawns Its Shell From the Browser’s File Dialog. The Process Lineage Outlasts Content Signatures

In the canonical Chromium FileFix flow, a browser-named utility process is the direct parent of powershell.exe. That relationship is the most durable core of FileFix detection, and it has held up against the content tricks the operators have thrown at it since mr.d0x published the technique in June 2025: the whitespace padding that replaced the # comment, the PowerShell body stuffed into a JPG by way of steganography. FileFix executes its command out of the File Explorer open-file dialog. On Chromium browsers that dialog is serviced by a browser-named utility process — not a separate explorer.exe, the way ClickFix’s Run dialog is — so the shell it launches comes back parented to msedge.exe or chrome.exe. A browser file-picker spawning a hidden PowerShell process should be rare and high-suspicion after local baselining, and it is the signal content-based rules keep failing to hold. Treat it as the anchor of a small family of signals — file-picker parent, suspicious descendants, and the registry artifact the paste can leave behind — rather than one immutable edge.

TL;DR

  • FileFix runs its PowerShell out of the browser’s file-open dialog, so the shell comes back parented to chrome.exe/msedge.exe — a browser-to-interpreter edge that should be rare and high-suspicion after local baselining, and that payload rewrites don’t remove without changing the execution path.
  • It has been used across separately reported campaigns: the KongTuke/Interlock cluster, whose PHP-RAT delivery shifted from Run-dialog ClickFix to FileFix in mid-2025, and a separate Meta-support lure dropping StealC, which defeated #-based rules with space-padded commands and hid its second stage inside JPGs via steganography.
  • Detect it in Sysmon EID 1 or Defender DeviceProcessEvents by alerting on a browser parent — best, a Chromium utility process matching the file-picker context — spawning powershell/cmd/mshta/wscript/curl/msiexec/rundll32/certutil or a downloaded EXE, and keep command-line content as high-value corroboration, not the gate: the padding hides the command from the address bar, not from the process command line.
  • Back the live rule with the TypedPaths registry artifact the paste can leave, baseline before you page (Elastic documents signed installer/diagnostic workflows as the real false positives), and validate your WebView2 and VDI edges rather than assuming them.
  • The prevention that bites is App Control (WDAC) script enforcement — which forces untrusted PowerShell into Constrained Language Mode, restricting the arbitrary .NET access, most COM creation, and dynamically evaluated code several observed FileFix cradles rely on — plus removing mshta/WSH; it’s one enforcement layer, not a complete fix (Invoke-WebRequest stays available and FileFix can pivot to other allowed binaries), and training moves the base rate but can’t replace the detection.

This is not a lab curiosity anymore. Interlock RAT activity tied to the KongTuke / LandUpdate808 traffic-distribution cluster has run since May 2025 — initially through Run-dialog ClickFix and fake CAPTCHA pages — and per The Hacker News the PHP-based RAT variant appeared in June. In its July 14 report, The DFIR Report said it had recently observed that same KongTuke web inject transition to FileFix. By September, a separately reported campaign of unknown attribution was using a FileFix lure impersonating Meta support to deliver StealC, hiding the second-stage PowerShell inside images on Bitbucket — Acronis’s analysis and BleepingComputer’s writeup have the chain. Two separately reported campaigns, different payloads and lures, same delivery primitive — Acronis, which analyzed the Meta campaign, called the operator’s identity “difficult to ascertain” and left attribution open, so read them as distinct reports, not proven-unrelated crews. If you deferred this in June because it was a researcher’s proof of concept, it is in your inbox now.

The dialog runs under the browser, and that changes the parentage

ClickFix and FileFix look identical to the victim — paste this, press enter, dismiss the fake error — but they diverge in exactly the place a detection engineer cares about. ClickFix uses the Windows Run dialog, which is hosted by explorer.exe. So a successful ClickFix shows up as explorer.exe spawning powershell.exe, and that is a noisy edge because Explorer legitimately launches everything a user double-clicks all day. You can detect it, but you are fighting a baseline.

FileFix moves the execution into the file-open common dialog. When the phishing page presents a fake “upload your file” control and the victim clicks it, the browser opens the standard file picker. That picker has an address bar, and the address bar happily executes a command if you paste one and hit enter. On Chromium browsers the picker is serviced by a browser-named utility process — Elastic’s maintained rule fingerprints it by its --message-loop-type-ui and --service-sandbox-type=none arguments — so in telemetry the resulting powershell.exe appears as a direct child of chrome.exe, msedge.exe, or brave.exe. Be precise about the mechanism: that’s a distinct utility process sharing the browser’s image name, not code running inside the main browser’s address space. Those two arguments describe a UI-capable, unsandboxed Chromium utility process — a strong heuristic that approximates the file picker, not a conclusive fingerprint, because other Chromium utility services carry the same pair. The more specific --utility-sub-type=chrome.mojom.UtilWin names Chromium’s Windows utility service — the one that exposes the CallExecuteSelectFile operation used for open/save dialogs — so it’s stronger corroboration than the generic pair, though not by itself proof a dialog was open (that same service also handles tasks like taskbar-pin checks and module inspection). Elastic’s rule leaves it out in favor of the broader pair. Firefox isn’t Chromium and doesn’t use any of these arguments, so it needs its own coverage.

That inversion is a gift. explorer.exe → shell is common; browser → shell is rare in a clean environment. Bridewell’s analysis walks the same parent-child hierarchy — msedge.exe executing PowerShell — and it is the most durable indicator across FileFix variants. Two honest exceptions keep it from being universal: a variant that has the browser launch a downloaded EXE which then spawns the shell parents to that EXE, not the browser, and later payload stages parent to whatever dropped them. The direct browser edge is the anchor, not the whole net.

Why you cannot stay in the string-matching game

The first wave of FileFix detections keyed on the pasted string. The original technique concatenated a dummy file path onto a PowerShell comment, so the File Explorer address bar visually showed only something like C:\Company\Reports\Q3.pdf while the real command sat hidden behind a #. Reasonable people wrote rules for the #, for the iex, for the New-Object Microsoft.XMLHTTP COM cradle.

Then the operators moved. The StealC campaign dropped the # entirely and disguised the trailing part of the command as a file path padded with a long run of spaces, so only the benign path renders in the address bar and no comment character appears — a deliberate answer to anyone matching on #. Acronis, quoted in the BleepingComputer piece, described the payload as a variable “which contains a lot of spaces.” Be precise about what that padding hides, though: it hides the command from the victim’s eyes in the address bar. It does not scrub the resulting powershell.exe command line, which in Acronis’s captured sample still carried -noP -W H -ep Bypass, a Bitbucket JPG URL, a base64 blob, and multiple iex calls. What steganography changed was the container and the scanning context, not whether anything touched disk. The first stage downloaded the JPG to %TEMP% and sliced the second-stage PowerShell out of it — that script sat inside the image in plaintext (Acronis notes it’s visible in the file’s strings), extracted into memory and run with iex; only the embedded executables were encrypted. So a scanner limited to file extensions or standalone .ps1 files could miss it, but the script bytes did exist on disk, inside the JPG. The first-stage command line was still loud, too. The technique family kept mutating. In October, an Expel-documented lure posing as a Fortinet VPN compliance check added cache smuggling — JavaScript makes Chrome fetch and cache a fake JPEG that is actually a ZIP, so the pasted command (padded with 139 spaces) only has to carve the archive out of Chrome’s on-disk cache and run it, with no downloader URL or web request in the script at all. But mind how you file it: Expel describes the victim pasting into a separately opened Explorer window’s address bar and launching conhost.exe --headless powershell, not into a browser-hosted file dialog — that’s the ClickFix execution surface (explorer.exe parentage), which is why Expel calls it a ClickFix innovation while BleepingComputer labels it FileFix for abusing the address bar. No source shows a process tree, so don’t count it as proof the browser-parent edge survived another content change; count it as a reminder that “FileFix”-labeled techniques are already broadening past the browser-hosted dialog — the argument for keeping the broader Explorer→shell and TypedPaths coverage beside the browser-parent rule.

This is the same lesson the ClickFix-as-a-service crowd taught earlier, arriving through a different door: content is the attacker’s most fluid variable, process lineage far stickier. You can rewrite a clipboard string in an afternoon; moving the execution off the browser’s own file dialog means abandoning what makes FileFix FileFix. But “stickier” isn’t “immutable,” and it isn’t a reason to retire command-line content detection — the loud first-stage commands above are exactly the kind of thing a content rule catches on its own. Demote content from sole gate to strong corroboration, and pair the lineage with the registry artifact below.

The detection, and the volume it should produce

On Sysmon you want Event ID 1 with a browser parent and an interpreter, LOLBin, or downloaded-EXE child. In Splunk, assuming your Sysmon data is normalized to the usual fields:

index=edr sourcetype=xmlwineventlog:microsoft-windows-sysmon/operational EventCode=1
  ParentImage IN ("*\\chrome.exe","*\\msedge.exe","*\\firefox.exe","*\\brave.exe","*\\opera.exe")
  ( Image IN ("*\\powershell.exe","*\\pwsh.exe","*\\cmd.exe","*\\mshta.exe",
              "*\\wscript.exe","*\\cscript.exe","*\\rundll32.exe","*\\msiexec.exe",
              "*\\certutil.exe","*\\certreq.exe","*\\curl.exe","*\\bitsadmin.exe")
    OR Image="*\\Downloads\\*" )
  NOT ( Image="*\\rundll32.exe" AND
        (CommandLine="*ndfapi.dll,NdfRunDllDiagnoseWithAnswerFile*"
         OR CommandLine="*shwebsvc.dll,AddNetPlaceRunDll*") )
| stats count values(CommandLine) as cmdline by ComputerName, User, ParentImage, Image

Defender for Endpoint captures the same relationship natively, and if you have it you should prefer it because it does not depend on your Sysmon config being complete:

DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","mshta.exe","wscript.exe","cscript.exe",
                      "rundll32.exe","msiexec.exe","certutil.exe","certreq.exe","curl.exe","bitsadmin.exe")
      or FolderPath contains @"\Downloads\"
| where not( FileName =~ "rundll32.exe"
    and ( ProcessCommandLine contains "ndfapi.dll,NdfRunDllDiagnoseWithAnswerFile"
       or ProcessCommandLine contains "shwebsvc.dll,AddNetPlaceRunDll" ) )
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine,
          FileName, FolderPath, ProcessCommandLine, SHA1

Two refinements. First, FileFix is an execution mechanism, not a PowerShell mechanism — the maintained Elastic rule covers curl, msiexec, rundll32, mshta, wscript/cscript, certutil/certreq, and a bare executable launched from Downloads, so the child list above is deliberately broad. Second, on Chromium you can raise fidelity toward the file-picker parent by requiring InitiatingProcessCommandLine has "--message-loop-type-ui" and InitiatingProcessCommandLine has "--service-sandbox-type=none" — the pair Elastic’s Potential Execution via FileFix Phishing Attack rule keys on. Those two are generic to UI-capable unsandboxed Chromium utility processes, so treat them as a strong approximation, and score rather than require the more specific --utility-sub-type=chrome.mojom.UtilWin when it appears (it names the Chromium Windows utility service that exposes the select-file operation — corroboration, not proof a dialog was open — and the token and its companion switches drift across browser and Chromium version). Keep a broader browser-to-suspicious-child analytic for Firefox, unsupported Chromium variants, and adjacent browser-mediated execution — but treat it as behavioral coverage, not proof a hit was FileFix: firefox.exepowershell.exe can be highly suspicious without demonstrating the file-open dialog caused it.

The command line is in the query as strong corroboration, not the sole gate. Severity scoring can read it for -w hidden, -nop, -ep bypass, -enc, iex, or an outbound URL — the observed first-stage commands carry several of these, so a content rule can independently flag known variants. Keep the process-lineage alert firing regardless, because the next variant may rearrange or encode that content; lineage is what survives the rewrite, content is what confirms it fast.

One more signal, useful both live and in DFIR: the paste can leave a registry trail. Intel 471 and delivr.to report that the FileFix command can appear under HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths, and there is a SigmaHQ rule (FileFix - Command Evidence in TypedPaths) matching command tokens in url1. Separate forensic testing of Explorer’s address-bar history shows url1 holds the newest entry, that entries are commonly committed only when the window closes, and that a concurrently open window can overwrite another’s — so treat the artifact as conditional: absence is inconclusive rather than exculpatory, and history can age out or be cleared. A rule keyed on the # character also inherits the exact blind spot the space-padded variant was built to exploit, so match on the command tokens, not the comment.

Volume, after environment-specific baselining, should be low and high-suspicion — especially when the parent matches the Chromium file-picker arguments rather than any browser process. Whether it pages a human should follow your measured local frequency and the corroborating context, not an assumption. And “low volume” assumes your telemetry is actually there. Sysmon has to be configured to log process creation and not filter out powershell.exe — plenty of hand-me-down Sysmon configs exclude common images to cut volume, and a # do not touch exclusion block for PowerShell is exactly the kind of thing that quietly guts this rule. Agent coverage is the other gap: if 30% of your fleet has no EDR or a dead Sysmon service, the rule is blind on those hosts and the dashboard will not tell you, because absence of a low-volume signal looks identical to a quiet week.

The first week of tuning is baselining, not a WebView2 certainty

The parent list above is the standalone browser binaries, which deliberately excludes msedgewebview2.exe — Edge’s embeddable runtime, now inside the new Outlook, Teams, and a lot of enterprise line-of-business apps. State that exclusion, because it cuts both ways. Microsoft’s documented WebView2 child processes are browser-architecture processes — renderer, GPU, network, Crashpad — not PowerShell or script hosts, so “WebView2 apps routinely spawn interpreters and will be your top false positive” is a hypothesis to validate against your own telemetry, not an established tuning fact; don’t fold msedgewebview2.exe into the primary rule on that assumption. The real tradeoff runs the other way: a lure rendered inside a WebView2-hosted app could produce msedgewebview2.exe — or host-application — parentage and slide past a rule scoped to standalone browser names. Which process actually owns a command entered into the file dialog isn’t established by Microsoft’s docs, so validate the exact lineage experimentally before you add it to production coverage; that’s the argument for a separate, allowlisted WebView2 analytic once you have measured what those apps actually do.

The easy noise is narrower. Native messaging hosts and password-manager helpers get launched from the browser, but they resolve to their own signed binaries rather than to powershell.exe, so the child-image filter already excludes them. Browser auto-update (MicrosoftEdgeUpdate.exe) is the same story — real, frequent, not a shell.

The false positives that actually need a human decision are the ones Elastic documents from real telemetry: signed, browser-initiated installer and diagnostic workflows — a legitimately downloaded setup or support tool that the browser launches and that then calls an interpreter. Note what is not on that list: a developer double-clicking a downloaded .ps1. Windows won’t run it — double-clicking a .ps1 opens it in an editor by security-feature design, and choosing “Run with PowerShell” from File Explorer would ordinarily produce Explorer — not the browser — as the initiating process. A legitimate browser → powershell.exe edge therefore needs the browser or one of its utility processes to invoke the interpreter directly (browser automation, a modified file-handler workflow, or an app-specific support process) — a narrower, more suspicious population than “devs download scripts.” Where legitimate hits cluster on engineering OUs, the clean fix is a device-group carve-out, not a global suppression: suppress fleet-wide and you suppress the actual attack on the finance laptop that has no business doing this. Before creating any exception, do what Elastic recommends — require recurrence with a stable parent path and flags, signer, hash, command-line shape, user, and host, and never except on browser parentage or child image alone. And expect the download-then-run installer case — the browser launches a downloaded EXE that itself spawns PowerShell — which parents the shell to the installer, not the browser, so it never touches this rule.

One more coverage note. In Citrix and VDI published-browser setups the parentage should hold — the shell is still a child of the browser inside the session — but whether it survives to your SIEM depends on where the browser runs, session isolation, and EDR placement, so validate it against your own VDI telemetry rather than assuming. What is reliable triage advice on shared session hosts: your ComputerName field collapses dozens of users onto a handful of hosts, so pivot on User, not host, or you will misattribute the hit.

Prevention that actually bites

Detection buys you the alert; it does not stop the download cradle from completing before the analyst wakes up. The control that changes the outcome is CM-7, least functionality, applied to the script interpreters. App Control for Business (WDAC) with script enforcement puts untrusted PowerShell — anything not allowed by policy — into Constrained Language Mode, and AppLocker in enforce mode triggers the same CLM behavior (though Microsoft now recommends App Control over AppLocker and is no longer investing in the latter beyond security fixes). CLM doesn’t disable PowerShell; for untrusted code it disallows Add-Type, restricts arbitrary .NET type and member access, limits New-Object to approved .NET and COM types, and keeps the script blocks handed to Invoke-Expression in Constrained Language Mode. Scripts and modules explicitly approved by App Control run in Full Language Mode. Those restrictions materially disrupt several observed FileFix cradles, which are not all the same one: Bridewell’s sample used the disallowed New-Object -Com Microsoft.XMLHTTP object, the Interlock sample instantiated System.Net.WebClient, and the StealC chain leaned on static System.IO.File / System.Text.Encoding / System.Convert methods plus code evaluated through iex. Be precise about the other limits, though: CLM is not a blanket ban on downloading — Invoke-WebRequest, which the StealC first stage used, stays available — App Control script enforcement does not police .bat / .cmd content run through cmd.exe, and FileFix can pivot to installers, curl, certutil, rundll32, or a downloaded EXE. So it is a high-value enforcement layer that constrains these chains, not a complete preventive control — and it is the one most shops keep deferring because it breaks somebody’s legacy script and nobody wants to own the exception list.

Remove or WDAC-block mshta.exe and disable Windows Script Host for standard-user context while you are in there — App Control script enforcement already blocks unauthorized WSH scripts and MSHTA/MSXML execution, and both are on the FileFix child-image list precisely because they are still present on default builds. This is AC-6 and CM-7 doing the same job from two directions.

Layer Control What it does here
Browser → interpreter alert SI-4 Fires on the durable process edge
Malicious code chain SI-3 Catches the second-stage RAT/stealer if the cradle completes
Constrained Language Mode via App Control/AppLocker CM-7 (AC-6) Drops untrusted PowerShell into CLM, constraining the in-memory cradle
Remove mshta / disable WSH CM-7 Removes two of the interpreters the lure can pivot to
Phishing-resistant user handling AT-2 Reduces successful paste-and-run, with limits

On AT-2, be honest about the ceiling. FileFix works because pressing Ctrl+L and Ctrl+V in a File Explorer window feels like a normal Windows action, not like running a script, and no amount of “don’t paste things” training fully closes a gap that is engineered to look benign. Training moves the base rate; it does not replace the SI-4 detection.

Write the browser-parent rule — better, match the Chromium utility-process context — back it with the TypedPaths artifact, validate your WebView2 and VDI edges before you turn on paging, and get App Control script enforcement onto at least the standard-user population. The operators have already shown they will rewrite the payload as many times as it takes to beat your string matcher. What they have not cheaply rewritten is the process lineage that makes FileFix run its shell out of the browser’s own file dialog — so anchor there, and let content confirm rather than gate.

Sources


This post was engineered and validated through a multi-agent AI workflow — drafted, adversarially reviewed by several independent models, checked against primary sources, and given a human review before publishing. See an inaccuracy, or found this useful? Leave a comment below — corrections and feedback are read and shape what comes next.

Leave a comment