What a compromised VPN concentrator, firewall or gateway leaves behind, what each trace proves, how long it lasts, and in what order to collect it. For responders who know DFIR and need the parts that are different on these devices.
Do not reboot, upgrade, factory-reset or run the vendor's external integrity tool. Each destroys evidence - the red share of every bar above - and several implants were built to survive or fool exactly those.
2
Pull the off-box copies first - SIEM syslog, NetFlow for the management interface, IdP and MFA logs. A gap in them is itself evidence.
3
Record whether it has rebooted or been upgraded since the suspected window. It changes what the report can claim.
4
Use a read-only account where the platform has one, and pin the host key.
None of the collection profiles has been run against a real device or a vendor virtual edition yet. Every command comes from vendor documentation and was checked only against synthetic replays. A command can fail, prompt, page differently or print a format the parsers do not recognise; the analyzer reports that as not evaluated, never as clean. Try a profile on a lab device or virtual edition first, read plan before you collect, and expect the collector to ask you to type UNVALIDATED (or pass --accept-unvalidated) before it connects. Report what you find - how to record a run.
python -m edgetriage plan --profile <platform> # read it first
python -m edgetriage collect --profile <platform> --host <ip> --user <ro-user> \
--case-id IR-2026-001 --rebooted no --patched no
python -m edgetriage ingest cases/IR-2026-001 --label tsf --bundle <file>
python -m edgetriage analyze cases/IR-2026-001
How hits become a verdictCorroboration, not counting: repeated hits in one evidence class never raise the verdict. There is no "clean".
COMPROMISED
A confirmed hit, or likely hits in two different evidence classes.
LIKELY-COMPROMISED
One likely hit, or possible hits in two evidence classes.
SUSPICIOUS
A possible hit in one class: often an attempt, success not shown, or off-box logs that do not add up (the SIEM's copy disagreeing with the box, or going quiet while the device was busy). A published indicator found as text in memory is a lead and never counts as the second class.
NO-INDICATORS-FOUND
Nothing fired. Not a finding that the device is clean: read the evidence gaps.
4. The order the collector follows
Every profile runs in this order and refuses to move a step that can reload the device ahead of the memory steps. The number on each tier is how many steps use it across all profiles.
collected firstlast
Volatile statelost at reboot - collected first
V0Identity and clock24
V1Memory state11
V2Processes and sockets23
V3Active sessions16
Persistent evidencesurvives a reboot, not always an upgrade
P1Integrity11
P2Configuration18
P3Auth and VPN logs10
P4Web and portal logs11
P5System and crash logs11
P6Filesystem21
P7Persistence22
Vendor bundleswrites to the device; opt-in
BVendor bundles6
Break-glasscan crash, reload or image it; opt-in, always last
XBreak-glass13
Reading the badges
Volatility - how long the trace lasts
live
exists only while the device runs; gone at reboot
rotating
overwritten as logs roll, often in hours
stable
survives until someone changes or wipes it
off-box
a copy somewhere else: SIEM, syslog, management server
Detection tier - what a hit means
possible
an attempt, or a lead that needs a second signal
likely
post-exploitation trace with a plausible benign explanation
confirmed
no legitimate reason to exist on the device
Step impact - what collection does to the device
none
read-only; runs by default
vendor-bundle
writes to the device; needs --allow-bundle
break-glass
can crash, reload or image it; needs --break-glass, runs last
Confidence - where a claim comes from
high
vendor-documented or verified on a device
medium
several independent sources
unverified
single source or inferred - check before relying on it
Artifacts
What each trace proves, how long it lasts and the step that collects it.
About this library
Every artifact on every platform: what it proves, how long it lasts and the step that collects it. Filter, open a row for the full record, and export what you picked as a collection checklist.
Platforms
What a responder can reach on each platform, how, and what disappears if you wait.
CVEs
One record per CVE: what CISA's catalog says about it, and everything the toolkit has for it.
About this library
Every CISA KEV entry on the ten platforms since 2024, and every CVE a record here names. CISA's own fields (date added, due date, ransomware use, the BOD 26-04 forensic triage flag) are copied verbatim from the KEV catalog by `advisory_watch.py --records`; the rest joins what the toolkit has: detections, the traces artifacts record, hunts, hardening, playbooks, actors and indicator sets. A CVE CISA flags for forensic triage carries the directive's six steps, each beside the place the toolkit starts it.
Detections
Each runs in the analyzer and reports hit, no hit or not evaluated, never a pass. 47 also carry a Sigma rule for your SIEM.
About this library
Every detection runs in the analyzer against evidence collected from the device, and reports hit, no hit or not evaluated - never a pass. Where the same indicator can be watched in your SIEM, the detection carries its Sigma rule; YARA rules scan recovered files. Sigma rules convert with `sigma convert -t <backend>`; no vendor query dialect is shipped.
Threat intel
Attribution is reported, never computed: our link confidence beside the reporter's own words.
About this library
Attribution is reported, never computed. Each actor carries our link confidence (how well sourced the link is) beside the reporting party's own words. Clusters are merged only where the reporters themselves equate them.
Hardening
Controls that would have prevented or blunted the exploitation waves these platforms have seen.
About this library
Controls that would have prevented or blunted the exploitation waves these platforms have seen - first among them, keeping management interfaces off the internet. The baseline applies to every platform; each control cites the vendor or government source it comes from.
Hunts
Hypotheses for data the intruder could not edit: NetFlow, SIEM, AAA, identity and proxy logs.
About this library
Hypotheses to test in data that lives off the device - NetFlow, SIEM, AAA accounting, identity and proxy logs - because the device's own record is the one an intruder edits. The analyzer does not run these; each states what normal looks like, what would be a lead, and the source it rests on.
Collection plans
Every profile in order of volatility. Bundle and break-glass rows stay greyed out until you allow them in the builder, exactly as the collector behaves.
None of the collection profiles has been run against a real device or a vendor virtual edition yet. Every command comes from vendor documentation and was checked only against synthetic replays. A command can fail, prompt, page differently or print a format the parsers do not recognise; the analyzer reports that as not evaluated, never as clean. Try a profile on a lab device or virtual edition first, read plan before you collect, and expect the collector to ask you to type UNVALIDATED (or pass --accept-unvalidated) before it connects. Report what you find - how to record a run.
Device clock, compared with collector UTC to compute skew for every later timestamp.
high
show_system_info
none
command
show system info
Model, serial, PAN-OS version, uptime. Uptime tells you whether volatile state survived.
high
V2 Processes and sockets - process
show_system_resources
none
command
show system resources
Process snapshot (top). A shell, miner or tunnel running as root shows here and nowhere durable.
high
show_system_files
none
command
show system files
Core and crash files. Exploitation attempts that crash a daemon leave cores.
medium
V3 Active sessions - sessions
show_admins
none
command
show admins all
Administrator accounts and who is logged in now. Rogue admins are a common post-exploitation step.
medium
show_gp_current_users
none
command
show global-protect-gateway current-user
Active GlobalProtect sessions, for session-hijack and stolen-credential scoping.
medium
show_jobs
none
command
show jobs all
Commits, content installs and exports, with admin and time. Config theft shows as an export job.
high
P2 Configuration - config
show_config_running
none
command
show config running
Running configuration for diffing against a known-good backup. Contains secrets in encrypted form; handle accordingly.
high
show_config_diff
none
command
show config diff
Uncommitted candidate changes. An attacker who staged but did not commit leaves them here.
medium
show_log_config
none
command
show log config direction equal backward
Configuration audit log.
medium
P3 Auth and VPN logs - auth-logs
show_log_auth
none
command
show log auth direction equal backward
Authentication log.
medium
P4 Web and portal logs - web-logs
mp_gpsvc
none
command
tail lines 10000 mp-log gpsvc.log
GlobalProtect service log. CVE-2024-3400 exploitation leaves "failed to unmarshal session(...)" entries carrying a path instead of a session GUID. The vendor's own on-box check is `grep pattern "failed to unmarshal session(.\+.\/" mp-log gpsvc.log*`.
medium
mp_sslvpn_ngx_error
none
command
tail lines 10000 mp-log sslvpn_ngx_error.log
SSL VPN nginx error log. UPSTYLE read commands from requests logged here and then deleted its own lines, so gaps matter as much as hits.
medium
mp_appweb_access
none
command
tail lines 10000 mp-log appweb3-sslvpn.log
Portal web server log, for request timing around exploitation.
medium
P5 System and crash logs - system-logs
show_log_system
none
command
show log system direction equal backward
System log, including GlobalProtect portal and gateway events and admin logins.
medium
B Vendor bundles - bundle
tsf_export
vendor-bundle
command
scp export tech-support to {bundle_dest}
needs --var bundle_dest
Generate and export the tech support file to an SCP target you control. Writes a temporary file on the device. Ingest the result with `edge-triage ingest`.
Withheld until you allow it in the builder
high
X Break-glass - memory
tac_root_artifacts
break-glass
manual
manual
Files the TSF does not carry, and memory.
Withheld until you allow it in the builder
Instructions
PAN-OS customers cannot collect memory or read the root filesystem themselves.
Open a support case and request, through vendor-assisted access:
- /etc/cron.d/ and /var/spool/cron/ (cron persistence; Unit 42's
"Level 3: Interactive Access" covers "shell-based backdoors,
introduction of code, downloading files or running commands")
- Python site-packages *.pth files (UPSTYLE, where installed, uses system.pth)
- /var/appweb/ document roots (copied running-config, web shells)
- a memory capture, if the vendor will take one
Ingest each returned archive with: edge-triage ingest <case> --label tac --bundle <file>
Version, build, serial, system time. Build number selects the integrity baseline.
high
V1 Memory state - memory
proc_dump_init
none
command
diagnose sys process dump 1
Process information and memory map of PID 1. MIVD/AIVD: deleted entries linked to /lib/preload.so in this map are a strong indicator of COATHANGER, which injects into PID 1 for persistence.
medium
proc_dump_miglogd
none
command
diagnose sys process dump {miglogd_pid}
needs --var miglogd_pid
Process information and memory map of miglogd. Unit 42's PEERSTITCH hunting row: "Inspect memory maps (/proc/<pid>/maps) of /bin/miglogd and /bin/iked for anonymous 16,384-byte regions with rwxp permissions". The command is the one MIVD/AIVD use for PID 1; the operator supplies the PID from a process listing taken just before (diagnose sys top). PEERSTITCH lives only in memory, so this runs before anything that could restart the daemon.
medium
proc_dump_iked
none
command
diagnose sys process dump {iked_pid}
needs --var iked_pid
Process information and memory map of iked, for the same PEERSTITCH check as proc_dump_miglogd.
medium
V2 Processes and sockets - process
diag_sys_top
none
command
diagnose sys top 1 50 1
Process snapshot. Implanted daemons and renamed binaries show here first.
medium
get_perf_status
none
command
get system performance status
CPU, memory, uptime. Uptime bounds how much volatile state survived.
high
diag_session_stat
none
command
diagnose sys session stat
Session table summary.
high
V3 Active sessions - sessions
tcpsock
none
command
diagnose sys tcpsock
Active TCP sockets with the owning process. MIVD/AIVD: an outgoing connection to an external address from a process called httpsd is a strong indicator of COATHANGER (the name may vary; an implant that has not reached its C2 shows no socket).
medium
get_admin_list
none
command
get system admin list
Administrators logged in now, with source address.
high
get_vpn_ssl_monitor
none
command
get vpn ssl monitor
Active SSL-VPN users and tunnels.
high
P1 Integrity - integrity
fs_hash
none
command
diagnose sys filesystem hash
SHA-256 of system files, for comparison against a known-good baseline of the same build.
high
P2 Configuration - config
show_config
none
command
show
Configuration (non-default values). Diff against backup for new admins, local users, VPN portals, scripts and automation stitches.
high
P3 Auth and VPN logs - auth-logs
log_export
none
manual
manual
Event, VPN and admin logs.
Instructions
Export system event, VPN and admin login logs for the full retention window
from FortiAnalyzer, FortiCloud or your SIEM (preferred: off-box copies are the
ones the attacker is least likely to have touched), then:
edge-triage ingest <case> --label logs --bundle <export>
medium
P5 System and crash logs - system-logs
crashlog
none
command
diagnose debug crashlog read
Crash log. SSL-VPN exploitation (CVE-2022-42475 class) crashed sslvpnd with signal 11; the vendor published that pattern as an IOC. The output puts "application sslvpnd" and "*** signal 11 (Segmentation fault) received ***" on separate rows, so it is scored by a coded check, not a line regex.
high
P6 Filesystem - filesystem
listing_data
none
command
fnsysctl ls -la /data
Top of the persistent user filesystem. Unexpected files and links here survive reboot. This directory is the toolkit's choice; the vendor's IOC tech tip lists /data/lib, /data/etc, /var and / (the steps below).
medium
listing_data_lib
none
command
fnsysctl ls -l /data/lib
Fortinet's CVE-2022-42475 IOC listing. libips.bak, libgif.so, libiptcp.so, libipudp.so and libjepg.so here are published indicators.
medium
listing_var
none
command
fnsysctl ls -la /var
Fortinet's CVE-2022-42475 IOC listing. /var/.sslvpnconfigbk is a published indicator.
medium
listing_data_etc
none
command
fnsysctl ls -l /data/etc
Fortinet's CVE-2022-42475 IOC listing. /data/etc/wxd.conf is a published indicator.
medium
listing_root
none
command
fnsysctl ls -l /
Fortinet's CVE-2022-42475 IOC listing. /flash is a published indicator.
medium
listing_bin
none
command
fnsysctl ls -la /bin
System binaries. Mandiant published this command's output for CVE-2022-41328 (lspci must be a standalone binary; /bin/fgfm was the CASTLETAP backdoor), and the MIVD/AIVD COATHANGER advisory uses it to find a smartctl that is not a symlink. On a clean device most entries are links to /bin/init.
medium
listing_data_bin
none
command
fnsysctl ls -la /data/bin
The MIVD/AIVD COATHANGER advisory checks for /data/bin/smartctl here.
medium
listing_data2_template
none
command
fnsysctl ls -la /data2/template
Unit 42: the PEERSTITCH script "downloaded five files ... into /data2/template/" - pM.js (self-deleting), index.js, index.wasm, ipssem.js and lldprx, which "unlinked its own binary from disk". The directory name comes from one researcher and has not been listed on a device by this project.
lowunverified
listing_tmp
none
command
fnsysctl ls -la /tmp
SOCRadar's PivotC2 stager "saves the decrypted output to a temporary path (/tmp/.i.js)", and its hunting guidance is "fnsysctl ls -la /tmp/.i.js" and "fnsysctl ls -la /tmp/". This project does not know what a clean /tmp holds, so only named indicators are scored.
lowunverified
P7 Persistence - persistence
listing_sslvpn_lang
none
command
fnsysctl ls -la {sslvpn_lang_dir}
needs --var sslvpn_lang_dir
Listing of the directory that serves SSL-VPN language files, where the 2025 symlink persistence lived. Fortinet does not publish the path. The researcher Fortinet credits in FG-IR-25-934 shows /migadmin/lang holding one link, custom -> /data2/custom_lang, which the attackers repointed at the root filesystem. That is a single source and this project has not seen it on a device, so the operator supplies the path.
Memory and disk state, before anything else touches the appliance.
Instructions
Virtual appliance: take a hypervisor snapshot that INCLUDES MEMORY now, before
running the external ICT (which reboots the appliance) and before any upgrade
or factory reset. Physical appliance: engage Ivanti support for memory and
disk acquisition. Record the snapshot identifier and hash, then:
edge-triage ingest <case> --label snapshot --bundle <snapshot or its hash record>
medium
P1 Integrity - integrity
ict_internal_history
none
manual
manual
Internal ICT scan history and its last result.
Instructions
Record the internal ICT history from the admin UI (screenshots are acceptable
evidence if they are hashed on ingest). Ivanti KB44859: results also reach
the Events log as SYS32039 (new files) and SYS32040 (mismatched files). A
clean result is NOT evidence of a clean device; note it as one signal.
Ingest with --label ict-internal.
medium
P2 Configuration - config
config_export
none
manual
manual
System and user configuration export.
Instructions
Export system.cfg and user.cfg (binary) and an XML export. Diff against the
last known-good backup for new admin accounts, auth servers and resource
policies. Ingest with --label config.
medium
P3 Auth and VPN logs - auth-logs
siem_logs
none
manual
manual
Off-box copies of event, user-access and admin-access logs.
Instructions
Export the appliance's syslog stream from your SIEM for the full retention window.
These are the copies most likely to be intact: in 2025 UNC5221's exploit
script added iptables DROP rules on the syslog ports and SPAWNSLOTH disabled
local logging and remote syslog forwarding, so a gap in the off-box stream
is itself evidence. Ingest with --label siem.
medium
ui_log_export
none
manual
manual
On-box event, user access and admin access logs.
Instructions
From the admin UI, save the Events, User Access and Admin Access logs (all
available, not filtered). Ingest with --label ui-logs.
medium
X Break-glass - memory
ict_external
break-glass
manual
manual
External ICT snapshot (reboots the appliance).
Withheld until you allow it in the builder
Instructions
Only after vm_snapshot is complete. Run the current external ICT and keep
its snapshot: since February 2024 Ivanti says it provides "an unencrypted
snapshot for their own review" when new or modified files are found. The
external ICT reboots the appliance (Volexity), which destroys memory and
running-process evidence.
Ingest the snapshot and Ivanti's findings with --label ict-external.
Build. Selects which CVE packs apply and which baseline to compare against.
high
shell_uptime
none
command
shell uptime
A reboot since compromise means anything written to the memory-disk root is gone.
high
V2 Processes and sockets - process
shell_ps
none
command
shell ps auxww
Process list. Reverse shells, tunnels and miners.
high
shell_netstat
none
command
shell netstat -an
Sockets. Listeners that are not NetScaler services, and egress from the management plane.
high
shell_sockstat
none
command
shell sockstat -46
Socket-to-process mapping, so a listener can be attributed to a binary.
high
V3 Active sessions - sessions
show_system_session
none
command
show system session
Management sessions now active.
medium
show_aaa_session
none
command
show aaa session
Gateway AAA sessions. Replayed session tokens (Citrix Bleed class) are sessions from addresses the user never had.
medium
P2 Configuration - config
show_running_config
none
command
show ns runningConfig
Running configuration. New system users, policies and responder actions.
high
httpd_conf
none
command
shell cat /etc/httpd.conf
Apache configuration. The appliance ships with php_flag engine off; the 2026 log-poisoning intrusions switched it on and added CSS-looking aliases to web shells, and NCSC-NL's CVE-2025-6543 check looks for Require all denied and php_flag engine off commented out.
medium
rc_netscaler
none
command
shell cat /flash/nsconfig/rc.netscaler
Start-up script on persistent flash. NCSC-NL checks it for python; Rapid7 saw the 2026 framework persist through it.
medium
P3 Auth and VPN logs - auth-logs
fetch_logs
none
fetch
shell find /var/log -maxdepth 1 -type f -name 'ns.log*'
ns.log and its rotations. AAA failures with non-printable bytes are the CVE-2025-5777 memory-leak signature, and rotation is fast on a busy gateway.
medium
P4 Web and portal logs - web-logs
fetch_httpaccess
none
fetch
shell find /var/log -maxdepth 1 -type f -name 'http*.log*'
Web access and error logs for the management and gateway virtual servers.
Every PHP file under the web roots with mode, owner, size and mtime (find -ls). Web shells from the 2023 mass exploitation campaign sat in these directories. Fox-IT's triage tool expects shipped PHP at mode 0444, so a writable PHP file here is a lead.
medium
listing_tmp
none
command
shell ls -la /tmp /var/tmp
Staging directories.
high
core_list
none
command
shell find /var/core -name 'NSPPE-*' -ls
Packet-engine core files, listed not copied (Bishop Fox: "roughly 2.3 GB on our 14.1 appliance"). Bishop Fox, for CVE-2026-8452: "A fresh NSPPE-* core file" is "The same crash, with the evidence attached", and "Core files land in the directory for the current boot, so looking only in /var/core/1 misses anything that arrived after a reboot; use find /var/core -name 'NSPPE-*' instead." NCSC-NL's core-dump rules (github.com/NCSC-NL/citrix-2025, core-dump-checks) scan these files once they are copied off the device: ingest them with --evidence-class memory and pass the rules to analyze with --memory-yara.
lowunverified
fetch_php
none
fetch
shell find /var/netscaler/logon /var/vpn /netscaler/ns_gui /var/netscaler/ns_gui -type f -name '*.php'
Copy every PHP file under the web roots for YARA and content review.
medium
logonpoint_custom
none
command
shell ls -la /var/netscaler/logon/LogonPoint/custom
The Gateway UI customisation directory, web-reachable by design. The 2026 intrusions hid PHP web shells here as .ctxs.receiver and .slap.receiver, which the *.php search misses.
medium
P7 Persistence - persistence
suid_files
none
command
shell find /var /tmp /flash -xdev -type f -perm -4000
SUID binaries outside the shipped set, a persistence and privilege technique seen on backdoored NetScalers.
medium
crontab_root
none
command
shell crontab -l
Root crontab.
high
crontab_system
none
command
shell cat /etc/crontab
System crontab.
high
nsconfig_listing
none
command
shell ls -la /flash/nsconfig
Persistent configuration directory (/nsconfig points here). Rapid7 found the 2026 framework under /nsconfig/.slap and a relay implant under /nsconfig/.relay.
medium
listing_flash
none
command
shell ls -la /flash
Persistent flash. Rapid7 found the 2026 nsvmd proxy agent and its watchdog under /flash/.nsvm, marked immutable with chflags uchg.
medium
bin_sh_mode
none
command
shell ls -la /bin/sh
Mode of /bin/sh. Both Unit 42 and Rapid7 report the actor ran chmod 6555 /bin/sh so the unprivileged web process could become root; the suid search above does not cover /bin.
medium
X Break-glass - memory
disk_image
break-glass
manual
manual
Offline image of the persistent disks, the check that does not trust the device's own binaries.
Withheld until you allow it in the builder
Instructions
Image the memory-disk root (md0) and the persistent disk - named da0, ad0,
ada0 or similar depending on model, BIOS and hypervisor - to an external
target, or snapshot the VPX at the hypervisor with memory. Analyse
the images offline (Dissect-based triage such as fox-it/citrix-netscaler-triage
or NCSC-NL citrix-2025 disk-image checks). Then ingest the triage output:
edge-triage ingest <case> --label image-triage --bundle <output>
Version, hardware and uptime. A low uptime (unexpected reboot) after the exploitation window is itself a finding; ASA does not print a reload reason here, so pair it with show crashinfo.
high
V1 Memory state - memory
mem_region_lina
none
command
show memory region | include lina
Executable memory regions mapped to lina. More than one r-xp region, especially one of exactly 0x1000 bytes, is the vendor's LINE DANCER indicator. Runs before anything that could reload the device.
high
mem_region_full
none
command
show memory region
Full region map, kept so the lina filter above can be re-checked by hand.
high
show_checkheaps
none
command
show checkheaps
Heap checker state, first snapshot. Checkheaps runs every 60 seconds by default; Cisco reports the 2025 ArcaneDoor actor disabled it. CISA's procedure waits at least five minutes before the second reading and calls a Total number of runs with no positive change "evidence of a potential compromise". Compared with show_checkheaps_late.
high
V2 Processes and sockets - process
show_kernel_lina_cs
none
command
show kernel process | include lina_cs
Cisco's check for the 2026 FXOS persistent implant (FIRESTARTER, per Talos) on Firepower 1000, 2100, 4100, 9300 and Secure Firewall 1200, 3100, 4200: any output means the device is considered compromised. The legitimate lina_cs can get stuck; Cisco's false-positive check is verify disk0:/asdm.bin, then this command again. A cold restart removes the implant, so collect before anyone pulls power.
high
show_processes
none
command
show processes
Process table.
high
show_conn_count
none
command
show conn count
Connection table size.
high
V3 Active sessions - sessions
show_ssh_sessions
none
command
show ssh sessions
Management SSH sessions now active.
high
show_vpn_summary
none
command
show vpn-sessiondb summary
VPN session counts.
high
show_vpn_anyconnect
none
command
show vpn-sessiondb anyconnect
Remote-access sessions with user, address and duration.
high
P2 Configuration - config
show_running_config
none
command
show running-config
Running configuration with secrets masked. ArcaneDoor disabled syslog and altered AAA, so logging and aaa sections are the first diff targets.
high
P5 System and crash logs - system-logs
show_logging
none
command
show logging
Logging configuration and buffer. Logging that is off, or a buffer that ends abruptly, is a finding.
high
show_crashinfo
none
command
show crashinfo
Last crash record.
medium
P7 Persistence - persistence
dir_disk0
none
command
dir /recursive disk0:
disk0 contents. LINE RUNNER arrived as client_bundle.zip, which Talos says is deleted once processed at boot and re-created as client_bundle_install.zip at shutdown, so on a running device the zip is normally absent. Talos says LINE RUNNER's csco_config.lua creates a file named 1515480F4B538B669648B17C02337098 in csco_config/97/webcontent on disk0. Any zip here needs explaining.
high
show_checkheaps_late
none
command
show checkheaps | begin Checkheaps
Second checkheaps snapshot, taken last so that time has passed since the first. The pipe only makes the command distinct for replay; the output is the same. The engine does not wait: CISA's procedure needs at least five minutes between the readings, and the check reports the actual gap. Repeat by hand if the gap was shorter and the counter has not moved.
medium
X Break-glass - memory
lina_text_implant_grep
break-glass
command
more /binary system:/text | grep 55534154 41554156 41575756 488bb3a0
CISA ED 25-03's check for the 2025 actor's implant in the running lina text section; CISA - "Any output from this command is an indicator of compromise." Break-glass and last, beside the core dump: CISA says to stop all actions and contact it if the device crashes during its initial commands, and NCSC reports LINE VIPER reboots the device when system:/text is copied. Whether reading it with more can trigger that is not published, so it is opt-in (--break-glass) and runs after everything that a reload would destroy has been collected. Command as CISA prints it; its output format is not published.
Withheld until you allow it in the builder
medium
core_dump
break-glass
manual
manual
Core dump for memory analysis (RayDetect for the 2025 RayInitiator / LINE VIPER activity).
Withheld until you allow it in the builder
Instructions
Do NOT start until every live step above (memory regions, checkheaps, processes,
sessions, lina_text_implant_grep) has been collected and saved off the device.
CISA's Supplemental Direction procedure reloads the device twice: "The steps
below trigger an immediate reload of the device", starting with "Soft reboot the
device to maximize the likelihood of a clean core dump", then crashinfo force
page-fault. Either reload destroys memory-only implants.
LINE DANCER hooked the dump routine so that a dump request reloaded the device.
NCSC reports LINE VIPER does the same ("instead the device immediately reboots"),
reboots the device when system:/text is copied, and makes verify of system:/text
return the hash of the legitimate firmware - so a matching text hash proves nothing.
A dump attempt that reboots without producing a file is itself a finding (NCSC:
it "could indicate that LINE VIPER was deployed"). CISA: if nothing is written,
record it, retry once, and if there is still no output "document and inform CISA".
Follow CISA's procedure for your exact model and release, scan the dump with
RayDetect (ASA 5500-X only; it cannot detect RayInitiator in Firepower dumps), and
ingest the dump and the scanner output, each declaring the memory class (analyze
reads the dump as a memory image and the scanner's text report as a file):
edge-triage ingest <case> --label coredump --bundle <file> --evidence-class memory
edge-triage ingest <case> --label raydetect --bundle <output> --evidence-class memory
Gateway product version and build, which decides exposure to each 2026 CVE (the fixes are per Jumbo Hotfix take).
high
fwm_ver
none
command
fwm ver
Management server version, the management-side equivalent of fw ver. Reported unavailable on a gateway.
medium
hotfixes
none
command
cpinfo -d -y all
Installed hotfixes and Jumbo takes. Read with the version to decide whether CVE-2026-93616, -85102, -50751 and -16232 were ever exploitable here. sk92739 calls CPInfo "an auto-updatable utility" that checks for its own updates weekly and does not say whether -y skips that check; -d ("Specifies not to check for updates of CPInfo utility") keeps this step from downloading.
medium
livepatch
none
command
cplp list
LivePatch state. The CVE-2026-85102 fix shipped as LivePatch Take 26; LivePatch does NOT fix CVE-2026-93616.
high
V2 Processes and sockets - process
ps
none
command
ps auxww
Process list.
medium
netstat
none
command
netstat -anp
Sockets with owning process.
medium
V3 Active sessions - sessions
ma_users
none
command
listusers
Mobile Access users connected now, with source address.
high
P3 Auth and VPN logs - auth-logs
fetch_command_logger
none
fetch
ls -1d /var/log/command_logger.log*
Expert shell command auditing. Documented in the Scalable Platform guide; may not exist on standard gateways.
lowunverified
smartconsole_exports
none
manual
manual
SmartConsole log queries for the 2026 campaigns, which no shell on the device can run.
Instructions
In SmartConsole Logs and Monitor, run each query over the full retention
window, export the results (CSV), and ingest each export with its label:
CVE-2026-85102 certificate logins (label cp-85102):
product: "Mobile Access" AND action: "Log In" AND "Certificate"
CVE-2026-50751 IKEv1 Quick Mode (label cp-50751):
action:"Key Install" AND "Quick Mode completion"
(any hit before the fix or mitigation date is suspicious)
CVE-2024-24919 follow-on logins (label cp-24919):
blade:"Mobile Access" AND action:"Log In" AND auth_method:Password
CVE-2026-16232 (Audit Logs view, label cp-16232), both queries:
Authentication method: application token
"Authentication method: Password based application token"
(sk185169: "Any found events should be treated as suspicious and
inspected for an unrecognized administrator name or IP address.")
Then: edge-triage ingest <case> --label <label> --bundle <export.csv>
Compare source addresses against the IP lists in sk185033, sk185169 and
sk182336; they are not copied here because they go stale. sk185169's list is
"relevant for events before 2026-07-23".
After CVE-2026-85102 logins, Check Point's blog advises looking for "second
stage activity originating from suspicious logged-in users via Mobile
Access", often "internal port and service scan" - traffic through the
gateway, so look for it in these log exports, not in the gateway's sockets.
medium
P4 Web and portal logs - web-logs
fetch_cpm
none
fetch
ls -1d $MDS_FWDIR/log/cpm.elg*
CPM log on management infrastructure - where Check Point's CVE-2026-93616 hunting indicators appear (oversized login usernames, traversal in version and upload paths, JAR path breakout). Collect from every Security Management Server, Multi-Domain Server, Log Server, Multi-Domain Log Server and SmartEvent server. Absent on gateways.
high
fetch_vpnd
none
fetch
ls -1d $FWDIR/log/vpnd.elg*
Gateway VPN daemon log. watchTowr's patch diff shows the fixed code logging "Suspected path traversal attack from" when it blocks a CVE-2024-24919 attempt; whether that line lands here or only in a security alert log is not confirmed. Absent on management servers.
medium
P5 System and crash logs - system-logs
usermode_dumps
none
command
ls -l /var/log/dump/usermode/
User-mode core dumps. On a management server, fwm or mds dumps alongside a CVE-2026-93616 indicator-1 hit support the authentication-bypass reading.
high
fetch_messages
none
fetch
ls -1d /var/log/messages*
General log, including Clish configuration audit entries (auditlog is permanent by default).
high
P7 Persistence - persistence
crontab
none
command
crontab -l
Root crontab. Generic Linux persistence; Check Point has published no attacker persistence locations for Gaia.
lowunverified
B Vendor bundles - bundle
cpinfo_bundle
vendor-bundle
command
cpinfo -z -i -S -D -d -o {cpinfo_out}
needs --var cpinfo_out
Check Point's diagnostic bundle, compressed, non-interactive, with password hashes censored (-S). -D stops the upload: with the default "Allow Upload" consent cpinfo otherwise sends the file to Check Point without asking. -d skips its own update check and download. Writes a file. Check Point advises not running it while any CPU core is above 70%.
Model, Junos release and hostname. The release selects which CVEs apply and whether the device is end-of-life (the UNC3886 victims were).
medium
show_uptime
none
command
show system uptime | no-more
Device clock and time since boot. A reboot since the window means memory-resident payloads are gone.
medium
V2 Processes and sockets - process
show_processes
none
command
show system processes extensive | no-more
Process table. Mandiant names the UNC3886 backdoor binaries after real daemons (appid / appidd, irad / irsd, lmpad / lmpd, jdosd / jddosd, oemd / oamd, to / top) but gives no process listing. The one execution it documents is lmpad injected into a hijacked `cat` process, which a process table shows as `cat`, not as the backdoor name.
high
V3 Active sessions - sessions
show_users
none
command
show system users | no-more
Users logged in now, with source and idle time.
medium
P2 Configuration - config
show_config
none
command
show configuration | display set | no-more
Configuration as set commands, for diffing against the last known-good: login users and classes, scripts, services and syslog hosts. UNC3886's syslog tampering edited the runtime /mfs/var/etc/syslog.conf and was restored afterwards, so it does not appear here; see listing_mfs_etc.
medium
show_commits
none
command
show system commit | no-more
Commit history with user, time and method (cli, other, autoinstall). An unexpected commit or a "via other" entry in the window needs an owner. Not reliable after lmpad: its backup command copied /var/db/commits aside and its restore command copied it back, so the history can be rewound to its pre-session state (Mandiant).
high
P3 Auth and VPN logs - auth-logs
log_interactive
none
command
show log interactive-commands | no-more
CLI command history (UI_CMDLINE_READ_LINE). lmpad's command 2 deleted lines containing "root" from it; compare with the syslog receiver copy.
medium
P4 Web and portal logs - web-logs
jweb_php_log
none
command
file show /var/jail/sess/php.log | no-more
J-Web's PHP log, where watchTowr saw the traces of unauthenticated J-Web API use ("CACHING FLOW: query user not set", and "Trying to access array offset on value of type null" from /html/core/session.php). file show is Juniper's documented way to display a log file by path; the path is from watchTowr's single SRX reproduction, so the step errors where it differs.
lowunverified
P5 System and crash logs - system-logs
log_messages
none
command
show log messages | no-more
System log including UI_LOGIN_EVENT, UI_AUTH_EVENT and sshd lines. lmpad's command 2 deleted every line containing "root" from messages before each remote shell, so gaps around root activity are the finding.
high
P6 Filesystem - filesystem
listing_var_log
none
command
file list /var/log detail | no-more
Log directory listing. lmpad's embedded script (pre_ssh) copied wtmp to wtmp0 and utx.log to utx.log0; post_ssh copied wtmp0 back over wtmp (erasing the session's records) and deleted wtmp0, and - per the script's code, though Mandiant's prose says both were restored - re-copied utx.log to utx.log0 instead of restoring it. utx.log0 therefore survives; wtmp0 survives only if post_ssh never ran.
high
listing_var_tmp
none
command
file list /var/tmp detail | no-more
Temporary directory. Mandiant places pfed_jdhcp6_trace.log (and .bak, both deleted after use) and rts in /var/tmp. loader.bin, pc.bin, payload.bin and the FIFO named null were in a directory the actor removed afterwards; Mandiant gives no location for it or for ldb.b64 and ldb.tar.gz, so their absence means little. watchTowr's J-Web uploads were found under /var/jail/tmp, not here. Core files also land here.
high
listing_var_jail
none
command
file list /var/jail detail recursive | no-more
The chroot J-Web's PHP runs in. watchTowr's J-Web upload reproduction (webauth_operation.php, which Juniper's JSA72300 tracks as CVE-2023-36851) wrote uploads as 64-hex-character names with the original extension under /var/jail/tmp and logged to /var/jail/sess/php.log. That is one researcher on one SRX, so the whole jail is listed rather than the one path.
lowunverified
P7 Persistence - persistence
listing_config_dir
none
command
file list /config detail | no-more
Live configuration directory. lmpad's backup command (6) wrote usage_db here and under /var/db/config - tar archives of juniper.conf files - and its restore command (7) deleted them, so they survive between the two or if restore never ran.
medium
listing_db_config
none
command
file list /var/db/config detail | no-more
Rolled-back configurations (juniper.conf.4.gz and later) and the second usage_db location.
medium
listing_mfs_etc
none
command
file list /mfs/var/etc detail | no-more
Runtime configuration directory. lmpad's embedded pre_ssh script backed up the runtime syslog.conf to syslog.conf0 before redirecting logging to /dev/null; post_ssh restored it and deleted the copy, so syslog.conf0 survives only if post_ssh never ran. The path comes from one Mandiant report and has not been confirmed on other platforms or releases.
lowunverified
listing_scripts
none
command
file list /var/db/scripts detail recursive | no-more
Commit, op and event scripts. A natural persistence location, but not one Juniper or Mandiant names as used; listed for completeness.
lowunverified
B Vendor bundles - bundle
jmrt_integrity_check
vendor-bundle
command
request system malware-scan integrity-check
JMRT check that the integrity mechanisms (veriexec on Junos, IMA on Evolved) are working. Not read-only: Juniper says to expect log lines such as "MAC/veriexec: no fingerprint (file=/tmp/veriexec_test", so it adds syslog entries - collect the logs first. That it also writes a test file is this toolkit's inference from that path; Juniper does not say so. Junos 19.2 to 21.3 use a differently named JMRT command (veriexec-check), which the guard refuses; run it by hand from Juniper's documentation. UNC3886 bypassed veriexec by injecting into a running process, so a pass here does not clear the device.
Withheld until you allow it in the builder
medium
support_info
vendor-bundle
command
request support information | no-more
The JTAC support bundle, printed to the session rather than saved (`| save` would write a file and is refused by the guard). Opt-in because it is long and loads the routing engine.
Withheld until you allow it in the builder
medium
X Break-glass - memory
jmrt_quick_scan
break-glass
manual
manual
JMRT malware scan of running processes.
Withheld until you allow it in the builder
Instructions
Only after every step above is collected and hashed. By default
`request system malware-scan quick-scan` DELETES the malicious files and
KILLS the processes it finds - the evidence and the implant go together.
Run it with the clean action set to warn so it only reports (check the
exact option order for your release in Juniper's JMRT documentation),
capture the output, then ingest it:
edge-triage ingest <case> --label jmrt --bundle <output>
Mandiant's guidance was to upgrade to the latest image and run JMRT quick
scan and integrity check after the upgrade.
medium
memory_and_files
break-glass
manual
manual
Process memory and the backdoor binaries themselves.
Withheld until you allow it in the builder
Instructions
There is no documented customer path to Junos memory. Mandiant says the
investigation "required novel methods for artifact acquisition and
analysis". Engage Juniper
(JTAC) for memory and file acquisition. If you obtain binaries, scan them
with 09-juniper-junos/junos_unc3886_tinyshell.yar and ingest the result.
Release, uptime and running image. The release selects which of the 2023 and 2026 CVEs apply.
high
V1 Memory state - memory
iosd_smaps
none
command
show platform software process memory rp active name iosd smaps
Memory map of the IOSd process. Cisco's forensic guide checks "for non-zero values in the Private Dirty entry for each segment with the executable flag set (i.e., r-xp or rwxp)"; its example of possible tampering is an rwxp segment with Private_Dirty above zero. Collected before anything that could reload the device. This is the router form; iosd_smaps_switch and iosd_smaps_switch_r0 are the switch forms, and all but one error on any given device.
high
iosd_smaps_switch
none
command
show platform software process memory switch active rp active name iosd smaps
The same IOSd memory map in the form Cisco's guide gives for Catalyst switches. Expected to error on routers.
high
iosd_smaps_switch_r0
none
command
show platform software process memory switch active r0 active name iosd smaps
The switch form with r0 in place of rp. Cisco's guide - "Some switches require a target of r0. Replace rp with r0 in the following two commands if a syntax error occurs." The command is that literal substitution; Cisco prints no output for it. Expected to error where the rp form works.
medium
implant_check
none
manual
manual
Cisco's HTTP check for the 2023 web UI implant, run from a host that can reach the device.
Instructions
From a workstation, run Cisco's published check (replace DEVICEIP):
curl -k -H "Authorization: 0ff4fbf0ecffa77ce8d3852a29263e263838e9bb" -X POST "https://DEVICEIP/webui/logoutconfirm.html?logon_hash=1"
A hexadecimal string in the response means a known implant variant is present.
Talos also published a variant-independent probe:
curl -k "https://DEVICEIP/%25"
A 404 "Not Found" suggests variant 1 or 2, a login page suggests variant 3,
and a 200 with a JavaScript redirect means no known variant - not that the
device is clean. Talos: "For the implant to become active, the web server
must be restarted", and the logon_hash check "will only work as an
indication of compromise if the web server was restarted".
Save both responses and ingest them: edge-triage ingest <case> --label implant-check --bundle <file>
medium
V2 Processes and sockets - process
show_iox
none
command
show iox
Whether the IOx hosting framework is running. Cisco's guide says to enumerate the app hosting environment; AA25-239A reports Guest Shell, which runs on IOx, being used by the actors.
high
show_app_hosting
none
command
show app-hosting list
Containers hosted on the device, including Guest Shell.
high
V3 Active sessions - sessions
show_users
none
command
show users
Sessions on the console and VTY lines now.
medium
P1 Integrity - integrity
platform_integrity
none
command
show platform integrity sign nonce 12345
Signed boot and image measurements. Cisco's guide says to collect this output for the TAC service request; it does not give values to compare against.
high
sw_authenticity
none
command
show software authenticity running
Signature verification of the running image.
high
hw_authentication
none
command
show platform hardware authentication status
Hardware trust anchor authentication status.
high
image_hash
none
command
verify /sha512 {boot_image}
needs --var boot_image
SHA-512 of an image file on disk, for comparison with Cisco's published hash. In bundle mode, the .bin named in show version. In install mode (show version names packages.conf) hashing packages.conf proves nothing: Cisco's guide reads packages.conf and verifies each package it lists, so re-run this step once per .pkg from show_packages_conf.
high
show_packages_conf
none
command
more bootflash:packages.conf
The package list for an install-mode boot, so each .pkg can be hashed with image_hash. Errors on a bundle-mode device. Some platforms keep it on flash: rather than bootflash:.
medium
P2 Configuration - config
show_running_config
none
command
show running-config
Running configuration. Diff for privilege-15 local users, ip http server state, packet captures (monitor capture), tunnels, ACL changes, TACACS+ server changes, EEM applets and non-standard SSH ports.
high
http_server_state
none
command
show running-config | include ip http server|secure|active
Whether the web UI (the 2023 exploitation surface) is enabled. Cisco's own exposure check.
high
P5 System and crash logs - system-logs
show_logging
none
command
show logging
Local log buffer. Carries the %SYS-5-CONFIG_P and %WEBUI-6-INSTALL_OPERATION_INFO messages from the 2023 campaign; the actor also cleared logs, so compare with the syslog receiver.
high
P6 Filesystem - filesystem
dir_all
none
command
dir /recursive all-filesystems
Every filesystem. Look for packet captures left on bootflash (tac.pcap, mycap.pcap, 1.pcap were named in AA25-239A) and the system_shell trace logs under tracelogs, which Cisco's guide says to collect for the TAC service request. Cisco's guide lists dir harddisk:/tracelogs, not this command; dir /recursive all-filesystems is the toolkit's choice.
medium
dir_tracelogs
none
command
dir harddisk:/tracelogs
The command Cisco's forensic guide gives for trace logs; files whose names begin with system_shell are to be preserved for the TAC service request. Cisco notes core files and tracelogs "may exist on the flash:, bootflash:, harddisk:, or crashinfo: partitions", so this errors on devices without harddisk: and dir_all covers the rest.
high
B Vendor bundles - bundle
show_tech
vendor-bundle
command
show tech-support
Cisco's support bundle, printed to the session (the guard refuses `| redirect`). Opt-in because it is long and loads the device.
Withheld until you allow it in the builder
high
X Break-glass - memory
memory_text
break-glass
manual
manual
The IOSd text segment, for hashing against the image.
Withheld until you allow it in the builder
Instructions
Per Cisco's forensic guide: `copy system:memory/text ftp:` to a server you
control and `verify /md5 system:memory/text`. Some of these commands need
`service internal`, which is a configuration change: record when you set
it and run `no service internal` afterwards. Ingest the copy with
`--evidence-class memory`.
medium
core_dump
break-glass
manual
manual
Full IOSd core dump.
Withheld until you allow it in the builder
Instructions
`request platform software process core ios rp active` (switches:
`... switch active rp active`). Cisco's guide warns this "may cause the
platform to reboot", which would erase memory-only evidence. Do it last,
or not at all, with Cisco TAC.
OAuth authorization-server counters. F5 lists a rise in total_failed as a CVE-2026-94127 indicator; only meaningful when APM is an OAuth Authorization Server.
high
P1 Integrity - integrity
getenforce
none
command
getenforce
SELinux mode. F5 lists anything other than Enforcing as a c05d5254 indicator.
high
selinux_permissive
none
command
semanage permissive -l
SELinux domains set to permissive. httpd_t, mount_t or apd_t listed here is a c05d5254 indicator per F5.
high
mount
none
command
mount
Mount table. /usr mounted read-write is a weak indicator F5 lists for c05d5254.
high
P2 Configuration - config
auth_users
none
command
tmsh list auth user | cat
Local accounts and roles. Diff against the known set.
medium
mgmt_ip
none
command
tmsh list sys management-ip | cat
Management address, to judge exposure of the control plane (the CVE-2022-1388 and CVE-2023-46747 surface).
medium
irules
none
command
tmsh list ltm rule | cat
iRules. Data-plane logic an attacker can use for traffic capture or redirection; diff against change control.
medium
fetch_audit
none
fetch
ls -1d /var/log/audit*
Configuration and command audit log. Carries the tmsh `run util bash` entries from CVE-2022-1388 and c05d5254 activity.
high
P3 Auth and VPN logs - auth-logs
fetch_apm
none
fetch
ls -1d /var/log/apm*
APM log. CVE-2026-94127 attempts appear as 10 or more "Request UserInfo ... failed. Error Code (invalid_token)" (01990004) lines in one log, especially from a single address.
high
fetch_secure
none
fetch
ls -1d /var/log/secure*
Authentication log.
high
P4 Web and portal logs - web-logs
fetch_restjavad_audit
none
fetch
ls -1d /var/log/restjavad-audit*
iControl REST audit log. POSTs to /mgmt/tm/util/bash are the shared signature of CVE-2022-1388 and the c05d5254 activity.
high
fetch_catalina
none
fetch
ls -1d /var/log/tomcat/catalina.out*
Configuration utility (Tomcat) log. CVE-2023-46748 exploitation left "java.sql.SQLException: Column not found: 0." followed by "sh: no job control in this shell"; the number after "Column not found:" varies (F5).
high
P5 System and crash logs - system-logs
fetch_auditd
none
fetch
ls -1d /var/log/auditd/audit.log*
Linux audit log. "received setenforce notice (enforcing=0)" is a c05d5254 indicator.
high
fetch_ltm
none
fetch
ls -1d /var/log/ltm*
LTM log, including TMM restarts (a TMM SIGABRT followed CVE-2026-94127 exploitation in F5's description).
high
P7 Persistence - persistence
ioc_files
none
command
ls -la /run/bigtlog.pipe /run/bigstart.ltm
Existence of the two runtime files F5 names as c05d5254 indicators. "No such file" for both is the expected clean result.
high
cron_listing
none
command
ls -lt /etc/cron* /var/spool/cron
Cron persistence, per F5's compromise-checking article.
high
hidden_www
none
command
find /usr/local/www -type f -name '.*'
Hidden files under the web root, per F5's compromise-checking article.
high
startup_script
none
command
cat /config/startup
Runs at boot. F5 lists it among the persistence locations to check.
high
rc_local
none
command
cat /etc/rc.d/rc.local
Boot script whose hash or size changed under c05d5254.
high
B Vendor bundles - bundle
qkview
vendor-bundle
command
nice -n 19 qkview
F5 diagnostic bundle, written to /var/tmp. Adds load. F5 asks for it alongside System Scanner results on a suspected compromise.
Withheld until you allow it in the builder
high
X Break-glass - memory
system_scanner
break-glass
manual
manual
F5 System Scanner, the vendor's file-integrity check (writes a report to the device).
Withheld until you allow it in the builder
Instructions
Download F5 System Scanner and the .ndjson file for your exact release from
MyF5, copy them to /var/tmp/scanner, check the manifest with
sha512sum -c <release>.ndjson.sha512, and run as root:
nice -n 19 ./f5-system-scanner --dat-file ./<release>.ndjson \
--dat-file-sha512 "$(cut -d' ' -f1 <release>.ndjson.sha512)" \
--root / --format text --progress --output ./scanner-report.txt
Limits F5 states: it supports "BIG-IP versions released between October 2025
and Oct 14, 2026" and "EHFs released between October 2025 and Oct 11, 2026";
it "cannot be run on systems configured in Appliance mode"; and if running
it with no flags shows a usage that "requires the --sha256 flag, you must
download a new copy" (the current build requires --dat-file-sha512).
Scanner 1.2 adds --scan-procs ("Scans only process executables that have
been deleted from the disk (unlinked)") and --scan-all-procs (hashes every
running process's in-memory binary, read from /proc, against the manifest).
F5 does not say either detects the in-memory c05d5254 webshells, so a clean
result does not clear them. The scanner runs on the suspect host itself.
Do not substitute sys-icheck: c05d5254 tampered with it. On any mismatch F5
asks for a support case with the scanner results and a qkview. Ingest the
report: edge-triage ingest <case> --label scanner --bundle scanner-report.txt
medium
disk_image
break-glass
manual
manual
Disk image and TPM attestation, before any rebuild.
Withheld until you allow it in the builder
Instructions
Follow F5's disk image procedure (K17551329) and TPM attestation (K58311205)
as referenced from K11438344. The c05d5254 webshells ran in memory; there is
no documented memory acquisition path, so involve F5 Support if memory
matters.
Time since boot. If the appliance has restarted since the suspected window, agents injected into the workplace JVM are gone from memory; on Volexity's second appliance a reboot "likely removed memory-resident backdoors".
medium
hotfix_version
none
manual
manual
The appliance's release and platform-hotfix build, which decides exposure to each SMA 1000 advisory.
Instructions
Read the release and platform-hotfix build from AMC and record it in the
case notes. SonicWall's fixed builds:
CVE-2025-23006 12.4.3-02854 and higher
CVE-2025-40602 12.4.3-03245, 12.5.0-02283 and higher
CVE-2026-15409, -15410 12.4.3-03453, 12.5.0-02835 and higher
CVE-2026-83548, -83549 12.4.3-03526, 12.5.0-02952 and higher
medium
V2 Processes and sockets - process
ps
none
command
ps auxww
Process list. The workplace.startup.CommandStartup JVM is the process KNUCKLEBALL injects its agents into (Volexity); a tcpdump writing into /tmp or /var/tmp is the capture Volexity's lib.sh started. Neither source shows ps on the appliance.
lowunverified
open_fds
none
command
ls -l /proc/*/fd
Every process's open files. Volexity says KNUCKLEBALL deletes the two JARs after injecting them; whether the JVM keeps a deleted JAR open is this toolkit's expectation, not something Volexity reports, so a JAR in /tmp shown as "(deleted)" is a lead, not a finding.
lowunverified
tcp_sockets
none
command
cat /proc/net/tcp
The kernel's IPv4 TCP socket table. Volexity: "Look for unexpected connections from SonicWall SMA appliances". Kept as recorded; the analyzer does not parse it.
lowunverified
tcp6_sockets
none
command
cat /proc/net/tcp6
The IPv6 TCP socket table, kept beside the IPv4 one.
lowunverified
P1 Integrity - integrity
setuid_listing
none
command
find / -perm -4000
Every setuid binary. Volexity's own command, with its list of binaries that "may show up legitimately"; ROOTRUN was one more, as /usr/bin/xzfind. Walks the whole filesystem, so it is slow. Volexity's alone.
lowunverified
P2 Configuration - config
unit_conf
none
command
cat /var/lib/unit/conf.json
The NGINX Unit configuration. SonicWall: routes for /__api__/login or /__api__/logout "do not exist in legitimate configuration"; Volexity found them, proxied to http://127.0.0.1:8085, on both compromised appliances.
medium
P4 Web and portal logs - web-logs
fetch_extraweb
none
fetch
ls -1d /var/log/aventail/extraweb_access.log*
External web requests, including the /wsproxy bypass and the actor's /__api__ routes (SonicWall's and Volexity's indicators).
medium
fetch_access_servers
none
fetch
ls -1d /var/log/aventail/access_servers.log*
WebSocket proxy activity, with each connection's backend host and client address (Volexity alone).
lowunverified
P5 System and crash logs - system-logs
fetch_ctrl_service
none
fetch
ls -1d /var/log/aventail/ctrl-service.log*
What the control service ran as root, including hotfix removals; SonicWall lists "hotfix rollbacks with path traversal names" here.
medium
P6 Filesystem - filesystem
tmp_listing
none
command
ls -la /tmp /var/tmp
Volexity: "Examine SonicWall SMA appliances for unexpected files in /var/tmp and /tmp". Owners matter: its /tmp files were owned by the internal database service's account (couchdb), and the agents' logs were links to /dev/null.
lowunverified
aventail_log_listing
none
command
ls -la /var/log/aventail
Inventory of the appliance's log directory, so missing or truncated logs are visible. The directory is Volexity's.
lowunverified
P7 Persistence - persistence
workplace_init
none
command
cat /etc/init.d/workplace
The workplace service's startup script, to which UTA0533 added the line that starts KNUCKLEBALL. The path is Volexity's alone.
lowunverified
site_packages_listing
none
command
ls -la /usr/lib/python3.11/site-packages
The Python package directory where Volexity found KNUCKLEBALL (deploy_new.py). A later release may ship another Python version, and then this path does not exist. The path is Volexity's alone.
lowunverified
fetch_uta0533_files
none
fetch
ls -1d /usr/bin/xzfind /usr/lib/python3.11/site-packages/deploy_new.py /tmp/hypdate.b64 /tmp/1234.sh /tmp/agent_wp8.jar /tmp/agent_wp9.jar /var/tmp/lib.sh
The files Volexity found, fetched when present so Volexity's YARA rules can read them. ls prints only the paths that exist; "No such file" for each is the expected clean result. The paths are Volexity's alone.
lowunverified
X Break-glass - memory
memory_acquisition
break-glass
manual
manual
The appliance's memory, where the injected agents live. Do it before anything restarts the workplace service or the appliance.
Withheld until you allow it in the builder
Instructions
Volexity: "Using SSH access to the devices, Volexity collected system memory
(RAM) with Volexity Surge Collect Pro". Use the Linux memory tool your team
trusts; it runs on the appliance and writes to it. For an 8200v, a
hypervisor snapshot with memory is an alternative (the toolkit's
suggestion, not SonicWall's). Copy the image off and ingest it:
edge-triage ingest <case> --label mem --bundle <image> --evidence-class memory
analyze then runs Volexity's ORANGETAIL, KNUCKLEBALL, ROOTRUN and
user-agent rules over it.
medium
disk_image
break-glass
manual
manual
Full disk image, before any re-image or re-deploy.
Withheld until you allow it in the builder
Instructions
Volexity "gathered select files and full disk images"; how is not
published. SonicWall's instruction for an appliance with indicators is to
re-image (hardware) or re-deploy (virtual) it, which destroys the disk:
image first. For an 8200v the hypervisor holds the disk.
medium
Response
Containment and recovery in the order that protects evidence: preserve, contain, rotate credentials, eradicate, recover, verify. Guidance a person acts on; the toolkit never contains or remediates on its own.
How to use this page
The do-not list comes first because most evidence is destroyed by a well-meant action taken too early. Pick the verdict the analyzer gave to see only the steps it calls for, enter the case id so the ticks are kept per case, tick steps off as you go - each tick records the time - and copy the list into your case notes.
Analyzer verdict
Steps without a verdict condition apply whatever the verdict. Hidden steps are still in the playbook - choose All steps to see them.
Upgrade before exporting the tech support fileThe vendor advisory states some logs from the prior installation become inaccessible on the device after an upgrade.
Trust a config backup taken after compromiseIt may carry attacker changes (new admins, modified logging, scheduled jobs).
Treat a clean TAC IOC check as proof the device is cleanMemory and some persistence paths are not covered by a standard customer collection.
0 of 17 steps done
1
Preserve first - before anything changes the device
Collect before you change anything
Record whether the device rebooted or was upgraded in the suspected window
2
Contain - stop further access without destroying evidence
Restrict the management interfaceforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Disable GlobalProtect portal/gateway if exploitation is ongoing and the business can tolerate itforCOMPROMISEDLIKELY-COMPROMISED
Block known C2 egress at an upstream device, not on the firewall itself
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
Do not clean in place
Rebuild from a known-good image of a fixed release, with vendor guidance
Re-import a reviewed configuration, not a blind backup
5
Recover - return to service on a trusted build
Restore service on the rebuilt device on the fixed release
Re-enrol certificates and re-establish VPN after credentials are rotated
Keep heightened monitoring on the rebuilt device and the hosts the VPN reached
6
Verify - prove it, and hunt for the dwell period
Re-run the panos profile and analyze against a baseline, expecting NO-INDICATORS-FOUND with no open gaps
Confirm the management interface is no longer exposed and logging/forwarding is intact
Hunt from the device into the network for the dwell period, not just on the device
Rely on patching alone after suspected compromiseThe symlink persistence survived patching, and FG-IR-25-934 bypasses the fix on every 7.2, 7.0 and 6.4 release.
Trust `diagnose sys filesystem hash` without a baselineA list of hashes with nothing to compare against proves nothing.
0 of 16 steps done
1
Preserve first - before anything changes the device
Collect before remediation
Pull off-box logs from FortiAnalyzer / FortiCloud / syslog
2
Contain - stop further access without destroying evidence
Restrict SSL-VPN and administrative access upstreamforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Remember that patching does not evict an established actor
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
Do not clean the symlink and declare victory
Rebuild on a fixed build and remove post-patch persistence per vendor guidance
Re-import a reviewed configuration
5
Recover - return to service on a trusted build
Restore service on the rebuilt device on a fixed build
Re-establish SSL-VPN after credentials are rotated
Keep heightened monitoring and confirm filesystem hashes match the clean baseline
6
Verify - prove it, and hunt for the dwell period
Re-run `diagnose sys filesystem hash` against the baseline; expect no deltas
Confirm no symlinks into the root filesystem from served directories
Run the external ICT before snapshottingIt reboots the appliance and erases what lives only in memory - the 2026 renamed tcpdump and the files Volexity recovered from memory. The 2026 in-memory patch itself was restored after each command, so a snapshot will usually not contain it either.
Treat a clean internal ICT as a clean deviceCISA AA24-060B reports the internal and an earlier external ICT failed to detect compromise, and SPAWNANT re-signed the manifest.
Rely on a factory reset to evict the actorCISA showed in a lab that root-level persistence may survive a factory reset (Ivanti disputes real-world feasibility); Ivanti recommends a new build over a factory reset for virtual appliances, which "have not been consistently successful".
0 of 15 steps done
1
Preserve first - before anything changes the device
Snapshot the virtual appliance with memory before anything else
Pull the syslog receiver's copy of the logs
2
Contain - stop further access without destroying evidence
Disconnect the appliance from untrusted networksforCOMPROMISEDLIKELY-COMPROMISED
Do not run the external ICT as a containment step
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
Rebuild, do not cleanforCOMPROMISEDLIKELY-COMPROMISED
Apply the fixed release on the rebuilt appliance
5
Recover - return to service on a trusted build
Bring the rebuilt appliance online on a fixed release
Re-establish VPN after credentials and the directory bind account are rotated
Restore syslog forwarding and confirm it reaches the collector
6
Verify - prove it, and hunt for the dwell period
Confirm the external ICT (post-rebuild) is clean and the manifest signing key is the vendor's
Confirm syslog forwarding is intact and SELinux is enforcing, root filesystem read-only
Hunt into the domain for the dwell period; the directory is the real target
Request a core dump, or copy or verify system:/text, before every live step is collectedLINE DANCER hooked the dump routine to reboot the device. NCSC reports LINE VIPER reboots the device on a dump request and on copy of system:/text, and makes verify of system:/text return the legitimate firmware's hash; CISA's own dump procedure starts with a soft reboot.
Trust a verify /sha-512 system:memory/text hash that matches the releaseNCSC reports LINE VIPER's modified verify command "returns the hash of text section of legitimate firmware instead".
Reload a device with LINE RUNNER on disk0 and call it cleanThe boot-time feature re-establishes it.
Treat an unsplit lina region map as cleanThat was LINE DANCER's tell; NCSC reports LINE VIPER fixed it.
Power-cycle a Firepower or Secure Firewall device before collectingA cold restart removes the FXOS lina_cs implant and its only on-box indicator.
Assume a factory reset clears a bootkitRayInitiator persists below the OS (NCSC - it survives reboots and firmware upgrades). Where compromise is detected, ED 25-03 says to disconnect without powering off and work with CISA; it directed permanent disconnection of the end-of-support models.
The rest of the list is folded now that you have read it. Untick to show it again.
0 of 20 steps done
1
Preserve first - before anything changes the device
Collect memory-region state before anything that could reload the device
On Firepower and Secure Firewall hardware, run `show kernel process | include lina_cs` before anyone pulls power
If a core dump or the implant check shows compromise, keep the device powered on and disconnect it from the networkforCOMPROMISEDLIKELY-COMPROMISED
Capture the syslog receiver's record, not just the device
2
Contain - stop further access without destroying evidence
Restrict management and VPN reachability upstreamforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
If a bootkit is suspected (non-Secure-Boot 5500-X), isolate the device physicallyforCOMPROMISED
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
For LINE DANCER only (memory, no bootkit) a reload removes the implant
For LINE RUNNER, remove the disk0 bundle and rebuild
For the FXOS lina_cs implant (FIRESTARTER), reimage and upgradeforCOMPROMISEDLIKELY-COMPROMISED
For RayInitiator / bootkit, follow CISA ED 25-03forCOMPROMISED
5
Recover - return to service on a trusted build
Reimage to a fixed release (or replace hardware where a bootkit is in scope) and restore a reviewed configuration
Re-establish VPN after secrets and user credentials are rotated
Confirm logging and syslog forwarding are back and flowing to the collector
6
Verify - prove it, and hunt for the dwell period
Re-run the profile; lina_cs absent, the checkheaps counter rising, and syslog enabled
Confirm syslog IDs 302013, 302014, 609002 and 710005 arrive at the collector at their usual volume
Confirm no unexpected files on disk0 and the expected boot image
Trust the tech support report or a live listing on a suspected OVERSTEP hostThe rootkit hooks file and directory calls and hides itself; only an offline image is reliable.
Clean in place and return the device to serviceBoot-level persistence and hidden files make in-place cleaning unreliable on end-of-life hardware.
0 of 12 steps done
1
Preserve first - before anything changes the device
Take an offline disk image
Pull any off-box logs and upstream VPN/firewall records
2
Contain - stop further access without destroying evidence
Disconnect the appliance from untrusted networksforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
Replace the deviceforCOMPROMISEDLIKELY-COMPROMISED
5
Recover - return to service on a trusted build
Stand up a supported replacement (the SMA 100 line is end-of-life; plan migration)
Re-establish VPN after credentials and OTP seeds are rotated
6
Verify - prove it, and hunt for the dwell period
On a replacement, confirm no ld.so.preload, no rogue libsamba-errors.so.6, expected rc.fwboot and initrd
Hunt into the network for the dwell period
The recommended build, 10.2.2.2-92sv, is from SonicWall's SNWLID-2025-0015; GTIG's 10.2.1.15-81sv is the release its victims were already running.
Rely on LivePatch for CVE-2026-93616Check Point states LivePatch Takes 28 and 29 do not fix it and no LivePatch exists for it.
Disable the VPN implied rules without planning the replacement rulesCheck Point advises caution with this mitigation because a wrong configuration can cause connectivity issues; the replacement rules are this toolkit's way of applying that caution.
Treat the patch as proof the server was not attackedInstalling the fix does not tell you whether the server was attacked before; run the sk1000171 indicators. This is the toolkit's guidance; Check Point says only that an indicator match "does not necessarily confirm successful compromise".
0 of 18 steps done
1
Preserve first - before anything changes the device
Collect in Expert mode before patching
Export the SmartConsole hunting queries over the full retention window
2
Contain - stop further access without destroying evidence
Restrict the management server to trusted addressesforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Reboot the appliance, restart its services or install a hotfix before memory is capturedThe agents exist only in the workplace JVM. On Volexity's second appliance a reboot "likely removed memory-resident backdoors and potentially other volatile artifacts".
Read a quiet log for /__api__/login and /__api__/logout as cleanVolexity - "These paths are arbitrary and could be anything".
Trust the agents' own logs in /tmpKNUCKLEBALL clears them and links them to /dev/null before injection.
Clean the appliance in place and return it to serviceSonicWall's instruction for an appliance with IoCs is to re-image or re-deploy it.
The rest of the list is folded now that you have read it. Untick to show it again.
0 of 14 steps done
1
Preserve first - before anything changes the device
Capture memory over SSH before anything restarts
Copy the logs, configuration and staging directories off the appliance
Save the AMC Management message log
Pull the off-box records of what the appliance connected to
2
Contain - stop further access without destroying evidence
Restrict the management consoles and SSH to trusted management hostsforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Stop the appliance reaching anything but the services its users needforCOMPROMISEDLIKELY-COMPROMISED
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
Re-image hardware appliances or re-deploy virtual onesforCOMPROMISEDLIKELY-COMPROMISED
5
Recover - return to service on a trusted build
Bring the replacement up on 12.4.3-03526 or 12.5.0-02952 (platform-hotfix) or later
Ask SonicWall Technical Support to review the system for indicators
6
Verify - prove it, and hunt for the dwell period
Re-read the replacement's conf.json routes, setuid listing, workplace startup script, /tmp and /var/tmp
Hunt the network for authentication from the appliance over the dwell period
SonicWall's recommended actions in SNWLID-2026-0008 also include "Perform a thorough forensic analysis of the system to determine if any indicators of compromise (IoCs) are present" before deciding to re-image. The TOTP and password steps are SonicWall's; the directory-credential step follows from Volexity's LDAP capture.
Reset passwords or TOTP before containment is in placeSonicWall: "It is very important that containment is done before moving on to the remediation steps." An actor still on the VPN, or holding a valid session, keeps it.
Run SonicWall's Credentials Reset Tool before the logs and settings are exportedIt makes changes: "Auto-API enabling - Temporary SSH-based activation", forced password resets and TOTP removal. Run it after preservation, if at all.
Import a preferences file SonicWall sent without checking it against the firewallSonicWall: "If the latest preferences file does not represent your desired settings, please do not use the file."
Read an empty Auditing Log as proof the configuration was not touchedArctic Wolf saw no "unauthorized OTP unbinding events or other malicious configuration changes" before the 2025 intrusions, which used valid credentials instead.
Treat a firewall upgraded past CVE-2024-40766 as safeSonicWall: many 2025 incidents followed Gen 6 to Gen 7 migrations "where local user passwords were carried over during the migration and not reset".
Turn on the Tech Support Report's Sensitive Keys option, or send the report to anyone but SonicWall supportIt "Saves shared secrets, encryption, and authentication keys to the report".
The rest of the list is folded now that you have read it. Untick to show it again.
0 of 21 steps done
1
Preserve first - before anything changes the device
Export the System Logs and the Auditing Logs before changing anything
Pull the syslog server's copy for the whole suspected period
Export the settings and download a Tech Support Report with its options as they are
Check MySonicWall's Issue List for the firewall's serial number
2
Contain - stop further access without destroying evidence
Disable or restrict HTTP, HTTPS and SSH management over the WANforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Disable or restrict the SSL VPN, IPsec VPN and SNMP until credentials are resetforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Disable or restrict inbound WAN access to internal servicesforCOMPROMISEDLIKELY-COMPROMISED
Disconnect the SSL VPN sessions of accounts that logged in from the published or hosting sourcesforCOMPROMISEDLIKELY-COMPROMISED
3
Rotate credentials - everything the device held is exposed
4
Eradicate - remove the foothold
Remove accounts and access nobody can account forforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
Replace Gen 6 hardwareforCOMPROMISEDLIKELY-COMPROMISEDSUSPICIOUS
5
Recover - return to service on a trusted build
Bring Gen 7 to 7.3.4-7013 or later and Gen 8 to 8.2.3-8015 or later
Reintroduce services one at a time
6
Verify - prove it, and hunt for the dwell period
Run the SSL VPN hunts over the whole period the firewall's logs and syslog cover
Export the Auditing Logs again and confirm the only changes are your own
The containment and credential steps are SonicWall's Essential Credential Reset, written for the MySonicWall backup incident and the SSL VPN activity; its table lists many more services (SNMPv3, DDNS, wireless keys, SSO secrets, DPI-SSL's re-signing CA) to rotate after the critical ones. SonicWall does not publish a re-image procedure for a compromised firewall; the toolkit adds none.
Where the content is thin, how far to trust it, and whether it keeps up with exploitation. Computed from the repository at build time; every number opens the filtered library.
A file counts when its last commit is older than the newest KEV entry (CISA's date added) or indicator set on a platform it covers. It may need a re-read; it is not known to be wrong. Files that apply to every device are not compared. A commit is not a review: an unrelated edit after the advisory also clears the flag. Every record shows its own date in its quick facts.
How ready is each platform
Eight things a responder relies on. Sorted by how many each platform has; every cell opens the evidence behind it.
PlatformProfileRun on a deviceMemory evidenceConfirmed tierSIEM rules 25%+PlaybookHardening 5+HuntsHas
Every CISA KEV entry added since 2024-01-01 on a watched platform is either in a pack or carries a recorded decision. The weekly advisory-watch job opens an issue for anything else and never commits. The decisions live in artifacts/watch/kev-watch.yml. 15 of those 66 entries are flagged for forensic triage under BOD 26-04: see them, step by step.
70CVEs with a detection pack
15exploited CVEs with a decision instead
0waiting to be read
Pack CVEs by the year of the CVE
12018
12021
32022
152023
142024
182025
182026
50 of 70 pack CVEs carry a year of 2024 or later. The year is the one in the CVE id, not when exploitation began.
Why an exploited CVE has no pack
112 no-indicators11
1iocs: indicators only, no on-box detection
12no-indicators: primary sources read, nothing usable published
1lab-only: signals only from lab reproduction
1out-of-scope: not the watched product
0deferred: not yet read
Latest decision 2026-11-08. "deferred" would mean not yet read; none is open.
Exploited CVEs on watched platforms with no pack, and why
Palo Alto Networks' advisory publishes three addresses tied to exploit attempts and no host-side indicator, so there is no on-box detection to add; the management-interface hardening control now cites it.
Citrix CTX696939 publishes no indicators or exploitation statement. Bishop Fox's lab work: a Gateway bypass "logs an SSL VPN login for user anonymous" and "Every session this bug mints is anonymous", but the ns.log text is not published; revisit with a real sanitized line.
Cisco cisco-sa-asaftd-vpn-dos-dzv4mQFF: exploited ("In August 2026, the Cisco PSIRT became aware of active exploitation") via "a crafted HTTP request" causing a reload; no request, crash text or syslog ID is published, only Snort rules.
FG-IR-19-007: offline decryption of ENC values "If the CLI configuration is exposed"; nothing happens on the device. Hardening candidate: set private-data-encryption enable.
FG-IR-25-254 affects "FortiVoice, FortiMail, FortiNDR, FortiRecorder and FortiCamera" only; "Fortinet has observed this to be exploited in the wild on FortiVoice." FortiOS is not affected.
Cisco: "insufficient input validation of a parameter" and "additional attempted exploitation" in November 2024; no parameter, request or log line is published.
Cisco: "sending a crafted GET request to the web services interface"; Truesec "made an early decision to refrain from publicly releasing the exploit". Truesec's RADIUS pattern is a hunt candidate, not a pack detection.
Citrix CTX584986: "Exploits of these CVEs on unmitigated appliances have been observed"; NetScaler blog withholds detail and no researcher publication gives indicators.
Same bulletin CTX584986; no indicators from Citrix or a researcher. NHS England relays a CrowdStrike claim of no authentication needed; CrowdStrike's own publication was not found.
SonicWall SNWLID-2025-0019: "reported to be leveraged in combination with CVE-2025-23006 (CVSS score 9.8) to achieve unauthenticated remote code execution with root privileges", credited to Google Threat Intelligence Group; no IoCs are published, and no GTIG publication on it was found. CVE-2025-23006's message-log check is EDGE-PACK-0037.
SonicWall SNWLID-2026-0016: "SonicWall PSIRT has investigated a case indicating the active exploitation"; it publishes no IoCs and tells customers to "Contact SonicWall Technical Support for assistance reviewing the system for indicators of compromise (IoCs)". A web search turned up news and vendor-blog coverage only, none of it from a party that observed the exploitation.
SonicWall SNWLID-2026-0014 (6 November 2026): "SonicWall PSIRT has investigated a report indicating potential exploitation of this vulnerability"; it affects the SSL VPN only "if SAML is enabled for SSLVPN logins" and lets valid credentials for an alternate method bypass SAML-enforced MFA. It publishes no IoCs or log message. A search found news coverage only (BleepingComputer, SecurityWeek), none from a party that observed the exploitation. Hardening sonicos-current-release cites the fix.
2026-11-08
Edge device investigation guide
How to triage a VPN concentrator, firewall or gateway you suspect is compromised, using this toolkit. Written for responders who already know DFIR and need the parts that are different on appliances.
The order a case moves in. Off-box copies come first because an intruder on the device could not edit them; break-glass steps that can reload or image the device run last; and a verdict of no indicators found sends you back to close the evidence gaps, not to close the case.
Each step has a section below:
Preserve: before you touch the device.
Plan.
Collect or ingest.
The case folder.
Analyze.
Read the report.
Decide, then close the gaps.
Respond.
Two loops matter more than the straight line:
A NO-INDICATORS-FOUND verdict. It sends you back to close evidence gaps, not to close the case.
A step that can reload, crash or image the device. It waits until everything volatile is already in the case folder.
No shell, or a shell you cannot trust. PAN-OS, FortiOS, Cisco ASA and Ivanti Connect Secure give customers no root shell. NetScaler, Check Point (Expert mode), F5 (Advanced Shell) and Junos do, but an implant that hooks the C library lies to ps, ls and stat.
The decisive evidence is volatile. Memory-only implants (LINE DANCER on ASA), memory-only exploitation that leaves no implant (UTA0576's transient libdsplibs.so patch on Ivanti in 2026), memory-disk roots (NetScaler) and gateway logs that rotate in hours are gone after a reboot, an upgrade or a busy afternoon.
The integrity tools have been beaten. Ivanti's Integrity Checker was modified and its manifest re-signed; a FortiGate persistence mechanism survived patching; LINE DANCER turned core-dump requests into reloads. A clean vendor check is one signal, never a verdict.
Patched does not mean clean. Most backdoored NetScalers in the 2023 campaign were patched after the web shell was planted, and the patch did not remove it. Persistence outlives the vulnerability.
Read this before you connect. Most evidence on an appliance is destroyed by a well-meant action taken in the first hour: a reboot, an upgrade, or the vendor's own integrity tool.
Do not reboot, upgrade, factory-reset or run an external integrity tool. Each of those destroys evidence. On PAN-OS, the vendor says some logs from the prior installation become inaccessible on the device after an upgrade.
Pull the off-box copies first. SIEM syslog, NetFlow to and from the management interface, IdP and MFA logs, AD authentication from the device's addresses. These are the copies the attacker is least likely to have touched, and a gap in them is itself evidence.
Record the preflight honestly. Has the device rebooted or been upgraded since the suspected window? The answers change what the report can claim.
Use a read-only account where the platform has one, and pin the host key.
On Cisco ASA and Firepower, do not use tab completion. CISA: "Do NOT use the “tab” autocomplete or any other autocomplete function. The threat actor has hooked these commands to crash the device."
python -m edgetriage plan --profile <platform> # read it before you run it
python -m edgetriage collect --profile <platform> --host <ip> --user <ro-user> \
--case-id IR-YYYY-NNN --operator <you> --rebooted no --patched no
The engine runs steps in order of volatility and refuses anything the profile did not declare. Vendor bundles need --allow-bundle; core dumps, imaging and anything that reboots need --break-glass and run last.
Memory comes first, by the safest route. On a virtual appliance, a hypervisor snapshot that includes memory captures what no command on the device will, and nothing running inside the guest can interfere with it. Take it before the first live command. On hardware, the vendor's own core dump is the only route. It is break-glass and runs after everything else, because LINE DANCER turned a dump request into a reload. Ingest a snapshot or dump with --evidence-class memory; Memory images, under Analysis, says what analyze does with it.
A step that fails, times out or prints something the profile did not expect is recorded with its status in manifest.json: unavailable, error or refused. The detections that needed it then report not evaluated. Read collection.log before you read the verdict.
To test a profile without a device, use collect --replay <dir>. It serves recorded outputs exactly as a live run would see them: the output of each command in commands/<slug of the command>.txt, and pulled files under files/<path on the device>. A command with no recording is reported unavailable, just as on a device that lacks it. A sanitized capture from a real device is the most useful contribution you can make (see CONTRIBUTING.md). It turns a guessed output format into a tested one.
No profile has yet been run against a real device or a vendor virtual edition. Every command comes from the vendor's documentation and has been checked only against synthetic replays, so a command can fail, prompt, page differently or print a format the parsers do not recognise. The analyzer reports such a step as not evaluated - never as clean - and the report carries an unvalidated-profile gap, but a failed collection during an incident still costs time you do not have.
plan prints a warning for an unvalidated profile, and a live collect asks you to type UNVALIDATED (or pass --accept-unvalidated when there is no terminal) before it connects. Replays are not affected. Try a profile on a lab device or virtual edition first.
A profile becomes validated when someone runs it on a real device or virtual edition and records the run: an entry in the profile's validated_on list (release, date, basis) and a row under Device verification in docs/VERIFICATION.md with any command that had to change. Sanitised replay captures from such a run are the most useful contribution to this project.
PAN-OS / GlobalProtect. Collect, then export the tech support file through the web UI and ingest it. The TSF does not necessarily carry cron or Python site-packages; ask the vendor for those through a support case if CVE-2024-3400 is in scope.
FortiOS.diagnose sys filesystem hash is only meaningful against a baseline from the same build. Supply the SSL-VPN language directory with --var sslvpn_lang_dir=... so the symlink check can run.
Ivanti Connect Secure. Nothing to type at. Snapshot the virtual appliance with memory first, then export logs and configuration, then (and only then) the external Integrity Checker, which reboots the appliance.
NetScaler. Live response works, but image the disks (break-glass) before anyone reboots: anything on the memory-disk root is lost. Packet-engine core files under /var/core can be copied off and scanned with NCSC-NL's core-dump rules (Memory images, under Analysis, says how).
Cisco ASA / FTD. The memory-region check runs before anything else. Do not request a core dump until it has been reviewed; then follow the CISA ED 25-03 supplemental direction (updated April 24, 2026), which has separate procedures for Firepower and Secure Firewall with FTD software, Firepower and Secure Firewall with ASA software, and ASA devices - CISA: "instructions and commands vary". It adds the FIRESTARTER persistence mechanism on Firepower and Secure Firewall devices. Scan the dump with RayDetect.
SonicWall SMA 100. No collection profile yet. Export the tech support report and logs through the management UI and ingest them. GTIG advises disk images because the rootkit hides from on-box tools; physical units may need SonicWall's help.
SonicWall SMA 1000. The sonicwall-sma1000 profile logs in as root with the AMC admin password; enabling SSH in AMC is a configuration change, so record it. Nobody has run the profile on an appliance yet, so live use needs --accept-unvalidated. Nothing it runs restarts the workplace JVM, where UTA0533's web shell and tunnel lived; capture memory straight after it (the break-glass step) and before anything restarts the service, because on a rebooted appliance Volexity found them gone. Its file fetches use SFTP, but SonicWall documents SCP: if they report unavailable, copy /var/log/aventail/ off with scp and ingest it. For the CVE-2025-23006 check, save the AMC Management message log as a text file named amc-management-messages.txt; SonicWall names the view, not a file.
Check Point Gaia. Collect in Expert mode; entering Expert from Clish asks for a second password, so the collection account needs /bin/bash as its shell. Gateway and management roles keep different logs, so a role's log that is absent is a gap, not a pass. Export the SmartConsole hunting queries over the full retention window. cpinfo writes a file and by default uploads to Check Point and updates itself; the profile runs it with -D -d, behind --allow-bundle. Patching CVE-2026-93616 does not show whether the server was attacked before the patch.
Juniper Junos. Everything runs from the operational CLI with | no-more. The JMRT integrity check writes and runs a test file and adds syslog lines, so it is opt-in and runs after the logs are collected. JMRT quick-scan deletes malicious files and kills processes by default: it is break-glass, runs last, and should report only. UNC3886 deleted log lines containing "root" and redirected logging during its sessions, so the syslog receiver's copy outranks the device's.
Cisco IOS XE. Do not reboot: the 2023 web UI implant lived only in memory, though the accounts it created survive. The IOSd memory map is collected first; routers and Catalyst switches use different forms of the command and the profile collects both. In install mode, hash each package listed in packages.conf, not packages.conf itself. Run Cisco's HTTP implant check from a workstation; a negative result does not rule out an implant that never became active, because it only activates once the web server restarts.
F5 BIG-IP. Do not trust sys-icheck on a suspect device: c05d5254 tampered with it. Use F5 System Scanner from known-good media (break-glass), and take a qkview behind --allow-bundle. Never restore a UCS archive taken after the compromise; F5 warns it can carry the malware back.
SonicWall SonicOS. The TZ, NSa, NSsp and NSv firewalls. No collection profile yet: SonicOS has no general shell, and SonicWall's own CLI method for dumping the configuration commits a pager change first. Collect from the web console instead. Export MONITOR | Logs > System Logs and Auditing Logs as CSV or text, saved as sonicos-system-logs.csv and sonicos-auditing-logs.csv, pull the syslog server's copy as sonicos-syslog.log (the packs read the system-log and syslog names), and export the settings (.exp, which holds every credential, encoded rather than encrypted apart from the secrets themselves). Ingest them with --platform EDGE-0012. Leave the Tech Support Report's Sensitive Keys option off, and do not run SonicWall's credentials reset tool until all of that is saved: it enables the API over SSH and resets passwords. The 2024 and 2025 intrusions logged in with valid credentials, so a clean on-box result means little until the SSL VPN hunts (EDGE-HUNT-0029 to 0032) have been run over the syslog copy.
Every ingest is hashed and copied, archives are extracted regular-file-only, and refused members (traversal paths, symlinks) are recorded. A refused symlink is still evidence: the FortiOS symlink check reads those refusals.
Everything the toolkit knows about a case is in one folder. Archive it whole.
Path
What it holds
manifest.json
Case, operator, platform, preflight answers, every step with its status and the sha256 of its output, every ingest
manifest.sha256
The manifest's own hash. Each save also records the previous manifest's hash, so edits form a chain
collection.log
Each step and ingest on one line, in the order they happened
commands/
Each command's output, one file per step id
files/
Files pulled from the device, under the step that pulled them
bundles/
Ingested exports and vendor bundles, extracted regular-file-only
findings.json, report.md, report.html
The analysis: verdict, findings, evidence gaps
memory/
Volatility 3 outputs for each memory image, with volatility.json listing their hashes (only with --memory-symbols)
timeline.jsonl, timeline.csv
Device events and your own collection steps on one axis
llm_brief.md
The prompt for a model, with every record numbered and hashed
analyze, timeline and brief rewrite only their own output files, so you can re-run them as often as you like. They never touch the evidence the case holds.
--forwarded takes the device's syslog as your SIEM or collector received it, for the same hours: raw lines, or JSON or CSV with a Message field. The analyzer lines it up with the logs on the box by message and in order, like a diff, never by clock. A message repeated throughout the log, such as a logout, only pairs with a copy nearby. The analyzer reports two things as possible indicators:
runs of forwarded lines that are gone from the box;
runs of local lines the SIEM never received.
It also lists the longest silences in the forwarded stream. When the two copies share too few lines (the device forwards a different format than it keeps, or logs at a different level), it says the comparison was not evaluated. Use --forwarded-local to name the local file when it is not a *.log, messages or syslog file.
A silence on its own is not scored: a quiet night is not an indicator. An intruder who stops local logging and forwarding together leaves two copies that still agree, and then the only evidence is the device doing work while its logs say nothing. --activity takes that work from a source the device does not control, one event per record or line:
sign-ins through it (identity provider or RADIUS);
sessions from its inside address on the next firewall;
A gap with three or more of those events inside it, not counting five minutes at each end, is a possible indicator. A gap the export spans with no events is reported as quiet there too. A gap the export does not span is not counted as tested. The forwarded copy needs timestamps with a year (a JSON or CSV export with the SIEM's time field), or its gaps cannot be placed. A poll that happens anyway, such as an SNMP sample or a ping, shows the device was up, not that it was busy, so export only events the device caused. --activity-match keeps the records that name the device's address or host name as a whole token, so 192.0.2.1 does not match 192.0.2.10.
An ingest that declares --evidence-class memory and holds a LiME capture, an ELF core file or raw bytes is read as a memory image, gzip-compressed or not. A text file ingested the same way, such as a scanner's report, stays a file. The file detections never read an image: a file rule's size limit, header check or whole-file hash cannot be true of memory, so against an image it could only ever say no hit.
analyze reads each image once, in 64 MiB pieces that overlap by 1 MiB so a match across a seam is seen whole, and reports three checks beside the packs:
memory-yara: the platform packs' YARA rules whose condition reads nothing but their strings. Every other rule is listed as not run, with what it reads (the file's size, bytes at fixed offsets, a module, where its strings sit). A pack rule scores at most likely here. For Cisco ASA these are the publishers' own rules for memory and core dumps: NCSC's for LINE DANCER, RayInitiator and LINE VIPER, and CISA's for FIRESTARTER. For Ivanti they are Volexity's rules for the 2026 exploit request and privilege-escalation script, and for PySoxy. For PAN-OS they are Volexity's rules for the reverse-SSH tool UTA0218 ran. For SonicWall SMA 1000 they are Volexity's UTA0533 rules for ORANGETAIL, the Suo5 agent, KNUCKLEBALL, ROOTRUN and the actor's user agent; the two agents are deleted from disk once injected, so memory is where they are found.
memory-yara-own: rules written for memory that you pass with --memory-yara, scored possible.
memory-indicators: the platform's published network indicators found as ASCII or UTF-16LE text, each with the publisher's role. It is a lead and never corroborates: the device may hold the address because it logged or refused the connection a log detection already found. An address stored as four bytes, inside a socket structure, is not searched.
A match shows that the bytes were in memory, not where they came from. A raw image keeps physical pages in no order, so the strings of one program can sit far apart, and a rule that needs several of them can miss: no hit is weak evidence. Offsets in the report are offsets in the image file. A rule that states its own confidence, as NCSC-NL's do, has it shown beside the hit.
NetScaler has no memory rule in the packs. NCSC-NL publishes its own for the packet engine's core dumps, the NSPPE-* files the core_list step lists under /var/core: core-dump-checks/iocs.yar in github.com/NCSC-NL/citrix-2025, "for IOCs related to CVE-2025-5349, CVE-2025-5777 and CVE-2025-6543". It is licensed under the EUPL, so the toolkit does not carry a copy. Download it, copy the core files off the device, ingest them with --evidence-class memory, and pass the rules with --memory-yara. NCSC-NL warns that "Some of these checks are meant to hunt for compromises, while other checks verify if you are compromised". Six of the rules are IP addresses, and a few (python3 -c, base64.b64decode) are ordinary Python strings, so scan the core files themselves rather than a whole-VM snapshot.
The Memory section of the report also lists the Linux kernel banners found in the image. Volatility 3 needs symbols built for that exact kernel, and without --memory-symbols the analyzer does not run it. With them, a fixed set of Linux plugins runs offline: processes and their arguments, modules and hidden modules, the syscall table, malfind, netfilter hooks, sockets and bash history. The outputs are kept in the case's memory/ folder with their hashes. The analyzer does not read or score them, so read them yourself.
report.md and report.html hold the same findings, in the order to read them:
Verdict and what to do next. The verdict is COMPROMISED, LIKELY-COMPROMISED, SUSPICIOUS or NO-INDICATORS-FOUND; there is no "clean". The next steps quote the platform playbook's do-not list.
Evidence gaps. What the analysis could not see. A detection that was not-evaluated did not pass; it never ran. Each gap says what it needed (the step that failed, the file it had nothing to scan) and how to close it.
Findings, strongest first. Each hit gives the command and line that produced it, an excerpt, and the confidence of the pack behind it.
The header also carries the case manifest's sha256, so a report can be tied to the exact case folder it came from.
Tier
Typically means
possible
exploitation attempted, a lead that needs a second signal, or off-box logs that do not add up (the SIEM's copy disagreeing with the box, or going quiet while the device was busy)
likely
post-exploitation artefact with a plausible benign explanation
confirmed
artefact with no legitimate reason to exist on the device
The verdict follows from the strongest evidence, and corroboration counts only across different evidence classes; repeated hits in one class do not raise it.
A synthetic Junos case, built from the toolkit's own test fixtures (nothing in it came from a real device):
python tests/make_fixtures.py fx
python -m edgetriage collect --profile junos --case-id IR-2026-EX1 --replay fx/junos-compromised \
--operator analyst --rebooted no --patched no
python -m edgetriage analyze cases/IR-2026-EX1 --window-start 2026-10-01T00:00:00
The parts of report.md that matter, in the order to read them (... marks lines left out):
**Verdict: COMPROMISED**
likely indicators corroborated across evidence classes (filesystem, persistence): junos-login-record-backups, junos-syslog-conf-backup, junos-unc3886-null-fifo, junos-unc3886-payload-files
...
## What to do next
- Preserve before you act: the case folder may be the only copy of what the device held. Do not reboot, upgrade or factory-reset it until the playbook's preservation steps are done.
...
From the playbook, do not:
- **Run JMRT quick-scan with its default clean action before collecting.** It deletes the malicious files and kills the processes it finds.
...
## Evidence gaps
- **missing-evidence-class** - no integrity evidence in the case
Close it: The junos plan's integrity steps: jmrt_integrity_check (needs --allow-bundle), not run. ...
- **detection-not-evaluated** - junos-unc3886-tinyshell-yara, junos-tinyshell-mandiant-hunting (EDGE-PACK-0010): needs a file to scan: a recovered binary or an ingested image
- **detection-not-evaluated** - junos-jweb-php-log-anonymous, junos-jweb-php-log-session-null (EDGE-PACK-0020): needs step jweb_php_log (`file show /var/jail/sess/php.log | no-more`), which was unavailable, or a file matching **/php.log*
...
## Findings
...
### [LIKELY] Backup copies of the login records
`junos-login-record-backups` - EDGE-PACK-0010 (pack confidence high) - CVE-2025-21590 - 2 hit(s), evidence class persistence
- `file list /var/log detail | no-more` (step listing_var_log), line 5
`-rw-r--r-- 1 root wheel 4410 Oct 14 07:10 wtmp0`
...
### [POSSIBLE] File named by a 64-character hex digest in /var/tmp or the J-Web jail
`junos-jweb-hex-named-upload` - EDGE-PACK-0020 (pack confidence low, unverified) - ...
How to read it:
The verdict and why.COMPROMISED rests on likely hits in two evidence classes, filesystem and persistence, which corroborate each other. The two possible process hits did not raise it; one class of possible hits never would.
What to do next comes before anything else. The do-not lines are quoted from the Junos playbook. The first is the one that would destroy this evidence: JMRT quick-scan's default action deletes what it finds.
The gaps say what the verdict cannot see. The integrity check never ran (it writes to the device, so it needs --allow-bundle), so nothing here says whether veriexec is intact. No recovered binary was scanned, so TINYSHELL's presence is unknown, not ruled out. The J-Web log step failed, so J-Web exploitation is unexamined. Each gap ends with how to close it.
Then the findings, strongest first. Each names the command and line to re-check by hand, and how far to trust the detection behind it. The leftover wtmp0 means the login records themselves may have been altered, so weigh the session list accordingly. The J-Web finding comes from a low-confidence, single-source pack: read the file before you count it.
What to tell the incident lead. "Compromised. The artifacts match tradecraft Mandiant reports for UNC3886; that is a lead, not attribution. Credentials the router held are exposed. The integrity check, a binary scan and the J-Web logs are still outstanding."
--baseline takes a {path: sha256} JSON from a known-good device of the same build. The hash comparison refuses to run without one rather than call every file unknown, so it shows as not evaluated until you supply one.
What fired
Verdict
any confirmed indicator
COMPROMISED
likely indicators in two or more evidence classes
COMPROMISED
likely indicators in one class
LIKELY-COMPROMISED
possible indicators in two or more evidence classes
The timeline puts device events and your own collection steps on one axis, so a log that ends two minutes before you connected is visible. Timestamps the device did not qualify are marked device-local.
Clocks. On every platform with a command line, one of the first collection steps reads the device's clock (FortiOS's get system status, Junos's show system uptime, show clock on Cisco and PAN-OS, date in the bash shells). Ivanti gives you no command line, so take the time from the hypervisor and the exported logs. Compare the device's clock with your own and note the difference before you line device times up with anything else. A device clock that is off by minutes moves every event you correlate against the SIEM, the IdP or the firewall. The off-box checks never depend on it: they line the copies up by message, and they test gaps against activity in UTC.
The brief is a prompt for a model, not a verdict. It numbers and hashes every record, requires a citation for every claim, and tells the model not to change the verdict. Run it against a local or tenant-controlled model: device configurations carry credentials and keys.
The analyzer reads what is on the device. The Hunts library holds hypotheses for what an intruder could not edit, the data you already keep elsewhere: SIEM syslog, sign-in logs, AAA accounting, the next firewall's sessions, proxies, NetFlow. Filter it by *Data you need* to see the hunts your data supports. Each hunt quotes its sources, says what normal looks like, and links the CVEs and actors it came from. Two of them run in code (--forwarded, --activity); the rest you run in your own tools, in whatever query language they speak.
An indicator is not the actor. Most published addresses are compromised third parties, VPN exits or shared hosting. Each value keeps the role its publisher gave it, and unknown when none was given. Read the role before you block, and never attribute on one.
Indicators have a shelf life. Each set states when it was valid and when it was published. Infrastructure gets recycled, so an old address in your logs last week may be someone else's now.
Attribution is reported, not computed. An actor record carries two confidences. The *link confidence* is the toolkit's: it measures provenance, and one reporting party is medium however authoritative. The *stated confidence* is the reporter's own words, verbatim.
The site shows values defanged; exports are refanged. Paste from the export into your SIEM, not from the page.
The CVEs library has one record per CVE: CISA's KEV fields verbatim, the CVE.org record, and everything the toolkit has for it (detections, artifacts, hunts, hardening, playbooks). Read the BOD 26-04 forensic triage flag carefully:
It is a deadline category. Every KEV entry added before the directive (2026-06-10) reads No, which means "added before BOD 26-04", not "no triage needed".
A flagged CVE lists CISA's implementation steps with their target timelines, quoted.
Exploitation dates are the primary sources' own words, quoted with the page they came from. A CVE without one has simply not been dated by a primary source.
Compromised or likely: treat credentials the device held as exposed (VPN users, LDAP bind accounts, local admins, certificates), hunt from the device into the network, and plan replacement or vendor-guided rebuild rather than in-place cleanup.
Follow the platform's response playbook, in its order: preserve, contain, rotate, eradicate, recover, verify. Read its *do not* list first; most evidence is destroyed by a well-meant action taken too early. The toolkit never contains or remediates for you.
Suspicious: find the second signal before you escalate. A hit in a different evidence class is what raises the verdict, and the off-box hunts are where most second signals come from.
No indicators: close the listed gaps before closing the case. The most common honest outcome is "no indicators in the evidence available".
Telling people. Say the verdict in the toolkit's words. "No indicators found in the evidence available" is not "clean", and "suspicious" is not "breached". Attach the evidence gaps; they are what a reader will ask about.
Most of the intrusions behind this toolkit started at a management interface or VPN portal reachable from the internet, an administrator login without MFA, or a device past its end of life, and were then hard to investigate because the logs only lived on the device. The site's Hardening section lists the controls for each platform with their vendor or government sources. The baseline, from CISA BOD 23-02 and the 2025 joint edge-device guidance:
Take management interfaces off the internet, or put them behind a policy enforcement point that is separate from the device.
Phishing-resistant MFA for every administrator and VPN user, with device administration kept out of the corporate AD forest.
Forward logs off the device in real time to a store it cannot modify.
Replace end-of-life devices, and patch knowing that a patch does not remove an implant that is already there.