The Alert Said Ransomware. The File Events Said Otherwise.

High-severity ransomware alert, one TLS connection, and ninety-eight minutes to prove nothing happened. The hunt produced a technique that reconstructs browsing from endpoint file events, and an injection campaign no reputation verdict has flagged.

Share
The Alert Said Ransomware. The File Events Said Otherwise.

At 12:21:35 UTC on 29 July, Microsoft Defender for Endpoint fired the kind of alert that reorders an afternoon: "A file or network connection related to a ransomware-linked emerging threat activity group detected." High severity. Ransomware category. A critical-asset tag on the affected user. Fourteen minutes later the device was pulled off the network by hand, on a partial picture, because nothing in the evidence yet ruled anything out.

Microsoft says this alert family is the one customers should treat as incident-response-now. It fires when telemetry on a device matches infrastructure or tooling that Microsoft's threat intelligence attributes to a tracked, ransomware-affiliated activity cluster. The banner tells you a crew with a ransomware business model touched this machine. It does not tell you what happened next. That part is yours.

What follows is the afternoon it took to answer, the two telemetry traps that ate the first hour, the technique that turned standard endpoint file events into a browsing timeline accurate to the second, and the operation that technique led to.

Timeline of the 29 July alert: connection, callout, browser storage, manual isolation, release. No automated containment fired.

The Evidence Pull That Disappoints

The natural first move is to join the alert to its evidence.

AlertInfo
| where Timestamp > ago(7d)
| join kind=inner AlertEvidence on AlertId
| where DeviceName startswith "" or AccountUpn =~ ""
| project Timestamp, Title, EntityType, EvidenceRole, FileName,
SHA256, RemoteIP, RemoteUrl, DeviceName, AccountUpn

It returns two rows: a Machine entity and a User entity. No file. No IP. No URL. For an alert whose title promises "a file or network connection," the evidence table appears to contain neither.

The trap is in the filter. The Url, Ip, and Process evidence rows for this alert family carry empty DeviceName and AccountUpn fields, so any evidence query filtered on device or user quietly drops exactly the rows that hold the IOC. Pull the table by AlertId with no entity filters and read AdditionalFields.

AlertEvidence
| where AlertId == ""
| project Timestamp, EntityType, EvidenceRole, EvidenceDirection,
FileName, FolderPath, SHA1, SHA256, ProcessCommandLine,
RemoteIP, RemoteUrl, AccountUpn, DeviceName, AdditionalFields

The unfiltered pull produced what the join could not: a Url entity flagged IsIoc: true for tofartinthesummer[.]com, an Ip entity for 104.21.77[.]246, and the process that made the connection, firefox.exe. Not a trojanised build, either: a genuine Mozilla-signed binary with a global prevalence around 52,000 machines, running as a per-user install from AppData, launched with -os-autostart the previous morning. The flagged "file or network connection" was a network connection, and the process behind it was an ordinary browser.

One smaller lesson rides along: the clock. The Defender portal displays local time while Advanced Hunting stores UTC, so a windowed query built from a portal timestamp lands an hour wide of the event in an Irish summer. Every timestamp in AlertEvidence is UTC, so pick one event, compare the two renderings, and confirm your offset before building any windowed query on top of it.

The Connection

With the domain in hand, the timeline query is short and its answer shorter.

let FlaggedDomain = "";
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where RemoteUrl has FlaggedDomain
| project Timestamp, DeviceName, ActionType, RemoteIP, RemotePort,
RemoteUrl, InitiatingProcessFileName
| order by Timestamp asc

One row. A single ConnectionSuccess to tofartinthesummer[.]com on port 443 at 12:21:35 UTC, from firefox.exe. No retries, no beaconing, no second process, and no other device across fourteen days of telemetry.

One note on the infrastructure. The domain resolves to 104.21.77[.]246, a Cloudflare edge address shared by an enormous number of unrelated sites, and answered from whichever edge suits the client. Behind a CDN, the domain is the indicator and the address is context: an address-keyed hunt would have false-positived across every Cloudflare-fronted service the organisation touches.

A single successful connection from a browser is a fork in the road. Either the user's browser touched attacker infrastructure in passing, a redirect bounce or an injected resource, or something was fetched and the intrusion was under way. The difference between those two worlds is what got written to disk over the following minutes, and that is where the interesting telemetry lives.

The Storage Timeline

Firefox stores origin-keyed data under ...\Profiles\<id>\storage\default\https+++<origin>\. A first visit to a site creates a directory named for the origin; every active session writes sqlite inside an existing one. Those writes surface in DeviceFileEvents like any other file activity, which means standard MDE telemetry contains a browsing timeline. No browser-forensics acquisition, no endpoint access, and it works on a device that is already isolated.

It is a filtering exercise: strip the cache noise and the origin storage stands out.

let Dev = "";
let T0 = datetime();
DeviceFileEvents
| where DeviceName startswith Dev
| where Timestamp between ((T0 - 1h) .. (T0 + 2h))
| where InitiatingProcessFileName =~ "firefox.exe"
| where not(FolderPath has_any (@"\cache2", @"\startupCache",
@"\jumpListCache", @"\thumbnails", @"\OfflineCache", @"\safebrowsing"))
| project Timestamp, ActionType, FileName, FolderPath
| order by Timestamp asc

Around the alert, three rows told the whole story:

12:21:29 UTC - connection to rigolshop.eu 12:21:35 UTC - connection to the flagged domain (+6s) 12:21:39 UTC - FileCreated, first-visit origin storage for rigolshop.eu (+4s)

One page load. The user opened a European test-equipment retailer; six seconds into the load, an injected resource called out to the attacker domain; four seconds after that, the site's own JavaScript initialised local storage for the first visit. The caveat belongs in print alongside the finding: this is load-order inference, not a referrer log. It is strong, it is second-precision, and it is still circumstantial until verified.

The rest of the window is what a near-miss looks like in file telemetry. Forty-eight rows, all profile housekeeping and origin storage. Zero .part files, executables, archives, or scripts. Zero file creations in any Downloads folder, not merely in the alert window but across the entire two-day device timeline. A tenant-wide sweep of FileOriginUrl and FileOriginReferrerUrl for the flagged domain: empty. Nothing was fetched, nothing landed, nothing ran.

Reading Past the Action-Type Names

One more thing eats an afternoon if you let it. Pull the full device timeline for the window and the action-type column reads like an active intrusion. Across two days this device logged 1,350 SuspiciousProcessDataExfiltration events and 1,195 RemoteCreateThreadCrossProcessInjection events.

Both are firefox.exe. It accounts for 1,319 of the exfiltration events and 1,192 of the injections, with the remainder split between Word and a screen-capture tool. Firefox's multi-process content architecture legitimately creates threads across process boundaries, and ordinary browser upload traffic trips the exfiltration classifier.

The lesson is not that the classifications are wrong. It is that MDE names behaviours, not verdicts, and a name chosen to be findable is not a name chosen to be calm. When the alert already says ransomware, an action-type column full of injection and exfiltration is exactly the confirmation bias the hunt is meant to defeat. Establish the baseline before reading the labels.

A smaller version of the same trap: the flagged connection appears twice in the timeline, once as SuspiciousProcessDataExfiltration and once as OutboundConnectionToWebProtocol, both stamped 12:21:35.034. One connection, two rows. Count rows rather than events and the single request becomes two.

Verification, Scans conducted from urlscan.io

urlscan.io keeps public records of pages that we scanned and others, including every domain each page contacted during load. One query turns that archive into the referrer log endpoint telemetry cannot provide:

domain:tofartinthesummer.com AND NOT page.domain:tofartinthesummer.com

That returns scans where the flagged domain was contacted but was not itself the page being scanned. Pages that loaded it as a resource, in other words.

rigolshop.eu appears three times, in scans dated 27, 28 and 30 July 2026. The incident falls on 29 July, bracketed on both sides by third-party captures of the same behaviour. Both the site root and a deep product page are represented, which points to a site-wide injection rather than one poisoned page. A scan of a different product page on approximately 14 July shows no such contact, suggesting the content was introduced in the second half of July, though a single page is weak evidence for a whole site.

The retailer was notified on 31 July 2026 and is described here in dated factual terms only. It is a victim in this chain, not a party to it.

What the Injection Actually Does

The 27 July scan captures the request in full, and it is not what the phrase "injected script" would lead you to expect.

GET H3 200 tofartinthesummer.com/api/index.php
523 B Fetch application/json

Not a script tag. Not an iframe. A fetch() call returning JSON. The injected code is calling an API and reading a structured answer, which is a different animal from a loader being pulled down and executed.

The answer it received:

{"enc":"gcm1",
"q":"9fNrsEjtvkDIrTKPvdqG7ed_eOTq-N2RD9tuyGfU_EeKGfLzc1oLgjkKuwE3p2Jj...",
"q2":"S-kasm0IDSTWDjNedOtV0gsV8bX68sfwaeTw9zn3skH9j6v05v2J1Ucisfm6boHm..."}

Both values are base64url, unpadded. Decoded, q is 195 bytes and q2 is 175 bytes, with Shannon entropy of 6.98 and 6.86 bits per byte. enc: gcm1 declares the scheme and the byte layout matches it: a 12-byte initialisation vector, a body, and a 16-byte authentication tag, leaving 167 and 147 bytes of ciphertext. The configuration is encrypted above TLS, and the version suffix implies the operator anticipates shipping a gcm2.

We did not decrypt it. The key lives in the injected JavaScript on a compromised third party we have no authority over. What the payload contains is therefore inference, drawn from what the browser did next.

The Blockchain Hop

Immediately after the config fetch, the same page made two more calls:

POST polygon-rpc.com/ 401 101 B Fetch application/json
POST rpc-mainnet.matic.quiknode.pro/ 200 231 B Fetch application/json

A test-equipment retailer has no reason to make JSON-RPC calls to Polygon nodes during a homepage load. The first provider rejected the call and the script failed over to a second, which succeeded. Failover is deliberate engineering, not incidental traffic.

Both endpoints are legitimate public blockchain infrastructure and must not be blocked or treated as indicators. They are named here because their presence in a retail page load is the signal, not because they are hostile.

Two encrypted config blobs, two RPC providers tried in sequence. The straightforward reading is that q carries the primary lookup and q2 the fallback, and 167 bytes of plaintext holds a contract address and a method selector comfortably.

That pattern has a name. EtherHiding, first documented by Guardio Labs in October 2023, stores malicious code or routing data inside smart contracts on a public blockchain and retrieves it with a read-only eth_call. Because the data is replicated across thousands of nodes, there is no server to seize, no domain to suspend and no address to block. Google's threat intelligence team tracks a group designated UNC5142 running EtherHiding operations with a downloader called CLEARSHORT, an evolution of the ClearFake framework, and Trend Micro documented ClearFake routing payloads through BNB Smart Chain testnet contracts as recently as May 2026.

Two things distinguish what is on rigolshop.eu from the published reporting, both offered as observations rather than claims of novelty. The chain here runs on Polygon, where documented cases overwhelmingly use BNB Smart Chain. And the on-chain lookup is preceded by an encrypted configuration fetched from a conventional web C2, rather than the injected loader querying the contract directly. If that layering is a variant rather than something already documented, it is worth watching, because it means the routing can be changed without touching either the compromised sites or the chain.

The Endpoint Answers Differently Depending On Who Asks

Six days before the incident, on 23 July, somebody in the Netherlands ;) submitted tofartinthesummer[.]com/api/index.php to urlscan directly. That scan is public, and its result is the cleanest single piece of evidence in the investigation.

Requested directly, the endpoint returns HTTP 404 with a nine-byte body.

Requested from a compromised page, the same endpoint returns HTTP 200 with 523 bytes of encrypted JSON.

Same URL, same week, entirely different answer. The endpoint is gated on request context, which is why casual investigation finds nothing and why a reputation crawler hitting it head-on sees a dead 404 and moves along.

The response headers on that 404 are the other half of the story:

access-control-allow-origin: *
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-headers: Content-Type, X-Requested-With
cache-control: no-store, no-cache, must-revalidate, max-age=0
surrogate-control: no-store
referrer-policy: no-referrer
x-robots-tag: noindex, nofollow, noarchive, nosnippet

A wildcard CORS policy exists for one reason: so arbitrary third-party websites can call this endpoint from the browser and read the response. That is not a misconfiguration on a private API. It is the defining requirement of a cross-site command channel, correctly configured for the purpose.

The rest is operational hygiene. Do not cache me, at the browser or the CDN. Do not send referrers. Do not index, follow, archive or snippet me. Whoever built this did not want it appearing in a search engine or sitting in a cache where somebody could find it later.

The TLS certificate covers tofartinthesummer.com and *.tofartinthesummer.com, issued 6 June 2026 for ninety days. The wildcard means arbitrary subdomains can be stood up without a new certificate or a new transparency-log entry to notice.

Nobody Called It

The domain was registered around 5 April 2026, making it roughly three and a half months old at the time of the incident, and it has been publicly scanned around thirty times. Here is the part that should trouble anyone treating reputation feeds as a control.

Google Safe Browsing: no classification.

urlscan overall verdict: score 0, not malicious. That is what the interface displays and what a casual check reports.

But inside the same scan record, urlscan's own machine-learning engine returned a score of 76 and a verdict of malicious, tagged urlscan-ml. The signal existed. It was computed, stored and published in the scan JSON. It simply never surfaced into the headline verdict, where a human or an automated check would actually see it.

That is more uncomfortable than a straightforward miss. This was not infrastructure hiding successfully from detection. It was infrastructure a detection engine flagged, on a public platform, where the flag did not reach the field anyone reads. Community votes in three and a half months: zero.

Hunting checklist: the indicator, two time-wasting traps, four Advanced Hunting queries, four checks before releasing the device.

The Shape of the Operation

The same query returns two other sites contacting the same infrastructure.

digitnow.us appears in five scans across roughly a month, including a page offering a PotPlayer download. A compromised software download portal is a materially more dangerous placement than a product listing, because the visitor arrives already intending to run an executable.

alandalusgroup.org appears in a scan roughly four months old, placing contact at or before the domain's own registration window, which is worth resolving before anyone builds a timeline on it.

The three victims have nothing technical in common. rigolshop.eu runs a custom PHP application on Microsoft Azure. digitnow.us runs on Alibaba US infrastructure and embeds a PHP Live! chat widget. Different stacks, different hosts, different jurisdictions, no shared CMS or plugin visible from outside. That absence matters: it argues against a single vulnerable dependency and towards opportunistic compromise through credential reuse, exposed administration or mass scanning. There is no one patch that fixes this set.

digitnow.us carries the same reputation profile as everything else here: fourteen public scans, no Google Safe Browsing classification, no urlscan overall verdict.

What the Alert Actually Meant

Microsoft does not name adversaries in alert titles, and that is deliberate. It tracks emerging or unattributed clusters under interim Storm designations, and this detection family fires on telemetry matches against whichever Storm-tracked, ransomware-linked cluster's infrastructure is involved. The same title has captured operations as different as Storm-1811's Quick Assist social engineering and Qakbot distribution. The banner gives you the category; the identity sits one layer down, in the alert's related-threat link and Intel Explorer.

Which reframes the two techniques stamped on the alert, T1574.001 (DLL search-order hijacking) and T1001.002 (steganography). Neither was observed on the device. They describe the tracked actor's loader stage, the tradecraft that would have followed a successful delivery: a signed binary side-loading a malicious DLL that unpacks its next stage from an image file. Read against everything above, the alert metadata is the near-miss thesis in miniature. The techniques on the ticket belong to a stage this visit never reached, because the chain was interrupted at the point where a browser fetched a config it never got to act on.

Containment First, Proof Second

At 12:35:21 UTC, thirteen minutes and forty-six seconds after the alert fired, the device was isolated manually. Not because the evidence justified it, but because at that point the evidence ruled nothing out. A critical-asset user's workstation had matched ransomware-actor infrastructure, the obvious evidence query had returned two useless rows, and the asymmetry was stark: being wrong in one direction costs an afternoon, being wrong in the other costs the estate.

Worth stating plainly, because the marketing implies otherwise. Nothing contained this device automatically. Automated attack disruption did not fire. Every evidence row on the alert carries an empty remediation status. If your tenant is not licensed for it, not configured for it, or the confidence thresholds are not met, containment is a judgement a human makes with a partial picture and a running clock.

There is a second lesson in the containment itself. The isolation action does not surface in a DeviceEvents hunt the way you would expect, and has_any matches whole terms, so a filter on a truncated string like "Isolat" will silently miss IsolateResponse entirely. Go to the device timeline or the Action centre for the authoritative record of what was contained and by whom.

Release came at 14:13:41 UTC, one hour and thirty-eight minutes after isolation, gated on four checks answerable from telemetry already gathered:

  • ransomware-precursor hunts empty, covering shadow-copy tampering, recovery-disabling, log clearing and backup-service kills;
  • no unexpected persistence: every scheduled task event in the window traced to schtasks.exe, MsMpEng.exe, the Intune management agent or taskhostw.exe, with created and deleted counts balanced;
  • the org-wide sweep negative across thirty days;
  • a tenant-wide block indicator live on the domain, converting any future contact from a silent connection into an instant alert.

Then, and only then, release from isolation, with a closure note that survives audit:

29 July 2026, 14:13 UTC: MDE TI alert (High) - single successful TLS connection from firefox.exe to <flagged-domain> (CDN-fronted), associated by vendor TI with a ransomware-linked emerging threat activity group. No automated remediation applied; device isolated manually at 1:35:21 UTC. Advanced Hunting confirmed: one connection only, no repeat contact (14d), no downloads, no payload on disk, no execution, and no persistence attributable to the session; org-wide sweep negative (30d). Vector corroborated against public scan records: third-party retail site serving injected content that fetches encrypted configuration from the flagged domain. Actions: domain blocked tenant-wide via custom indicator; device released at 14:13:41 UTC. Determination: true positive - attempted access, no compromise.

Takeaways for Defenders

Query alert evidence by AlertId, unfiltered. The IOC-bearing entity rows can carry empty device and user fields; an innocent-looking filter drops them silently.

Hunt in UTC. The portal displays local time; Advanced Hunting does not. Confirm your offset against a known event before building any windowed query.

Establish the baseline before reading the labels. Alarming action-type names describe behaviours, not verdicts. Check whether the same names appear across every machine in the estate before treating them as incident evidence.

Behind a CDN, block the domain and ignore the address. Shared edge addresses false-positive at tenant scale while protecting against nothing.

Browser origin storage is a timeline. FileCreated on an origin directory marks a first visit; sqlite writes mark an active session. Standard file telemetry will reconstruct a browsing sequence to the second, even on an isolated device.

Public scan archives are the referrer log your telemetry lacks. domain:X AND NOT page.domain:X on urlscan.io returns the pages that loaded a given domain as a resource. It costs nothing and touches no infrastructure.

Read the scan JSON, not the verdict badge. The overall verdict on this domain is zero. The machine-learning sub-verdict in the same record is 76 and malicious. The interface shows you the first one.

Do not assume the platform contained it. Check the Action centre for what was actually actioned and by whom.

A 404 is not an absence of evidence. This C2 returns 404 to direct requests and 200 with an encrypted payload to requests arriving in the right context. Probing an endpoint head-on tells you how it treats you, not what it does.

Indicators

Domain - tofartinthesummer[.]com
C2 for injected content on compromised sites. Registered approximately 5 April 2026. Microsoft TI links it to a ransomware-linked emerging activity group.

URI - tofartinthesummer[.]com/api/index.php
Returns AES-GCM encrypted JSON config to requests from compromised pages; HTTP 404 to direct requests. Wildcard CORS, no-store, noindex.

Response structure - "enc":"gcm1" with q and q2 fields
Base64url, 12-byte IV, 16-byte GCM tag.

TLS certificate - tofartinthesummer.com, *.tofartinthesummer.com
Issued 6 June 2026, 90-day validity. Wildcard permits arbitrary subdomains.

IP addresses - 104.21.77[.]246, 172.67.213.85, 2606:4700:3030::6815:4df6
Cloudflare edge. Context only, do not block or pivot.

Affected site - rigolshop.eu
Observed serving the injection in public scans dated 27, 28 and 30 July 2026. Victim, notified 31 July 2026.

Affected site - digitnow.us
Observed contacting the C2 in public scans across approximately one month.

Affected site - alandalusgroup.org
Observed contacting the C2 in a scan approximately four months old.

Not indicators, listed to prevent misuse: polygon-rpc.com and rpc-mainnet.matic.quiknode.pro are legitimate public blockchain RPC providers. Their appearance in a retail page load is the signal. Blocking them is not a mitigation and will break unrelated services.

Affected sites are listed as victims. Detection is by public scan record only; ZDW has not tested, probed or interacted with any of them.

The Bigger Picture

Near-misses are the cheapest lessons an estate ever gets, and the least examined. This one cost a single TLS connection and returned a full rehearsal: the detection fired, containment took a human fourteen minutes, and the same afternoon of hunting that proved nothing happened is exactly the afternoon that would have found the loader if something had.

It also returned something the tenant could never have shown on its own. One connection from one browser, chased into a public archive, surfaced a command channel that answers 404 to anyone who knocks directly, holds a wildcard CORS policy so any compromised site can call it, encrypts its configuration above TLS, routes its payload through a public blockchain so there is nothing to seize, and instructs every crawler that finds it not to write it down. Three and a half months old, thirty public scans, and the only verdict that ever called it malicious was a machine-learning score buried in a JSON field no interface displays.

The answer to what happened on the endpoint was sitting in a file table nobody thinks to point at a browser. The answer to what it was part of was sitting in a search box, in a field nobody reads.