Back to Intelligence

Gootloader Takedown: 25,000 Infections, 100+ Servers Seized — Detection and Remediation Guide for Defenders

SA
Security Arsenal Team
November 15, 2026
10 min read

The U.S. Department of Justice, working with international law enforcement partners, has disrupted the Gootloader malware operation: more than 100 servers were taken down or cleaned of the malware, U.S. domain names used for delivery and command-and-control were seized, and victims worldwide are being notified. The operation had compromised approximately 25,000 computers.

Here is the part that matters for defenders: seizing C2 infrastructure does not remediate compromised endpoints. Gootloader is a loader — its entire purpose is to stage follow-on payloads. Historically, infections have preceded deployment of ransomware (including REvil/Sodinokibi), Cobalt Strike beacons, IcedID, and SystemBC. If any of those 25,000 machines are in your environment, the operator's infrastructure being seized buys you time — it does not close the incident. The staged payloads, persistence mechanisms, and any lateral movement that already occurred are still your problem.

This post covers how Gootloader operates from a defender's perspective, how to hunt for it retrospectively, and how to remediate.

Technical Analysis: How Gootloader Actually Works

Delivery: SEO Poisoning, Not Phishing

Gootloader's defining characteristic is that it rarely arrives by email. Operators compromise legitimate websites — predominantly WordPress — and inject fake forum threads and Q&A pages optimized for search engine ranking. Victims searching for business document templates (contracts, agreements, financial forms) land on these pages and download a ZIP archive containing an obfuscated JavaScript file.

This has two implications for detection:

  1. Your email gateway will not save you. The delivery vector is browser-driven downloads of .zip archives containing .js files from legitimate-but-compromised domains.
  2. User behavior telemetry matters. The typical execution chain starts with a user double-clicking a .js file from their Downloads folder, which invokes wscript.exe.

Execution Chain

The classic Gootloader execution chain, which defenders should treat as the canonical detection target:

  1. User downloads *.zip from a search result → extracts a .js file (often named to match the search query, e.g., what_is_a_post_nuptial_agreement 61472.js).
  2. wscript.exe executes the JS, which drops a second obfuscated JS stage into %APPDATA% with a legitimate-looking filename.
  3. The second stage runs (again via wscript.exe), which launches PowerShell as a child process of wscript.exe — this parent-child relationship is one of the highest-fidelity signals available.
  4. PowerShell performs reflective injection and executes payloads in memory, fingerprinting the host and pulling follow-on malware.
  5. Persistence is established via registry Run keys and, in some campaigns, scheduled tasks pointing at the JS or follow-on payloads.

Exploitation Status

  • Scale: ~25,000 confirmed infections per the DOJ announcement; over 100 malicious servers and seized domains.
  • Follow-on risk: Confirmed history of ransomware enablement. Gootloader has been a documented initial access vector for human-operated ransomware intrusions.
  • Post-takedown status: C2 is disrupted, but sinkholing/neutralization of infrastructure is frequently temporary. Operators of comparable loaders (Emotet, Qakbot) have rebuilt after takedowns. Treat this as ongoing risk, not a closed chapter.
  • CISA KEV: No CVE applies — this is a malware campaign, not a vulnerability. Do not look for a patch; look for artifacts.

Detection & Response

The highest-fidelity detection opportunities are the wscript.exe → powershell.exe parent-child relationship, JS files executing from user-writable directories (Downloads, AppData), and Run-key persistence referencing script files.

Sigma Rules

YAML
---
title: Gootloader - Wscript Spawning PowerShell
id: 3f8a2c41-7b9d-4e51-a6c2-9d1e5f8b3a07
status: experimental
description: Detects PowerShell spawned by Windows Script Host (wscript/cscript), a hallmark of Gootloader's staged JavaScript execution chain.
references:
  - https://securityaffairs.com/202997/cyber-crime/gootloader-malware-disrupted-after-infecting-25000-computers.html
  - https://attack.mitre.org/techniques/T1059/007/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.execution
  - attack.t1059.007
  - attack.t1059.001
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\wscript.exe'
      - '\cscript.exe'
  selection_child:
    Image|endswith:
      - '\powershell.exe'
      - '\powershell_ise.exe'
      - '\pwsh.exe'
  condition: all of selection_*
falsepositives:
  - Rare legitimate admin scripts that invoke PowerShell from WSH wrappers
level: high
---
title: Gootloader - JavaScript Execution from User Directories
id: 8c1d4e62-3a5f-4b78-9c01-2e7a6d4f9b15
status: experimental
description: Detects wscript.exe or cscript.exe executing JavaScript files from Downloads, AppData, or Temp directories, consistent with Gootloader SEO-poisoned ZIP delivery.
references:
  - https://securityaffairs.com/202997/cyber-crime/gootloader-malware-disrupted-after-infecting-25000-computers.html
  - https://attack.mitre.org/techniques/T1204/002/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.execution
  - attack.t1204.002
logsource:
  category: process_creation
  product: windows
detection:
  selection_img:
    Image|endswith:
      - '\wscript.exe'
      - '\cscript.exe'
  selection_path:
    CommandLine|contains:
      - '\Downloads\'
      - '\AppData\Roaming\'
      - '\AppData\Local\Temp\'
  selection_ext:
    CommandLine|contains:
      - '.js'
      - '.jse'
  condition: all of selection_*
falsepositives:
  - Some enterprise software installers run JS helpers from temp paths; tune per environment
level: high
---
title: Gootloader - Run Key Persistence Referencing Script Files
id: b5e7f1a3-6c28-4d94-a817-4f2c8e6b1d93
status: experimental
description: Detects registry Run/RunOnce key values pointing to .js, .jse, or .vbs files in user-writable paths, a Gootloader persistence pattern.
references:
  - https://securityaffairs.com/202997/cyber-crime/gootloader-malware-disrupted-after-infecting-25000-computers.html
  - https://attack.mitre.org/techniques/T1060/
  - https://attack.mitre.org/techniques/T1547/001/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.persistence
  - attack.t1547.001
logsource:
  category: registry_set
  product: windows
detection:
  selection_key:
    TargetObject|contains:
      - '\CurrentVersion\Run'
  selection_value:
    Details|contains:
      - '.js'
      - '.jse'
      - '.vbs'
      - 'wscript'
  selection_path:
    Details|contains:
      - '\AppData\'
      - '\Temp\'
      - '\Downloads\'
      - '\ProgramData\'
  condition: all of selection_*
falsepositives:
  - Rare; legitimate software almost never persists scripts from user-writable paths via Run keys
level: critical

KQL — Microsoft Sentinel / Defender

This hunt targets the full Gootloader chain: script-host execution from user paths, the wscript→PowerShell relationship, and persistence artifacts. Run it over a 30–90 day lookback if you suspect historical exposure.

KQL — Microsoft Sentinel / Defender
// Gootloader hunt: WSH execution from user paths + wscript spawning PowerShell
let lookback = 90d;
let ScriptHosts = dynamic(["wscript.exe", "cscript.exe"]);
let UserPaths = dynamic(["\\Downloads\\", "\\AppData\\Roaming\\", "\\AppData\\Local\\Temp\\", "\\ProgramData\\"]);
DeviceProcessEvents
| where Timestamp > ago(lookback)
| where FileName in~ (ScriptHosts)
| where ProcessCommandLine has_any (UserPaths)
    and ProcessCommandLine has_any (".js", ".jse")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, ProcessId, InitiatingProcessFileName
| join kind=leftouter (
    DeviceProcessEvents
    | where Timestamp > ago(lookback)
    | where FileName in~ ("powershell.exe", "pwsh.exe")
    | project ChildTime=Timestamp, DeviceName, ChildCmd=ProcessCommandLine, InitiatingProcessId
) on DeviceName, $left.ProcessId == $right.InitiatingProcessId
| where ChildTime between (Timestamp .. Timestamp + 5m) or isempty(ChildTime)
| summarize by Timestamp, DeviceName, AccountName, ProcessCommandLine, ChildCmd
| sort by Timestamp desc
KQL — Microsoft Sentinel / Defender
// Gootloader persistence: Run keys referencing script interpreters or script files
DeviceRegistryEvents
| where Timestamp > ago(90d)
| where RegistryKey has_any ("\\CurrentVersion\\Run", "\\CurrentVersion\\RunOnce")
| where RegistryValueData has_any ("wscript", "cscript", ".js", ".jse", ".vbs")
| where RegistryValueData has_any ("AppData", "ProgramData", "Temp", "Downloads")
| project Timestamp, DeviceName, InitiatingProcessAccountName, RegistryKey, RegistryValueName, RegistryValueData
| sort by Timestamp desc

Velociraptor VQL

Use this as a fleet-wide hunt artifact to surface live script-host execution and persistence artifacts on endpoints you suspect may be among the infected population.

VQL — Velociraptor
-- Gootloader hunt: live WSH processes with script args + Run-key script persistence
SELECT {
  SELECT Pid, Name, CommandLine, Username, CreateTime
  FROM pslist()
  WHERE Name =~ '(?i)wscript|cscript'
    AND CommandLine =~ '(?i)\.js|\.jse|appdata|downloads|temp'
} AS SuspiciousScriptHosts,
{
  SELECT Key.Path AS RunKey, ValueName, String(value=ValueData) AS ValueData
  FROM stat(filename='')
  WHERE FALSE
} AS Placeholder
FROM scope()
VQL — Velociraptor
-- Gootloader persistence sweep: Run keys containing script interpreters
SELECT Mtime, Key.Path AS KeyPath, Name AS ValueName,
       Data.value AS ValueData
FROM foreach(row={
  SELECT FullPath FROM glob(globs='HKEY_USERS/*/Software/Microsoft/Windows/CurrentVersion/Run*')
}, query={
  SELECT Mtime, Key, Name, Data
  FROM read_reg_key(globs='HKEY_USERS/*/Software/Microsoft/Windows/CurrentVersion/Run*/*')
})
WHERE ValueData =~ '(?i)wscript|cscript|\.js|\.jse|\.vbs'
  AND ValueData =~ '(?i)appdata|programdata|temp|downloads'

Note: adapt the registry glob to your Velociraptor version — Windows.Registry.Hunter or the Windows.Sys.Users + raw registry accessors work well if the inline glob above isn't supported in your deployment.

Remediation & Verification Script

Run this on endpoints flagged by detection logic or listed in victim notifications. It inventories persistence, script-host executions, and suspicious JS artifacts, and produces a triage report.

PowerShell
# Gootloader triage and verification script - run elevated
$report = "$env:TEMP\gootloader_triage_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt"
"=== Gootloader Triage Report - $env:COMPUTERNAME ===" | Out-File $report

# 1. Audit Run/RunOnce keys for script-based persistence
"`n[+] Run Key Persistence Entries:`n" | Out-File $report -Append
$runPaths = @(
  'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
  'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce',
  'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
  'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce',
  'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run'
)
foreach ($path in $runPaths) {
  if (Test-Path $path) {
    (Get-ItemProperty $path).PSObject.Properties |
      Where-Object { $_.Value -match '\.js|\.jse|\.vbs|wscript|cscript' -and $_.Name -notmatch '^PS' } |
      ForEach-Object { "$path :: $($_.Name) = $($_.Value)" | Out-File $report -Append }
  }
}

# 2. Find JavaScript files in user-writable staging paths (last 90 days)
"`n[+] Suspicious JS files in AppData/Temp/Downloads (last 90 days):`n" | Out-File $report -Append
$cutoff = (Get-Date).AddDays(-90)
Get-ChildItem -Path "$env:APPDATA","$env:LOCALAPPDATA\Temp","$env:USERPROFILE\Downloads","C:\ProgramData" `
  -Recurse -Include *.js,*.jse -ErrorAction SilentlyContinue |
  Where-Object { $_.LastWriteTime -gt $cutoff } |
  Select-Object FullName, Length, LastWriteTime |
  Out-File $report -Append

# 3. Scheduled tasks invoking script interpreters or scripts
"`n[+] Scheduled tasks referencing scripts:`n" | Out-File $report -Append
Get-ScheduledTask | ForEach-Object {
  $actions = $_.Actions | Out-String
  if ($actions -match 'wscript|cscript|powershell.*\.js|\.jse') {
    "$($_.TaskPath)$($_.TaskName) :: $actions" | Out-File $report -Append
  }
}

# 4. Verify script-host hardening status (recommended: disable WSH for standard users via GPO)
"`n[+] WSH status (0 = disabled, recommended for non-admin endpoints):`n" | Out-File $report -Append
$wsh = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings' -Name Enabled -ErrorAction SilentlyContinue
if ($wsh) { "WSH Enabled = $($wsh.Enabled)" | Out-File $report -Append } else { "WSH setting not configured (default: enabled)" | Out-File $report -Append }

Write-Host "Triage complete. Report: $report"

Remediation

  1. Identify infected endpoints immediately. If the FBI/DOJ victim notification reaches you (directly or via your ISP/CERT), treat it as a confirmed incident, not an alert. Cross-reference against your own telemetry using the hunts above — notifications lag reality.
  2. Isolate before you clean. Gootloader infections correlate strongly with ransomware staging. Assume follow-on access (Cobalt Strike, SystemBC, credential theft) until proven otherwise. Isolate the host, capture memory if DFIR capability exists, then reimage. Gootloader's in-memory staging means artifacts may be sparse — memory capture is valuable.
  3. Hunt laterally. Loader infections on one endpoint justify a fleet-wide sweep for the TTPs above: wscript-spawned PowerShell, Run-key script persistence, JS in user paths. The 25,000-infection figure means operators had volume access — check for other initial access in the same window.
  4. Reset credentials for affected users and hosts. Assume any credentials typed, cached, or stored on an infected machine are compromised. Include service accounts and local admin passwords via LAPS rotation.
  5. Neutralize the delivery vector.
    • Block or restrict .js/.jse execution by standard users. Change the default file association for script files from WSH to Notepad via GPO — this single control kills the double-click execution chain.
    • Consider disabling Windows Script Host entirely (HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings\Enabled = 0) for endpoints whose users don't legitimately run scripts.
    • Configure proxy/DNS controls to flag ZIP downloads containing script files from low-reputation domains, and alert on search-referred downloads of archives containing .js.
  6. Block seized and sinkholed domains at your resolver anyway — seized domains have historically been transferred, expired, or re-registered. Add them to your blocklists rather than assuming law enforcement custody is permanent.
  7. Monitor for rebuild. Qakbot and Emotet both returned after takedowns. Keep the detections in this post active; Gootloader operators or their customers have demonstrated reconstitution capability. Expect the SEO-poisoning vector to survive even if this specific infrastructure does not.
  8. User awareness, targeted. Gootloader victims were searching for business templates. Brief your users — especially legal, finance, and HR — that search results for document templates are an active malware delivery channel. This is a more effective control than generic phishing training for this specific threat.

Related Resources

Security Arsenal Incident Response Services AlertMonitor Platform Book a SOC Assessment incident-response Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.