Insights / Threat Brief / 28 Sep 2026 · Morning

Threat Brief • 28 September 2026 • Morning Edition

Patched Is Not Clean: Citrix Confirms the NetScaler Zero-Days, and a Provider’s Fix Is Not Your Rotation

Citrix has confirmed the two NetScaler zero-days and shipped fixes, but attackers had most of September and an update does not remove what they left behind. Plus: a Cloudflare Containers flaw the provider says needs no customer action, and why rotating anyway is cheap.

By Fezz · Threat Vector Management · September 28, 2026

Research, fact-checking and graphics support by Claude (Anthropic)

Status as of publication on Monday, 28 September 2026, US Eastern. Item 1 is an active exploitation campaign with fixes available; no network indicators have been published. Where anything here differs from a vendor’s own advisory to you, follow the advisory.

: added a hunt pack to every item — search queries and scripts, what each result means, and what to do. Packs were fact-checked twice against this page; the bash scripts were run on synthetic data and the Sigma rules were converted for three platforms. Everything else is labelled as reviewed, not run. No indicator, version or recommendation above changed.

BLUF

Bottom Line Up Front

How to read this. The table below gives the fix and the most huntable indicators. Each item opens with its own bottom line and indicator table; the NetScaler item is followed by a short table of the order of operations. Reported facts are cited; priority, PIVCCERL™ staging and reasoning are our analysis and labeled.

At a glance: fixes and indicators

Written for organizations and the teams who defend them. IoC = evidence it already happened (an address, an account, a log line). IoA = the behavior of an attack in progress. Network IoCs are defanged: remove the square brackets before using them. Indicators age fast, so re-check the source before blocking.

Item Exploited? · Fix Do now Top IoCs Top IoAs
1. Citrix NetScaler ADC / Gateway CVE-2026-88771, CVE-2026-88772 and six more Yes, confirmed by Citrix · 14.1-73.37, 13.1-64.23, 14.1-73.37 FIPS, 13.1-37.279 FIPS/NDcPP Capture logs, configuration and a core dump; update; check every appliance for web shells; rotate credentials Log lines matching pitboss*IFS or pitboss*b64decode; a base64 string directly after the User-Agent field A shell or unexpected process started by the appliance's web services; web-served files that match no known-good build
The order that keeps evidence — Follow the table: evidence, update, hunt, rotate, then decide on rebuild — —
2. Cloudflare Containers / Sandboxes cross-tenant residual data (no CVE) No malicious exploitation found, per Cloudflare · Fixed by the provider; cleanup complete 19 Sep Rotate secrets that workloads wrote to container disk before 19 Sep; move secrets out of files None published — the telemetry is the provider's Use of a key or token that lived in a container file, from an address outside the organization's workloads

Highest payoff today — analysis. If NetScaler is in your estate, the single most valuable move is to capture evidence before you update: logs, configuration, a support bundle and a core dump. Exploitation began weeks before disclosure, logs rotate, and an update or reboot can erase exactly what would tell you whether you were inside the window. Updating first feels responsible and can leave you unable to answer the only question that matters afterward.

How to use this. A published-IoC hit means compromise: handle it under the item's defined response. An IoA hit alone is a lead to validate (PIVCCERL™'s Validation stage). A clean result is not reassurance here — the vendor says its own indicators do not cover every technique.

1. NetScaler zero-days confirmed: two exploited, eight fixed, and the update does not evict

Bottom line

Citrix published bulletin CTX697096 on 27 September covering eight NetScaler ADC and Gateway flaws, and states that exploits of CVE-2026-88771 and CVE-2026-88772 "on unmitigated NetScaler deployments have been observed." Both are rated CVSS 4.0 9.5 and allow remote code execution without authentication. Do now: capture evidence, update to 14.1-73.37 or 13.1-64.23 (or the FIPS/NDcPP builds listed below), check every appliance for web shells, and rotate what it held. Skip if: you run no NetScaler ADC, NetScaler Gateway or Secure Private Access hybrid deployment that uses NetScaler instances. If your remote access or load balancing is a different vendor, this one does not apply to you.

Indicators

Type Indicator Where to look Source
IoC Log lines matching pitboss*IFS or pitboss*b64decode SIEM copies of appliance logs — the appliance's own logs have likely rotated past the start of the activity Kevin Beaumont, 27 Sep 2026
IoC A base64 string immediately after the User-Agent field, no space between them SIEM copies of appliance HTTP logs Kevin Beaumont, 27 Sep 2026
IoC Web shells, which Beaumont describes as placed throughout September; no file names or hashes published Appliance filesystem, web-served directories, forensic image Kevin Beaumont, 27 Sep 2026
IoC Citrix's indicator scan, run from the NetScaler Console security advisory page (requires product telemetry); vendor warns it "might fail to identify actual compromises" NetScaler Console Citrix NetScaler Console documentation; watchTowr, 27 Sep 2026
IoA A shell, scripting interpreter or unexpected child process started by the appliance's web or management services Appliance process listings, core dump, forensic image Ours
IoA Configuration changes, new local accounts or SSH keys nobody made; outbound connections from the appliance to hosts outside its normal set ns.conf history, audit logs, flow logs Ours
IoA Gateway sessions reused from a new address or country mid-session Gateway session and authentication logs Ours

What

Citrix's bulletin CTX697096, published 27 September 2026, covers eight CVEs in NetScaler ADC and NetScaler Gateway. CVE-2026-88771 — improper input validation (CWE-20) that "can allow an unauthenticated attacker to execute arbitrary commands"; CVSS 4.0 9.5; Citrix lists it as affecting all deployments on affected versions, including the default configuration. CVE-2026-88772 — a memory overflow (CWE-119) leading to remote code execution or denial of service; CVSS 4.0 9.5; applies where DTLS is enabled, which is the default on VPN virtual servers. The others: CVE-2026-88773, HTTP request smuggling, CVSS 9.3; CVE-2026-88774, a policy bypass through HTTP URL-based expressions, 7.0; CVE-2026-88775, -88776 and -88777, memory overflows leading to denial of service in Gateway/AAA, Oracle-type load balancing and non-HTTP layer 7 configurations respectively, 8.8 each; and CVE-2026-88778, predictable TCP initial sequence numbers, 8.8, which needs Enhanced ISN Generation turned on (verify with show ns tcpparam | grep "Enhanced ISN Generation").

Affected: NetScaler ADC and Gateway 14.1 before 14.1-73.37; 13.1 before 13.1-64.23; ADC FIPS before 14.1-73.37 FIPS; ADC FIPS and NDcPP before 13.1-37.279. watchTowr notes Secure Private Access hybrid deployments that use NetScaler instances are also affected.

The Canadian Centre for Cyber Security (AL26-024, 27 September) reports exploitation "observed across multiple Citrix customer environments worldwide" and advises preserving forensic evidence "before shutting down, rebooting, patching, rebuilding, or otherwise modifying affected appliances, where feasible." CISA said it "has received reports and partner threat intelligence confirming that threat actors are actively exploiting these vulnerabilities globally," as carried by The Hacker News, CISA's Known Exploited Vulnerabilities catalog (version 2026.09.27) lists both as added 27 September with a US federal remediation deadline of 30 September; its required action cites Binding Operational Directive 26-04 and CISA's "Forensics Triage Requirements." Shadowserver counted over 23,000 NetScaler fingerprints exposed to the internet, per BleepingComputer — a count of exposed appliances, not of vulnerable or compromised ones.

Where sources differ. Citrix names CVE-2026-88771 and CVE-2026-88772 as exploited. Kevin Beaumont wrote on 27 September that "the primary vulns being exploited are CVE-2026-88771, CVE-2026-88772, CVE-2026-88773 chained," that attackers have been "using it to drop webshells all month of September," and that "patching alone doesn't remove the backdoors being placed." We plan on Beaumont's wider reading for scoping: the fix is the same update either way, and assuming a September-long window costs a longer log review, while assuming a short one can miss a compromise.

So what

Code execution on a gateway means the session tokens and credentials of everyone who signs in through it, plus a foothold facing the internal network. The trap this time is the order of operations: exploitation ran for weeks before any fix, the appliance's own logs have probably rotated past the start, and the vendor's scan is openly incomplete. An appliance that was reachable in September needs checking, not assuming — and an update applied first can destroy the evidence that would have settled it.

Now what — I → V (Identification and Validation for any internet-facing NetScaler on an affected build during September; P — Preparation — for everyone else; analysis)

Hunt pack — run it, then act on what it says

The indicators are Kevin Beaumont's, published 27 September, plus Citrix's own scan: log lines matching pitboss*IFS or pitboss*b64decode, a base64 string directly after the User-Agent field with no space between them, web shells (no file name or hash is published), and process, configuration and session anomalies that the page marks as ours. The hunts are our analysis of those indicators. The page does not say exactly how the User-Agent field appears in the appliance's log, so the pattern accepts an optional : or = and a quote, and the 20-character minimum is our noise filter: tune it. Order matters: capture logs, configuration, a support bundle and a core dump before you update, because an update can destroy the evidence. Run both scripts against copies and images, not the live appliance; a copy of the web directories must preserve file times (cp -a, rsync -a or a forensic image), or every file will look new, and a directory the script cannot fully read ends in exit 3 unless something was found. The two bash scripts were run against synthetic logs and directories (hit, clean, empty, missing, compressed and look-alike inputs); the Sigma rule was parsed and converted to Splunk, Kusto and Lucene queries with pySigma; the Splunk, KQL and ES|QL were reviewed, not executed against a live platform, so test them on a non-production index first. We are not offering PowerShell, Suricata, YARA or osquery for this item: the appliance is not a Windows host, its sessions are encrypted, and no web-shell file, hash or sample is published. Printed log lines (up to 300 characters) can carry tokens from query strings: review them before sharing the output. Adjust field and table names to your logging.

bash — SIEM exports or syslog copies of appliance logs, plain or .gz, export from 1 September onward, the script scans whatever you give it; exact appliance log wording is not verified on a live NetScaler; exit 0 = nothing found, 1 = found, 3 = nothing readable, any file unreadable or a truncated .gz (not a clean result)
#!/usr/bin/env bash
# Usage: ./netscaler-log-hunt.sh LOGFILE...     (SIEM exports or syslog copies of appliance logs; plain or .gz; needs python3)
# Read-only. Exit 0 = nothing found, 1 = found, 2 = usage, 3 = nothing readable, any file unreadable or a truncated .gz (NOT a clean result).
[ $# -ge 1 ] || { echo "usage: $0 LOGFILE..." >&2; exit 2; }
command -v python3 >/dev/null || { echo "python3 is required (NOT a clean result)" >&2; exit 3; }
files=(); skipped=0
for f in "$@"; do if [ -f "$f" ] && [ -r "$f" ]; then files+=("$f"); else echo "skipping unreadable: $f" >&2; skipped=1; fi; done
[ "${#files[@]}" -gt 0 ] || { echo "no readable log files (NOT a clean result)" >&2; exit 3; }
gzip -dcf -- "${files[@]}" 2>/dev/null | python3 -c '
import re, sys
cats = [
 ("PITBOSS-IFS",       re.compile(r"pitboss.*IFS")),
 ("PITBOSS-B64DECODE", re.compile(r"pitboss.*b64decode")),
 # base64-looking run directly after User-Agent (optional : or = or quote, no space); 20+ chars is our noise filter
 ("UA-BASE64",         re.compile(r"User-Agent[:=]?\"?[A-Za-z0-9+/]{20,}={0,2}")),
]
n = 0
counts = dict((c[0], 0) for c in cats)
for raw in sys.stdin.buffer:
    n += 1
    line = raw.decode("utf-8", "replace").rstrip()
    tags = [name for name, rx in cats if rx.search(line)]
    for t in tags: counts[t] += 1
    if tags: print("%d: [%s] %s" % (n, ",".join(tags), line[:300]))
print("scanned=%d " % n + " ".join("%s=%d" % (k, counts[k]) for k, _ in cats), file=sys.stderr)
sys.exit(3 if n == 0 else (1 if sum(counts.values()) else 0))
'
st=("${PIPESTATUS[@]}"); rc=${st[1]}; [ "${st[0]}" -eq 0 ] || { echo "a file could not be fully decompressed (NOT a clean result)" >&2; skipped=1; }
[ "$rc" -ne 0 ] || [ "$skipped" -eq 0 ] || { echo "some files were unreadable (NOT a clean result)" >&2; exit 3; }
exit $rc
bash — a forensic copy or mounted image of the appliance's web-served directories, with timestamps preserved (cp -a, rsync -a or a forensic image); lists files modified since 1 September unless you pass another date; exit 0 = nothing found, 1 = found, 3 = nothing scanned or some paths unreadable (not a clean result)
#!/usr/bin/env bash
# Usage: ./ns-webdir-hunt.sh DIR [YYYY-MM-DD]     (date defaults to 2026-09-01)
# Run it against a forensic copy or mounted image of the appliance's web-served directories, NOT the live appliance.
# The copy must preserve file times (cp -a, rsync -a or a forensic image); a plain copy makes every file look new.
# Lists files modified on or after the date. File times can be forged, so a quiet result proves little.
# Read-only. Exit 0 = nothing found, 1 = found, 2 = usage, 3 = nothing scanned or some paths unreadable (NOT a clean result).
[ $# -ge 1 ] && [ $# -le 2 ] || { echo "usage: $0 DIR [YYYY-MM-DD]" >&2; exit 2; }
dir=$1; since=${2:-2026-09-01}
case $since in [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) ;; *) echo "usage: date must be YYYY-MM-DD" >&2; exit 2;; esac
y=$((10#${since:0:4})); m=$((10#${since:5:2})); d=$((10#${since:8:2}))
case $m in 1|3|5|7|8|10|12) dm=31;; 4|6|9|11) dm=30;; 2) if [ $((y%4)) -eq 0 ] && { [ $((y%100)) -ne 0 ] || [ $((y%400)) -eq 0 ]; }; then dm=29; else dm=28; fi;; *) dm=0;; esac
if [ "$d" -lt 1 ] || [ "$d" -gt "$dm" ]; then echo "usage: $since is not a real calendar date" >&2; exit 2; fi
if [ ! -d "$dir" ] || [ ! -r "$dir" ]; then echo "cannot read directory: $dir (NOT a clean result)" >&2; exit 3; fi
scanned=$(set -o pipefail; find "$dir" -type f 2>/dev/null | wc -l | tr -d ' '); rc=$?
if [ "$scanned" -eq 0 ]; then echo "scanned=0: no files found (NOT a clean result)" >&2; exit 3; fi
changed=0
list=$(find "$dir" -type f -newermt "$since" -print 2>/dev/null) || rc=1
if [ -n "$list" ]; then while IFS= read -r f; do echo "CHANGED: ${f:0:300}"; changed=$((changed+1)); done <<< "$list"; fi
echo "scanned=$scanned changed_since_$since=$changed" >&2
[ "$rc" -eq 0 ] || echo "some paths unreadable (NOT a clean result)" >&2
[ "$changed" -gt 0 ] && exit 1
[ "$rc" -eq 0 ] || exit 3
exit 0
Sigma — appliance log text from syslog or a SIEM (searches the raw message; exact appliance log wording is not verified on a live NetScaler)
title: NetScaler CVE-2026-88771 and CVE-2026-88772 pitboss log lines
id: 12f37a1c-4d3f-44b0-907e-3ab83b6ba327
status: experimental
description: Log lines matching pitboss followed by IFS or b64decode, the pattern Kevin Beaumont published on 27 September 2026 for the exploitation of the NetScaler flaws.
author: Threat Vector Management
date: 2026-10-06
logsource:
  service: syslog
detection:
  keywords:
    - '*pitboss*IFS*'
    - '*pitboss*b64decode*'
  condition: keywords
falsepositives:
  - None known; confirm any match on the appliance
level: high
Splunk SPL — appliance logs forwarded to Splunk; searches the raw event from 1 September, so it does not depend on field names; exact appliance log wording is not verified on a live NetScaler
index=* earliest="09/01/2026:00:00:00"
| rex field=_raw "(?<pitboss_hit>pitboss.*(IFS|b64decode))"
| rex field=_raw "User-Agent[:=]?\"?(?<ua_run>[A-Za-z0-9+/]{20,}={0,2})"
| where isnotnull(pitboss_hit) OR isnotnull(ua_run)
| stats count min(_time) AS first max(_time) AS last values(ua_run) AS ua_runs BY host, source
Microsoft Sentinel / Log Analytics KQL — appliance syslog in the Syslog table (use CommonSecurityLog and its Message column if you ingest CEF); exact appliance log wording is not verified on a live NetScaler
Syslog
| where TimeGenerated > datetime(2026-09-01)
| where SyslogMessage matches regex @"pitboss.*(IFS|b64decode)" or SyslogMessage matches regex @"User-Agent[:=]?""?[A-Za-z0-9+/]{20,}"
| summarize hits = count(), first = min(TimeGenerated), last = max(TimeGenerated) by HostName
Elastic ES|QL — appliance logs with the raw text in message; RLIKE on a text field may need a keyword-type field in your mapping, and the exact appliance log wording is not verified on a live NetScaler
FROM logs-*
| WHERE @timestamp >= TO_DATETIME("2026-09-01T00:00:00Z")
| WHERE message RLIKE ".*pitboss.*(IFS|b64decode).*" OR message RLIKE ".*User-Agent[:=]?\"?[A-Za-z0-9+/]{20,}.*"
| STATS hits = COUNT(*), first = MIN(@timestamp), last = MAX(@timestamp) BY host.name
If you see Do this
A pitboss log line, a web shell, or a configuration change, new local account or SSH key nobody made (new local accounts and SSH keys are our addition to the page's list) Treat as a confirmed compromise, as the page's Defined response says. Isolate the appliance, keep the evidence you captured, rebuild from a clean image on the fixed build rather than cleaning in place, rotate every credential and key it held (LDAP or RADIUS bind credentials, local administrator passwords, certificates and private keys), and review sign-ins by every account that authenticated through it during September.
A base64 run after User-Agent only (no pitboss line) Treat as a lead in this pack, because the page does not give the exact field layout and our 20-character filter can match benign clients (the page itself classes this string as an IoC; if the full request is only base64 after the field name with no other content, handle it as the first row). Pull the full request and the source address, check that source in the other logs, and look for a web shell and configuration changes on that appliance. Legitimate monitoring or API clients can send odd User-Agent values, so confirm the owner before excluding anything.
Files modified since 1 September in the web-served directories Compare them against a known-good copy of the same build. A file that is not in the known-good build is a suspected web shell: handle it as the first row until the build vendor or your hash comparison explains it. Update-related changes that match the build are expected.
A Gateway session reused from a new address or country mid-session, or outbound connections from the appliance to hosts outside its normal set Treat as a lead to validate with the user and the flow logs, and escalate to the first row if the appliance made the connection. Terminate active sessions after updating.
Citrix's indicator scan flags something, or fails or is unavailable A flag is a hit: handle it as the first row. A clean scan is not a clean result: Citrix warns it might fail to identify actual compromises.
An appliance on a build older than 14.1-73.37, 13.1-64.23 or the FIPS and NDcPP builds the page lists Capture the evidence first, then update to the fixed build for the branch, enable Enhanced ISN Generation for CVE-2026-88778, and keep the management interface (NSIP) off the internet.
The scripts exit 3, or the queries return no rows because the appliance's own logs have rotated and no SIEM copy exists This is not a clean result. Assume the September window was missed, rely on the file review and the rebuild decision, and name who owns that decision now.
No hits A good sign, not an exoneration. The appliance's own logs have probably rotated past the start of the activity, web shells carry no published file name or hash so a directory listing needs a known-good build to compare against, file times can be forged, Citrix's scan is openly incomplete, and the update does not remove a backdoor already in place. If the appliance was reachable in September, check it rather than assume.

The order that keeps evidence — update second, not first

Bottom line

For an appliance that was exposed while a flaw was being exploited, the sequence matters as much as the fix. Do now: follow the rows in order. Skip if: you run no NetScaler — though the same order applies to any edge device with an exploited flaw.

Step Why this order Do this
1. Capture evidence An update or reboot can erase what shows whether you were compromised Logs, configuration, support bundle, core dump; copy SIEM-held logs from the start of September
2. Update Closes the door; does not remove anything already placed 14.1-73.37 / 13.1-64.23 or the FIPS/NDcPP build for the branch in use; enable Enhanced ISN Generation
3. Hunt The vendor's scan is incomplete and local logs have likely rotated Run the indicator scan, search SIEM logs for the pitboss and User-Agent patterns, check web-served files against a known-good build
4. Rotate A gateway compromise is a credential compromise, and rotation is cheaper than proving a negative Bind credentials, admin passwords, certificates and keys; terminate sessions
5. Decide on rebuild Cleaning in place trusts the thing you are investigating Any finding in step 3 means rebuild from a clean image, not repair

2. Cloudflare Containers and Sandboxes: fixed by the provider, but what sat on those disks is yours to rotate

Bottom line

Cloudflare disclosed on 24 September that Containers and Sandboxes reused disk blocks without clearing them, so a customer with a Workers Paid account could recover residual data left by other customers' deleted containers on the same host. The researcher who found it lists what that residue included: "directory listings, SQLite databases, Chromium profiles, .env files, and credential files." Cloudflare fixed it (rollout complete 7 September, older cached snapshots cleared by 19 September), found no evidence of malicious exploitation in the telemetry it holds, and says no customer action is required. Do now (analysis): if workloads ran on Cloudflare Containers or Sandboxes before 19 September, rotate every secret they wrote to local disk, and stop writing secrets to container disk. Skip if: you do not use Cloudflare Containers or Sandboxes. Cloudflare's CDN, WAF, Pages and Workers without containers are not named in the disclosure.

Indicators

Type Indicator Where to look Source
IoC None published. Cloudflare's no-exploitation finding comes from its own historical disk-I/O telemetry, which customers cannot see; there is no customer-side artifact of someone reading a residual block — Cloudflare, 24 Sep 2026
IoA A key, token or password that lived in a container file, used from an address outside the organization's own workloads Audit logs of the service each secret unlocks — cloud provider, SaaS, database, code host Ours
IoA Sessions for accounts that were signed in inside a container's browser profile, appearing from unfamiliar devices or addresses Identity provider and application sign-in logs Ours — the researcher lists Chromium profiles among recoverable data

What

Cloudflare's post of 24 September: its Containers storage used 64 KiB thin blocks with block zeroing disabled, so after a small write to a reused block "the remaining 60 KiB could retain data" from a previous container. "The researchers demonstrated that a customer with a Workers Paid account could recover residual disk blocks," and "observed residual material on 18 of 24 placements and 20 of 22 underlying nodes across four continents." Cloudflare also states: "An attacker could not select a particular victim or access an actively attached disk." Sandboxes, which is built on Containers, was affected too. The flaw was reported on 4 September through Cloudflare's bug bounty by Oren Yomtov of Accomplish; the setting was removed that day, rollout completed 7 September, and pre-mitigation cached snapshots were cleared by 19 September. Cloudflare: "Within the historical disk-I/O telemetry available to us, we identified no evidence of malicious exploitation," and remediation "does not require any further action by Cloudflare customers." No CVE is assigned. Cloudflare does not say when block zeroing was first disabled, so the start of the exposure window is unknown.

Where sources differ. Cloudflare writes that the researchers' scripts "output only aggregate counts and format checks, not recovered file contents." The researcher's write-up says the flaw let "a sandbox read other customers' files" and names the file types above. The two can both be true — format checks identify file types without reading contents — but they read very differently. We plan on the researcher's description of what was recoverable, because it says what a malicious customer could have taken, which is the question a defender has to answer.

So what

The provider's statement answers whether Cloudflare saw abuse in the telemetry it has. It cannot answer whether a secret that sat in a readable block is still one you can vouch for. .env files and credential files are where API keys and database passwords live, and a browser profile holds signed-in sessions — the kind of state AI-agent and browser-automation sandboxes keep. Fixed by the provider closes the hole; it does not tell you what went through it.

Now what — P → V (Preparation, moving to Validation for anyone whose workloads wrote secrets to container disk before 19 September; analysis)

Hunt pack — run it, then act on what it says

Cloudflare publishes no indicator, and the page notes there is no customer-side artifact of someone reading a residual block. What a defender can do is the page's own two steps: list the secrets that workloads wrote to container disk (the researcher names directory listings, SQLite databases, Chromium profiles, .env files and credential files) so you can rotate them, and look for those secrets or accounts being used from addresses outside your own workloads. The hunts are our analysis, not published indicators. The bash script lists paths only and never prints a file's contents; it was run against synthetic directories (hit, clean, empty, missing, and a directory with an unreadable subdirectory, which exits 3 unless something was found) and its output was checked for leaked contents. The Splunk, KQL and ES|QL queries are templates that need your own lists of addresses and accounts filled in, and were reviewed, not executed against a live platform; test them on a non-production index first. We are not offering Sigma, Suricata, YARA, osquery or PowerShell for this item: the logic is a membership test against lists only you hold, and no string, file or address is published. Filename patterns are a heuristic, because credential files have many names. Adjust field and table names to your logging.

bash — project folders, unpacked images or home directories of the workloads that ran on Containers or Sandboxes; paths only, no contents; exit 0 = none found, 1 = found, 3 = nothing scanned or some paths unreadable (not a clean result)
#!/usr/bin/env bash
# Usage: ./container-secret-inventory.sh DIR...     (project folders, images unpacked to a folder, or home directories of the workloads)
# Lists PATHS ONLY (never contents) of the file types the researcher says were recoverable, so you can build a rotation list.
# Name patterns are a heuristic: credential files have many names. Bounded to the directories you pass in.
# Read-only. Exit 0 = none found, 1 = found, 2 = usage, 3 = nothing scanned or some paths unreadable (NOT a clean result).
[ $# -ge 1 ] || { echo "usage: $0 DIR..." >&2; exit 2; }
dirs=(); skipped=0
for d in "$@"; do if [ -d "$d" ] && [ -r "$d" ]; then dirs+=("$d"); else echo "skipping unreadable: $d" >&2; skipped=1; fi; done
[ "${#dirs[@]}" -gt 0 ] || { echo "no readable directories (NOT a clean result)" >&2; exit 3; }
scanned=$(set -o pipefail; find "${dirs[@]}" -type f 2>/dev/null | wc -l | tr -d ' '); rc=$?; [ "$skipped" -eq 0 ] || rc=1
if [ "$scanned" -eq 0 ]; then echo "scanned=0: no files found (NOT a clean result)" >&2; exit 3; fi
show() { local f; [ -n "$2" ] || return 0; while IFS= read -r f; do echo "$1${f:0:300}"; done <<< "$2"; }
cnt() { if [ -n "$1" ]; then printf '%s\n' "$1" | wc -l | tr -d ' '; else echo 0; fi; }
l=$(find "${dirs[@]}" -type f \( -name '.env' -o -name '.env.*' \) -print 2>/dev/null) || rc=1; show "ENV:        " "$l"; env_n=$(cnt "$l")
l=$(find "${dirs[@]}" -type f \( -iname '*.sqlite' -o -iname '*.sqlite3' \) -print 2>/dev/null) || rc=1; show "SQLITE:     " "$l"; db_n=$(cnt "$l")
l=$(find "${dirs[@]}" -type f -iname '*credential*' -print 2>/dev/null) || rc=1; show "CREDENTIAL: " "$l"; cred_n=$(cnt "$l")
l=$(find "${dirs[@]}" -type d -iname '*chromium*' -print 2>/dev/null) || rc=1; show "CHROMIUM:   " "$l"; prof_n=$(cnt "$l")
echo "scanned=$scanned env_files=$env_n sqlite_files=$db_n credential_named_files=$cred_n chromium_dirs=$prof_n" >&2
[ "$rc" -eq 0 ] || echo "some paths unreadable (NOT a clean result)" >&2
[ $((env_n+db_n+cred_n+prof_n)) -gt 0 ] && exit 1
[ "$rc" -eq 0 ] || exit 3
exit 0
Splunk SPL — audit log of the service a secret unlocks; template: supply the lookup files; field names vary by service
index=* sourcetype=<audit log of the service the secret unlocks> earliest=-90d
| lookup rotated_secret_ids.csv key_id OUTPUT key_id AS in_scope
| where isnotnull(in_scope)
| lookup own_workload_egress.csv ip AS src_ip OUTPUT ip AS own
| where isnull(own)
| stats count min(_time) AS first max(_time) AS last BY key_id, src_ip
Microsoft Sentinel / Log Analytics KQL — Microsoft Entra sign-ins (SigninLogs); template: replace the two lists; this lists every sign-in from outside your workload egress, including the user's own devices; add the account's usual addresses to own_egress or compare against its history
let own_egress = dynamic(["<an address your workloads use>"]);
let accounts = dynamic(["<UPN of an account signed in inside a container browser profile>"]);
SigninLogs
| where TimeGenerated > ago(90d)
| where UserPrincipalName in~ (accounts) and IPAddress !in (own_egress)
| summarize signins = count(), first = min(TimeGenerated), last = max(TimeGenerated), apps = make_set(AppDisplayName) by UserPrincipalName, IPAddress
| order by last desc
Elastic ES|QL — ECS-mapped audit logs; template: replace the user list and the address range
FROM logs-*
| WHERE @timestamp > NOW() - 90 days
| WHERE user.name IN ("<service account or key owner>") AND NOT CIDR_MATCH(source.ip, "<your workload egress range>")
| STATS events = COUNT(*), first = MIN(@timestamp), last = MAX(@timestamp) BY user.name, source.ip
| SORT last DESC
If you see Do this
A rotated secret used after its rotation, or a pre-rotation secret used from an address nobody can account for Treat as a confirmed compromise of that credential, as the page's Defined response says. Revoke it, establish what it accessed, and treat that data as exposed.
Sign-ins for an account that was signed in inside a container's browser profile, from an unfamiliar device or address Treat as a lead. Confirm with the account owner. If it cannot be explained, revoke the sessions, reset the credentials and handle it as the row above.
The inventory lists .env files, credential files, SQLite databases or Chromium profile folders on workloads that ran before 19 September Add every secret in them to the rotation list and rotate, prefer short-lived and narrowly scoped replacements, and revoke sessions for accounts signed in inside those browser profiles. Then stop writing secrets to container disk (ours; Cloudflare says no customer action is required).
Use only from your own workloads, CI and known egress addresses Record as expected and add the addresses to the list the queries use.
Empty results because audit logging was off, retention is short, or you have no list of what the workloads wrote This is not a clean result. Rotate anyway, and ask Cloudflare through support or your account team whether the account's containers ran on affected hosts and when.
No hits A good sign, not an exoneration. There is no customer-side artifact of a residual block being read, so no log can show it; Cloudflare's no-exploitation finding comes from telemetry only it can see; the queries only cover secrets and accounts you listed and the retention you kept; and the start of the exposure window is unknown. Rotate what sat on container disk before 19 September regardless.

Sector signal — only what a source actually said

We do not infer sector exposure from products. No source in this window attributed either item to a named sector. Beaumont assesses the activity as likely espionage by a well-resourced actor; that describes the attacker, not a targeted industry. Absence of sector-attributable reporting is not absence of risk.

How this brief is put together

Facts are reported as their named source published them; CVE data, scores and fixed builds come from Citrix's bulletin and the CVE and NVD records directly, and catalog dates from CISA's own Known Exploited Vulnerabilities data. Where sources differ — Citrix names two exploited flaws and Beaumont three; Cloudflare and the researcher describe what was recoverable differently — we show it and say which reading we plan on. IoCs appear only as a named source published them; IoAs are cited or marked ours. PIVCCERL™ is Threat Vector Management's incident lifecycle — Preparation, Identification, Validation, Containment, Communication, Eradication, Recovery, Lessons Learned; an unaffected organization is in Preparation, and a second stage appears where a hunt is warranted. ATT&CK: no source in this window published technique mappings; ours would be inference and are omitted. This brief publishes when something warrants it, not on a schedule.

Threat Vector Management helps organizations and the MDR and SOC teams that defend them turn briefs like this into action — running an edge-appliance response in the order that keeps the evidence, and scoping what a provider-side exposure means for the secrets you hold. Start a conversation →

More editions: Threat Brief archive.

Threat Vector Management is not affiliated with any company operating under the name “ThreatVector.”


Sources

  1. Citrix — Security bulletin CTX697096 for CVE-2026-88771 through CVE-2026-88778, 27 Sep 2026
  2. Citrix — CTX694799, steps to take if NetScaler ADC is suspected to be compromised
  3. NetScaler Console — Indicators of Compromise detection
  4. Canadian Centre for Cyber Security — AL26-024, 27 Sep 2026
  5. CISA — Known Exploited Vulnerabilities catalog, catalog version 2026.09.27
  6. CISA — Known Exploited Vulnerabilities catalog data
  7. watchTowr — CVE-2026-88771, 27 Sep 2026
  8. watchTowr — Citrix NetScaler zero-day FAQ, 27 Sep 2026
  9. Kevin Beaumont — thread on the NetScaler zero-days, opened 26 Sep 2026; quoted replies 27 Sep 2026
  10. CVE record — CVE-2026-88772
  11. BleepingComputer — CISA orders federal agencies to patch exploited Citrix flaws (Shadowserver exposure counts), 28 Sep 2026
  12. The Hacker News — CISA says attackers are exploiting two critical Citrix NetScaler flaws, 28 Sep 2026
  13. Help Net Security — Citrix NetScaler RCE zero-days exploited for weeks, 28 Sep 2026
  14. Cloudflare — How Cloudflare addressed a cross-tenant data exposure vulnerability in Containers, 24 Sep 2026
  15. Accomplish (Oren Yomtov) — Escaping the Cloudflare sandbox, 24 Sep 2026
  16. BleepingComputer — Cloudflare fixes Containers cross-tenant flaw, 27 Sep 2026

Provenance: compiled 28 September 2026 (morning) by Threat Vector Management from a sweep of vendor, government and researcher sources, with research and fact-checking support from Claude (Anthropic). Every CVE identifier, version string, score, catalog date and indicator was checked against its cited primary record or researcher publication on the day of publication.

Get the next one first

New Threat Briefs and Deep Dives, delivered to your feed reader the moment they publish. No sign-up, no email, no tracking.

Follow Insights