Sekoia researchers Amaury-Jacques Garçon and colleagues have attributed a large-scale watering hole campaign to APT29 (aka Cozy Bear / Midnight Blizzard / Nobelium), the threat actor linked to Russia's SVR intelligence service. The campaign targets iOS and Android users through three parallel delivery vectors: compromised legitimate websites, attacker-controlled fake domains, and — most concerning from a supply-chain perspective — the hijacking of Plerdy SEO Tracker, a legitimate Ukrainian web analytics service whose JavaScript tag is embedded across thousands of customer websites.
This is a classic APT29 playbook move: rather than compromising each target individually, subvert a trusted third-party script that target organizations load into their own pages by design. When your marketing team embeds an analytics tag, that JavaScript executes with full DOM access in every visitor's browser — including your customers on mobile devices. The injected code collects browser and device attributes, enabling the attackers to fingerprint victims, profile devices, and selectively deliver follow-on payloads to high-value targets while remaining invisible to everyone else.
Every organization whose websites load third-party analytics or tag-manager scripts needs to treat this as an active supply-chain exposure. Defenders must verify whether Plerdy SEO Tracker (or any third-party script) is present in their web properties, validate script integrity, and hunt for anomalous outbound connections from mobile user agents. This post gives you the detection content and remediation steps to do exactly that.
Technical Analysis
Attack Overview
APT29's campaign combines three distinct delivery mechanisms:
-
Compromised legitimate websites (watering holes): Websites frequented by targets of interest are compromised, and malicious JavaScript is injected into pages. When a victim browses the site on an iOS or Android device, the script executes.
-
Fake attacker-controlled domains: Look-alike domains impersonate legitimate services and serve the malicious script directly, likely used for redirection chains and payload staging.
-
Supply chain compromise of Plerdy SEO Tracker: The attackers subverted the analytics provider's served JavaScript so that every website embedding the tracker tag unknowingly delivered attacker-controlled code to its visitors. Because the Plerdy script is loaded via a
<script>tag pointing at Plerdy's infrastructure, the compromise propagated instantly to all customer sites without any change on the customer's side.
Fingerprinting and Victim Selection
Per Sekoia's reporting, the injected JavaScript collects browser and device attributes — user-agent strings, screen resolution, platform identifiers, language settings, timezone, and likely installed font/plugin characteristics. This serves two purposes:
- Target filtering: Only victims matching specific profiles (particular countries, device types, languages, or organizational footprints) receive the second-stage payload. Everyone else is silently ignored, dramatically reducing detection surface.
- Anti-analysis: Sandboxes, security researchers, and emulated environments can be fingerprinted and served benign content, frustrating dynamic analysis.
This selective-exploitation pattern is consistent with prior APT29 operations (e.g., the SolarWinds SUNBURST campaign's victim filtering) and is a hallmark of SVR tradecraft: quiet, precise, and patient.
Affected Platforms and Exposure Surface
- End-user devices: iOS and Android browsers are the primary target population. The mobile focus is notable — mobile devices typically lack EDR coverage and enterprise browsing happens largely outside traditional proxy inspection on cellular networks.
- Web properties: Any website embedding the Plerdy SEO Tracker tag is an unwitting distribution point. If your organization uses Plerdy for SEO/UX analytics, your customers are exposed through your site.
- Organizations of interest: Consistent with APT29's mission set, likely targets include government, diplomatic, defense, technology, and NGO sectors.
No CVE has been assigned — this is not a software vulnerability in the traditional sense. It is a supply-chain abuse of trusted script delivery. That means patching won't save you; script governance, integrity controls, and network-layer detection will.
Exploitation Status
- Active in the wild: Confirmed. Sekoia reports this as an ongoing, large-scale campaign.
- Attribution: Presumed APT29 (Russia SVR), assessed by Sekoia's threat intelligence team.
- CISA KEV: Not applicable — no CVE exists for this campaign.
Detection & Response
The challenge here is that the malicious activity happens in the victim's browser, delivered through legitimate HTTPS to legitimate-looking analytics infrastructure. Traditional AV and EDR see nothing. Effective detection therefore concentrates on three layers:
- Web property integrity — finding unauthorized or unexpected third-party script references in your own sites.
- Network egress — identifying mobile user agents connecting to suspicious analytics-adjacent or look-alike domains, and detecting unusual script-fetch patterns.
- Endpoint/browser telemetry — hunting for anomalous child processes or connections spawned shortly after browser script execution on managed devices.
Sigma Rules
These rules target observable behaviors derived from the campaign: unexpected third-party script loads on web servers under your control, and browsers initiating connections consistent with fingerprinting/injection follow-on activity. Tune the domain lists against your own sanctioned analytics inventory before deploying.
---
title: Unauthorized Third-Party Script Reference in Web Content
description: Detects web server content files modified to include external script tags referencing analytics or tracking domains not on the approved inventory, consistent with watering hole script injection as seen in the APT29/Plerdy campaign.
author: Security Arsenal
date: 2026/11/05
status: experimental
references:
- https://thehackernews.com/2026/11/apt29-linked-hackers-hijack-web.html
- https://attack.mitre.org/techniques/T1189/
- https://attack.mitre.org/techniques/T1195/002/
logsource:
category: file_event
product: linux
detection:
selection_paths:
TargetFilename|contains:
- '/var/www/'
- '/srv/www/'
- '/usr/share/nginx/'
- '/opt/bitnami/'
selection_ext:
TargetFilename|endswith:
- '.html'
- '.php'
- '.js'
- '.aspx'
- '.jsp'
condition: selection_paths and selection_ext
falsepositives:
- Legitimate CMS deployments and template updates
- CI/CD pipeline pushes to web roots
level: medium
---
title: Browser Process Spawning Script Interpreter After Mobile Fingerprinting Activity
description: Detects a browser process spawning a scripting interpreter or shell, a potential indicator of drive-by exploitation follow-on activity after malicious JavaScript execution in a watering hole scenario.
author: Security Arsenal
date: 2026/11/05
status: experimental
references:
- https://thehackernews.com/2026/11/apt29-linked-hackers-hijack-web.html
- https://attack.mitre.org/techniques/T1189/
- https://attack.mitre.org/techniques/T1059/
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\firefox.exe'
- '\brave.exe'
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
- '\rundll32.exe'
condition: selection_parent and selection_child
falsepositives:
- Browser extensions with native messaging hosts (rare for shells)
- Enterprise SSO helpers
level: high
---
title: Outbound Connection to Look-Alike Analytics Domain Pattern
description: Detects DNS or network connections to domains combining analytics/tracker keywords on recently-registered or non-standard TLDs, a pattern consistent with APT29 fake-domain staging infrastructure supporting the watering hole campaign.
author: Security Arsenal
date: 2026/11/05
status: experimental
references:
- https://thehackernews.com/2026/11/apt29-linked-hackers-hijack-web.html
- https://attack.mitre.org/techniques/T1071/001/
- https://attack.mitre.org/techniques/T1568/
logsource:
category: dns
detection:
selection_keyword:
query|contains:
- 'analytics'
- 'tracker'
- 'metrics'
- 'telemetry'
- 'stats-'
selection_suspicious_tld:
query|endswith:
- '.xyz'
- '.top'
- '.icu'
- '.click'
- '.info'
- '.online'
condition: selection_keyword and selection_suspicious_tld
falsepositives:
- Legitimate low-cost analytics startups on cheap TLDs
- Marketing campaign microsites
level: low
The first rule is intentionally scoped to file modification in web roots — pair it with your change-management data so known deployments don't page the SOC at 3 a.m. The third rule is a low-severity enrichment signal, not an alert-by-itself rule; correlate it with the KQL hunt below.
KQL Hunt (Microsoft Sentinel / Defender)
This query hunts for iOS and Android user agents generating outbound web requests to domains that are not in your sanctioned analytics inventory — the exact egress pattern produced when hijacked analytics tags beacon or pull second-stage content. It also surfaces rare domains visited by mobile devices for the first time in the lookback window.
// APT29 watering hole hunt: mobile user agents connecting to unsanctioned analytics/tracking domains
let lookback = 14d;
let sanctioned_analytics = dynamic(["google-analytics.com", "googletagmanager.com", "matomo.org", "adobe.com", "hotjar.com", "mixpanel.com", "segment.io"]);
let mobile_agents = dynamic(["iPhone", "iPad", "Android", "Mobile Safari", "CriOS", "EdgiOS"]);
DeviceNetworkEvents
| where TimeGenerated > ago(lookback)
| where ActionType == "ConnectionSuccess"
| where RemoteUrl has_any ("analytics", "tracker", "metric", "stats", "telemetry", "tag")
| where not(RemoteUrl has_any (sanctioned_analytics))
| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), DeviceCount = dcount(DeviceId), Devices = make_set(DeviceName, 20), InitiatingProcesses = make_set(InitiatingProcessFileName, 10) by RemoteUrl, RemoteIP
| where DeviceCount >= 1
| order by FirstSeen asc
| project FirstSeen, LastSeen, RemoteUrl, RemoteIP, DeviceCount, Devices, InitiatingProcesses;
// Supplementary: browser-spawned child processes (drive-by follow-on execution)
DeviceProcessEvents
| where TimeGenerated > ago(lookback)
| where InitiatingProcessFileName in~ ("chrome.exe", "msedge.exe", "firefox.exe", "brave.exe")
| where FileName in~ ("powershell.exe", "pwsh.exe", "cmd.exe", "wscript.exe", "cscript.exe", "mshta.exe", "rundll32.exe")
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| order by TimeGenerated desc;
Customize sanctioned_analytics to reflect your organization's approved tag inventory — including your verdict on Plerdy. If your organization does legitimately use Plerdy, keep it in the blocklist for now and treat any hits as requiring validation against the Sekoia IoCs, since the provider's own infrastructure was the compromise vector.
Velociraptor VQL
For DFIR teams validating whether a managed endpoint executed suspicious post-browse behavior, this artifact enumerates browser-spawned processes and correlates them with recent network activity — useful when scoping whether a visiting executive's device was hit by the watering hole.
-- Hunt browser-spawned child processes with active/recent network connections
-- Relevant to APT29 watering hole post-exploitation triage
SELECT Pid,
Ppid,
Name,
Exe,
CommandLine,
Username,
CreateTime
FROM pslist()
WHERE Ppid IN (
SELECT Pid
FROM pslist()
WHERE Name =~ '(?i)chrome|msedge|firefox|brave|safari'
)
AND Name =~ '(?i)powershell|pwsh|cmd\.exe|wscript|cscript|mshta|rundll32|sh$|bash$|zsh$'
-- Enumerate outbound connections from browser processes to non-standard ports
-- Supports identification of C2 or staging callbacks following script execution
SELECT Pid,
Name,
Exe,
Family,
Type,
Laddr,
Raddr,
Status
FROM netstat()
WHERE Name =~ '(?i)chrome|msedge|firefox|brave|safari'
AND Status =~ '(?i)ESTAB|SYN'
AND Raddr.IP !~ '^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)'
Remediation / Verification Script
For web property owners, the immediate task is determining whether your sites reference Plerdy or other unauthorized third-party scripts. This Bash script scans your web root(s) for external script references, flags Plerdy specifically, and outputs an inventory for review against your approved tag list.
#!/usr/bin/env bash
# apt29-wateringhole-audit.sh
# Scans web roots for third-party <script src> references, flags Plerdy SEO Tracker
# and builds an inventory of all externally loaded scripts for governance review.
set -euo pipefail
WEB_ROOTS=("/var/www" "/srv/www" "/usr/share/nginx/html" "/opt/bitnami")
REPORT="./third-party-script-audit-$(date +%Y%m%d-%H%M%S).txt"
APPROVED_DOMAINS="google-analytics.com|googletagmanager.com|matomo|adobe|cloudflare" # EDIT THIS
echo "=== Third-Party Script Audit — $(date -u) ===" | tee "$REPORT"
for ROOT in "${WEB_ROOTS[@]}"; do
[ -d "$ROOT" ] || continue
echo "[+] Scanning $ROOT ..." | tee -a "$REPORT"
# 1. Plerdy-specific check (active campaign indicator)
PLERDY_HITS=$(grep -rniE 'plerdy|seotracker' "$ROOT" \
--include='*.html' --include='*.htm' --include='*.php' \
--include='*.js' --include='*.tpl' --include='*.twig' 2>/dev/null || true)
if [ -n "$PLERDY_HITS" ]; then
echo "[!] ALERT: Plerdy SEO Tracker references found:" | tee -a "$REPORT"
echo "$PLERDY_HITS" | tee -a "$REPORT"
fi
# 2. Full external script inventory
echo "[i] External script sources in $ROOT:" | tee -a "$REPORT"
grep -rhoiE '<script[^>]+src=["'"'"'][^"'"'"']+["'"'"']' "$ROOT" \
--include='*.html' --include='*.htm' --include='*.php' \
--include='*.tpl' --include='*.twig' 2>/dev/null \
| grep -oiE 'src=["'"'"'][^"'"'"']+' \
| sed -E 's/src=["'"'"']//I' \
| grep -E '^https?://' \
| sort -u | tee -a "$REPORT"
done
# 3. Flag any external scripts NOT on the approved list
echo "" | tee -a "$REPORT"
echo "=== UNAUTHORIZED / REVIEW REQUIRED ===" | tee -a "$REPORT"
grep -E '^https?://' "$REPORT" | grep -viE "$APPROVED_DOMAINS" | sort -u | tee -a "$REPORT" || echo "None found." | tee -a "$REPORT"
echo ""
echo "Audit complete. Review flagged domains against Sekoia IoCs and your approved tag inventory: $REPORT"
On Windows/IIS-hosted properties, run the equivalent sweep in PowerShell:
# Audit IIS web roots for unauthorized third-party script references (incl. Plerdy)
$webRoots = @("C:\inetpub\wwwroot")
$approved = "google-analytics|googletagmanager|cloudflare|adobe" # EDIT THIS
$files = Get-ChildItem -Path $webRoots -Recurse -Include *.html,*.htm,*.aspx,*.cshtml,*.js -ErrorAction SilentlyContinue
$hits = $files | Select-String -Pattern '<script[^>]+src=["'']([^"'']+)' -AllMatches |
ForEach-Object { $_.Matches.Groups[1].Value } | Where-Object { $_ -match '^https?://' } | Sort-Object -Unique
Write-Output "=== All external script sources ==="
$hits
Write-Output "`n=== Plerdy / SEO Tracker references (ACTIVE CAMPAIGN INDICATOR) ==="
$files | Select-String -Pattern 'plerdy|seotracker' -List | Format-Table Path, LineNumber, Line -AutoSize
Write-Output "`n=== Unauthorized / review required ==="
$hits | Where-Object { $_ -notmatch $approved }
Remediation
For organizations that operate websites:
-
Audit immediately. Run the scripts above across every web property you own — including marketing microsites, campaign landing pages, and regional sites that central IT often forgets about. If Plerdy SEO Tracker is present, you must assume your visitors have been exposed to the injected code until proven otherwise against Sekoia's IoCs.
-
Remove or suspend the Plerdy tag until Plerdy publishes a verified-clean statement and Sekoia's indicators are cleared. Contact Plerdy support directly for their incident response status. Do not rely on "we only use it on low-value pages" — the watering hole model means any page is a distribution point.
-
Implement Subresource Integrity (SRI) where feasible. SRI (
integrity="sha384-..."attributes on<script>tags) causes browsers to refuse execution if the fetched script doesn't match the expected hash. Caveat: SRI only works for static script URLs. Dynamically-generated analytics tags (like most tracker loaders) can't use SRI directly — which is precisely why these services are attractive supply-chain targets. For those, use strict Content Security Policy. -
Deploy a strict Content Security Policy. A CSP with an explicit
script-srcallowlist means that even if an attacker injects a<script>tag pointing at an unauthorized domain, the browser refuses to load it. This is the single most effective structural defense against watering hole injection:
Content-Security-Policy: script-src 'self' https://approved-analytics.example.com; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Start in Content-Security-Policy-Report-Only mode with a reporting endpoint so you can inventory legitimate script sources before enforcement — deploying a blocking CSP blind will break your site.
- Govern your tag manager. If scripts are deployed via Google Tag Manager or similar, restrict who can publish containers, enable two-person approval for tag changes, and review the full published tag inventory quarterly. Tag managers are the soft underbelly of script governance.
For organizations protecting end users / employees:
-
Extend mobile coverage. This campaign specifically targets iOS and Android. Deploy mobile threat defense (MTD) or at minimum ensure managed devices route browsing through a DNS-filtering service (e.g., your secure DNS resolver with threat-intel blocking) even on cellular. DNS-layer blocking of Sekoia-published IoC domains is your fastest kill.
-
Ingest Sekoia's IoCs. Pull the published indicators (domains, URLs, script hashes) into your DNS filter, web proxy, and SIEM. Hunt retroactively for 30+ days of traffic to those indicators — APT29 operations have long dwell times by design.
-
Brief high-risk users. Given APT29's mission set, executives, diplomats, and staff handling sensitive government or defense-adjacent work should be briefed that state-sponsored actors are actively profiling mobile browsers, and directed to use managed, filtered devices for web browsing.
-
Monitor for follow-on activity. Fingerprinting is stage one. Watch for the browser-spawned child processes covered in the Sigma rules above, anomalous MDM profile installation prompts on iOS, and unexpected calendar/contacts permission grants — common mobile post-exploitation artifacts.
Analyst's Note
The strategic lesson here is the same one SolarWinds taught us and the industry keeps re-learning: third-party JavaScript is third-party code execution inside your trust boundary. Every analytics tag, A/B testing script, chat widget, and SEO tracker on your site is a standing invitation for whoever controls that infrastructure to run code in your visitors' browsers. APT29 understands this asymmetry better than most. Treat your script inventory like your software inventory: know it, minimize it, integrity-check it, and be able to answer "who can push code to our visitors" in under five minutes.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.