Linux Memory Forensics with Volatility 3: Hunting Rootkits and Implants in 2026
A practical walkthrough of Linux memory forensics with Volatility 3 in 2026: capturing RAM with AVML or LiME, building ISF symbol tables, running the plugin chain that catches LKM, LD_PRELOAD, and eBPF rootkits, and mapping each detection back to a hardening control.
Linux memory forensics with Volatility 3 is the process of capturing a running system's RAM with a tool like LiME or AVML, then parsing it with the Python-based Volatility 3 framework to enumerate processes, network sockets, loaded modules, and injected code that disk-based artifacts cannot reveal. As a pentester who lives in misconfigured servers, I treat memory as the single most honest source I have on a box. By the time you reach the disk, the attacker has already lied to you. This guide walks the full chain: acquisition, profile generation, plugin-by-plugin analysis, and the hardening controls that map back to each detection.
Volatility 3 v2.28.x ships 40+ Linux plugins including linux.hidden_modules, linux.ebpf, linux.netfilter, and the new XFRM key extractor.
AVML is the fastest path to a forensically sound dump on heterogeneous fleets (no kernel headers, no compilation, single static Rust binary).
LiME still wins on lockdown-disabled hosts because it streams over TCP and can hash on the fly, though kernel lockdown and module signing increasingly block it.
You need a matching ISF symbol table (JSON.xz) for every kernel you analyze. Generate one with dwarf2json from the kernel's vmlinux or DWARF debug package.
Rootkit hunting hinges on cross-view diffing: linux.pslist vs linux.psaux vs linux.check_syscall vs linux.hidden_modules. Any disagreement is a tell.
Hardening controls (Secure Boot, kernel lockdown, IMA, eBPF restriction) reduce attack surface but also break naive acquisition. Plan acquisition before you harden.
Why memory forensics still beats disk forensics
Every implant I've shipped against a Linux target lives in memory before it touches the disk, and most live there permanently. A modern attacker drops a userland LD_PRELOAD library, hooks a syscall via an LKM or eBPF, opens a reverse shell, scrubs /var/log, and replaces ps, ss, and lsmod with trojaned binaries. All of this without ever creating a persistent artifact that survives find / -mtime -1. The instant the box is rebooted, that timeline evaporates. RAM is the only place every active process, socket, ELF mapping, and namespace state is held simultaneously and consistently, which is exactly what makes it brutal to fake.
The 2026 attacker stack (bring-your-own-vulnerable-driver, in-memory Go droppers, BPF-based pid hiding) assumes defenders only look at the filesystem. Volatility 3 walks kernel structures directly, bypassing every userland hook. So when you run linux.pslist against a memory image, you're reading task_struct entries from the init_task linked list. The trojaned ps binary on disk is irrelevant. That's the whole game.
If you haven't already wired this into your incident response, my earlier Linux incident response playbook covers the live-triage steps that should run before you pull the memory dump. Memory analysis is the second pass, not the first.
How do you capture memory on a Linux system?
You capture Linux RAM with either AVML (Microsoft's static Rust binary, no kernel module required) or LiME (a loadable kernel module that streams the dump to disk or over TCP). On a modern fleet I default to AVML because it doesn't care about your kernel version, your headers, or your distribution. One binary works on RHEL 9, Ubuntu 24.04, Alpine, and a hand-rolled embedded kernel. Drop it on the host, run it, ship the output to a forensic share.
LiME is still the right call when you need to capture over a network without writing to local disk (think: ransomware in progress), or when AVML chokes on an exotic kernel. Build it against the target kernel headers and stream to a netcat listener on the analysis host. The output format is the same .lime container that Volatility 3 consumes natively.
# Analyst box
nc -k -l 4444 > webhost-2026-06-10.lime
# Target box (kernel headers must match exactly)
git clone https://github.com/504ensicsLabs/LiME
cd LiME/src && make
insmod ./lime-$(uname -r).ko \
"path=tcp:10.0.0.5:4444 format=lime digest=sha256"
If you haven't built your acquisition runbook yet, pair this with the first-hour steps in my Linux server hacked: 60-minute runbook. Memory is item three on that list, not item ten.
What is the difference between Volatility 2 and 3?
Volatility 2 is archived; Volatility 3 is a complete Python 3 rewrite that replaces the old per-kernel profile system with auto-detected Intermediate Symbol Format (ISF) tables, ships a faster framework, and is the only branch receiving new Linux plugins. So if you're still on Volatility 2 in 2026, you're working with a dead codebase. The Volatility 3 GitHub repository has been the active project since 2020, and the legacy v2 repo now carries an archive banner.
Feature
Volatility 2
Volatility 3 (v2.28.x)
Python version
2.7 (EOL)
3.8+
Profile system
Hand-built .zip profiles per kernel
Auto-detected ISF JSON.xz symbol tables
Linux plugin count
~35
40+ and growing
eBPF program enumeration
None
linux.ebpf
Hidden module detection
linux_hidden_modules (partial)
linux.hidden_modules (lsmod cross-view)
XFRM IPsec key extraction
None
New XFRM Inspector plugin (May 2026)
Maintenance status
Archived
Active
The plugin namespace changed too. Old linux_pslist is now linux.pslist.PsList. Every command starts with vol -f <dump> linux.<plugin>. Symbol tables are pulled automatically from the bundled symbol cache when the kernel banner matches; otherwise you generate one with dwarf2json. There's no longer any concept of "selecting a profile." v3 reads the banner string out of the dump and finds the matching ISF on its own.
Installing Volatility 3 and building symbol tables
Install in a virtualenv so the framework's pinned dependencies don't collide with system Python. The 2.28.x line requires Python 3.8 or newer and pulls in pefile, capstone, and yara-python as optional extras you almost certainly want.
For real coverage, clone the repo. The pip wheel lags the GitHub develop branch by weeks, and new plugins land there first. Then point Volatility at the community symbol cache, which mirrors pre-built JSON.xz tables for Debian, Ubuntu, AlmaLinux, Rocky, and Fedora kernels.
When the kernel is something you compiled yourself or a pinned LTS that's not in the cache, build the ISF locally from the kernel's debug symbols. On Ubuntu, install linux-image-$(uname -r)-dbgsym from ddebs.ubuntu.com; on RHEL, install kernel-debuginfo.
# dwarf2json builds the JSON.xz symbol table
go install www.velocidex.com/golang/go-pe/cmd/dwarf2json@latest
dwarf2json linux \
--elf /usr/lib/debug/boot/vmlinux-6.8.0-31-generic \
| xz -9 > ~/vol3-symbols/linux/Ubuntu-24.04-6.8.0-31.json.xz
Core Volatility 3 Linux plugins, by attack stage
I structure every investigation by attack stage, not by plugin alphabetical order. This is the loadout I run, in order, against any suspected compromise. Pipe each command's output into a per-host case folder and diff against a known-good baseline from the same image.
1. Establish baseline
linux.banner: kernel build string and boot arguments.
linux.boottime: when the kernel actually booted. A mismatch with system records means someone rebooted to wipe state.
linux.lsmod: loaded kernel modules. Compare against the distribution's expected module list.
2. Enumerate processes with cross-views
linux.pslist: walks the init_task linked list.
linux.pstree: same data, parent-child layout.
linux.psaux: argv for each task. This is where you spot [kworker/u8:0-flush-8:0]-style mimicry with abnormal argv.
linux.pidhashtable: walks the PID hash table independently. Any PID that shows here but not in pslist is hidden.
3. Network state
linux.sockstat: every open socket with PID and inode. C2 connections that ss -tlnp would not show on the live host show up here.
linux.netfilter: registered netfilter hooks. New for 2026; surfaces in-kernel packet filters installed by a rootkit.
4. Persistence and execution
linux.bash: recovers .bash_history from memory even when the on-disk file is empty or symlinked to /dev/null. The single highest-signal plugin, in my experience.
linux.envars: environment variables for each process. LD_PRELOAD in an envar is a finding, full stop.
linux.library_list: loaded shared objects. Compare path against package manager records.
linux.ptrace: tasks currently under ptrace. Legitimate ptrace on a production webserver is suspicious.
Each plugin is one vol -f dump.lime linux.<name> away. The full Volatility 3 Linux tutorial in the official docs is worth bookmarking. It stays current with the plugin set and shows representative output for each command.
Can Volatility detect Linux rootkits?
Yes. Volatility 3 detects Linux rootkits by cross-referencing kernel data structures that a rootkit cannot simultaneously poison without leaving inconsistency. The detection is statistical, not magical: a rootkit hides a module from /proc/modules by unlinking it from one list, but it almost never unlinks from the mod_tree, the kallsyms area, and the sysfs /sys/module view at the same time. Volatility walks all of them and flags the gap.
# Hidden module detection
vol -f webhost.lime linux.hidden_modules
# User-visible module list for comparison
vol -f webhost.lime linux.lsmod
# Syscall table integrity check
vol -f webhost.lime linux.check_syscall
# Any address that does not resolve to a kernel symbol is a hook
# IDT (interrupt descriptor table) hooks
vol -f webhost.lime linux.check_idt
# Cross-references mod list against sysfs view
vol -f webhost.lime linux.check_modules
For an LKM rootkit such as Diamorphine or Reptile, linux.hidden_modules typically catches it on the first pass. For userland-only LD_PRELOAD rootkits (jynx2 lineage), you instead want linux.envars plus linux.library_list, then dump suspicious mappings with linux.proc.Maps and triage with file, strings, and YARA. I covered the kernel-level defensive angle in my eBPF security on Linux writeup. Read it before you assume a "hidden" eBPF program is malicious; Cilium and Tetragon both load plenty of legitimate kprobes.
Hunting eBPF implants with linux.ebpf
eBPF rootkits are the loud trend in 2026: TripleCross, Boopkit, and a half-dozen private toolkits abuse eBPF's kprobe, tracepoint, and xdp hooks to filter packets, hide processes, and inject syscall results from inside the kernel verifier-blessed sandbox. They're notoriously difficult to detect with disk-based EDR because they leave no persistent binary footprint outside the program bytecode in kernel memory.
The new linux.ebpf plugin enumerates loaded eBPF programs, their type, attach points, and the PIDs that loaded them. Anything attached to tcp_sendmsg, sys_execve, or sys_getdents64 on a system that does not run Cilium, Falco, or Tetragon is worth a hard look.
Once you have the suspect PIDs, dump their memory and run YARA. linux.malfind highlights mappings with rwx permissions, which is a strong flag for JIT-compiled shellcode, packer stubs, and in-memory ELFs that never touched the disk.
# Find PIDs with RWX mappings
vol -f webhost.lime linux.malfind
# YARA scan across every process mapping
vol -f webhost.lime linux.yarascan.YaraScan \
--yara-rules /opt/yara/rules/linux_malware.yar
# Dump a single process's memory for offline triage
vol -f webhost.lime -o /cases/dumps \
linux.proc.Maps --pid 1337 --dump
Pair the dumped mappings with a static-analysis pass. I run strings -e l for wide-character C2 URLs, readelf -a for in-memory ELF headers (a frequent rootkit smell), and a quick capstone disassembly on any suspicious rwx region. If you do this work routinely, build a YARA pack around the families you actually see, and version it in the same repo as your detections.
Mapping each detection to a hardening control
This is the part the average forensics guide skips. Each rootkit technique you just learned to detect has a Linux control that prevents it from working in the first place. Build both. Detection is the safety net, hardening is the actual fix.
LKM rootkits (linux.hidden_modules finds): enable module.sig_enforce=1 and Secure Boot so unsigned modules will not load. Reinforce with kernel.modules_disabled=1 after the legitimate set is loaded at boot.
Syscall table hooks (linux.check_syscall finds): enable kernel lockdown in confidentiality mode and kernel.kptr_restrict=2. Combine with the controls in my sysctl kernel hardening guide.
eBPF implants (linux.ebpf finds): restrict bpf() to CAP_BPF + CAP_PERFMON via kernel.unprivileged_bpf_disabled=2, gate at the LSM with BPF LSM, and audit every BPF_PROG_LOAD call with auditd.
LD_PRELOAD userland rootkits (linux.envars finds): drop CAP_SYS_PTRACE, enforce NoNewPrivileges=yes in systemd units, and use AppArmor/SELinux to deny the writable-plus-executable mapping pattern.
Memory acquisition itself: counterintuitively, full lockdown breaks AVML and LiME. Keep a forensic readiness profile (lockdown=integrity, not confidentiality) on response-ready hosts, or pre-stage an eBPF-based LEMON-style acquisition path that does not require module loading.
The Volatility Foundation publishes the upstream framework; the techniques here apply equally to live response platforms like Velociraptor that wrap Volatility under the hood. Wire the entire chain (acquisition, analysis, hardening) into a single runbook and rehearse it before you need it. The first time you run Volatility 3 should not be at 03:00 with a production breach in progress.
Frequently Asked Questions
Is Volatility 3 free to use commercially?
Yes. Volatility 3 is published under the Volatility Software License v1.0, which is permissive for both commercial and non-commercial use. You can ship it inside an internal IR platform without paying royalties; the only restrictions are on rebranding the framework itself as a competing product.
How do I analyze a Linux memory dump without the matching kernel symbols?
Generate symbols on a sacrificial VM that runs the same kernel build. Install linux-image-$(uname -r)-dbgsym on a fresh Ubuntu/Debian instance, run dwarf2json against /usr/lib/debug/boot/vmlinux-*, xz the JSON, and drop it under your symbols directory. For RHEL, install kernel-debuginfo from the matching CentOS Stream or RHEL release and follow the same flow.
Can Volatility 3 extract encryption keys from Linux RAM?
Yes, for several key types. The XFRM Inspector plugin (May 2026) extracts IPsec session keys directly from kernel state tables, and community plugins exist for LUKS master keys and TLS session keys held in OpenSSL contexts. Combined with a contemporaneous packet capture, these let you retroactively decrypt traffic that was encrypted on the wire.
Does Volatility 3 work on containers and Kubernetes nodes?
Yes, but you analyze the host's memory, not the container's. Containers share the host kernel, so a dump of the node captures every container's processes, sockets, and mappings. Filter by cgroup or namespace in the output to scope to a specific container. For ephemeral workloads, automate AVML acquisition via a DaemonSet that triggers on a Falco or Tetragon alert.
What is the difference between LiME and AVML for memory acquisition?
LiME is a loadable kernel module that needs to be compiled against the exact target kernel's headers; AVML is a static Rust binary from Microsoft that runs on any Linux without compilation. LiME is more flexible for streaming over the network and for integrated hashing; AVML is faster to deploy across heterogeneous fleets because it has no build step. Both produce the same .lime output format that Volatility 3 consumes.
How Linux kernel live patching works in 2026 with kpatch, Ubuntu Livepatch, and TuxCare KernelCare. Install commands, comparison table, and audit tips for zero-downtime CVE fixes.
Wolfi vs Distroless vs Alpine base images for 2026 Linux workloads, with CVE benchmarks, apko and cosign pipeline snippets, and real migration examples for Node.js and Python services.
Deploy Keylime continuous remote attestation on Linux with TPM 2.0: registrar, verifier, Rust agent, IMA runtime policies, and revocation webhooks in 2026.