April 24, 2026

When strings Won't Cut It: Volatility 3 for CTF Memory Forensics

Five Volatility 3 plugins in the right order solve most CTF memory dumps. A decision tree for CTF players, plus a two-way walkthrough of picoCTF 2025 Bitlocker-2.

strings is the first move, not the last

The picoCTF 2025 Bitlocker-2 writeup I keep linking to has one command in its solve block: strings memdump.mem | grep picoCTF. That is the entire answer. A BitLocker drive was mounted when someone captured RAM, the decrypted flag sat in memory as printable bytes, and grepfinds it in eight seconds. It's the cleanest memory-forensics solve on the site. It's also a trap, because every picoCTF player who sees it walks away thinking memory forensics is strings plus patience.

After cataloguing the Forensics category across every picoCTF edition on this site, I can tell you what the RAM-dump corner actually contains: Bitlocker-2. That is the list. One challenge, solvable with the same one-liner that solves most steganography puzzles. Which is fine, until the organisers eventually ship a problem where the flag isn't sitting in printable bytes and you need a tool that can reconstruct the state the machine was in when the snapshot was taken.

That tool is Volatility 3. It takes a raw memory snapshot and parses it back into the kernel data structures a live machine would have in RAM: process lists, open files, network connections, loaded drivers, page tables. Once parsed, you query those structures as if the machine were still running. You have solved a few forensics challenges with file, binwalk, and strings. You have never opened Volatility. By the end of this post you'll know the five plugins that matter for CTF-scale dumps, in the order the evidence sends you, not the order the docs list them.

SummaryMemory forensics means running tools against a snapshot of RAM from a system that was live when the snapshot was taken. The snapshot preserves process state, network connections, decrypted file contents, cryptographic keys in memory, and the attacker's injected code if the machine was compromised.

Install once, skip the profile fight

Volatility 3 is the version to install in 2026. Not Volatility 2. The v3 release is worth teaching because it makes the biggest beginner pitfall disappear, which is having to guess a Windows profile before any plugin will run. The current stable is v2.27.0 (released 2026-01-29, confusingly numbered because the framework version and the Volatility version diverged a while back).

pip install volatility3
vol --help

The CLI is vol (or vol.py if you cloned from source). Point it at a dump and ask it the first question any investigator asks:

vol -f memdump.mem windows.info

The first time v3 sees a Windows dump, it looks at kernel signatures, fetches the matching ISF (Intermediate Symbol Format) table from the Volatility Foundation CDN, caches it, and prints the OS version and build. In Volatility 2 you used to guess --profile=Win10x64_19041 and watch errors until you guessed the right one. Not anymore.

Heads upv3 auto-downloads Windows symbols. Linux and macOS still need manual ISF generation via dwarf2json. Every memory challenge on this site is Windows, so ignore that until it bites you.

What actually changed from v2

Three things matter when you read an old cheat sheet and nothing matches:

  • No profiles. v3 identifies the OS from the dump itself.
  • Plugins are namespaced. The old pslist is now windows.pslist. Every old-syntax plugin in a SANS cheat sheet gets a windows. prefix.
  • Output goes through a renderer. Default is a column-aligned table (the framework calls it a TreeGrid internally). Pass -r csv or -r json when you want to grep or post-process.

The four-question ladder

Volatility 3 ships dozens of plugins. You don't need them.

For a CTF-scale dump, five plugins run in the right order will crack almost every memory challenge you see. The order isn't alphabetical and it isn't what the plugin listing gives you. It's the order that answers four questions about the machine:

  1. What was running? windows.pslist (plus windows.pstree for the parent-child view).
  2. What did it talk to? windows.netscan.
  3. What did it touch? windows.cmdline, windows.filescan, windows.handles.
  4. What did it hide? windows.malfind, windows.registry.hivelist, and the third-party windows.bitlocker plugin when encryption is in play.

One question, mostly one plugin. If the brief hints at a user typing commands when the dump was captured, skip to cmdline. If the scenario mentions malware, malfindis where the flag lives. Let the brief tell you where to start, and let each plugin's output send you to the next.

The decision tree

The ladder tells you which questions to ask. This is the shape of answering them. Every Volatility tutorial I've read publishes a linear plugin list. SANS publishes a six-phase methodology; HackTricks publishes an unordered menu; Varonis publishes a workflow. None of them publish an if-then tree where the output of one plugin routes you to the next. For CTF triage that's exactly the shape you need.

TakeawayRead top-down. Run windows.pslistfirst. If a process has a weird image path, no parent process ID (PPID), or a name that doesn't belong, follow the left column to investigate that process in depth. If everything looks like a stock machine that happened to have Outlook and Chrome open, follow the right column to look at connections, files, and registry. The DumpMe walkthrough below actually crosses the two columns: it starts right (netscan surfaces the suspect), then jumps left (pstree and malfind on that PID). Real investigations do this.

The part tutorials skip: you'll dead-end. Not every branch finds a flag. Some dumps were captured minutes before the interesting thing happened, and every plugin returns clean output. That's when you loop back to the challenge brief, which you've now read three times and which probably contains the hint you need.

Walkthrough: Bitlocker-2, two ways

The site page for Bitlocker-2 shows the intended solve. Here is that solve, then here is what Volatility 3 would have told you instead.

The strings solve (eight seconds)

gunzip memdump.mem.gz
strings memdump.mem | grep picoCTF
# picoCTF{B1tl0ck3r_dr1v3_d3crypt3d_902...}

Flag submitted. Challenge solved. This works because BitLocker was mounted when the RAM capture happened, Windows keeps plaintext of recently-accessed files in its page cache, and stringsscans every printable byte in the dump. If the flag was inside a file on the encrypted volume and someone had read that file in the minutes before capture, it's sitting in cache as ASCII.

The Volatility path (five minutes, and you learn something)

The reason to run Volatility on the same dump is to recover the FVEK (Full Volume Encryption Key), the actual AES key BitLocker holds in RAM while the volume is mounted. Once you have it, you can decrypt the disk image independent of the user password.

Mainline Volatility 3 doesn't ship a BitLocker plugin. There's a community port at github.com/lorelyai/volatility3-bitlocker. Clone it, point v3 at it through --plugin-dirs:

git clone https://github.com/lorelyai/volatility3-bitlocker
vol -f memdump.mem -vvv \
--plugin-dirs ./volatility3-bitlocker \
windows.bitlocker.BitlockerFVEKScan \
--tags FVEc Cngb None --dislocker
# writes one or more 0x<addr>-Dislocker.fvek files in the cwd

The plugin may emit more than one FVEK candidate (one per plausible key address it finds). Pick the first one, or try each in turn. Now mount the disk image with dislocker using the uppercase -K flag, which is the FVEK flag (lowercase -k is for BEK (BitLocker External Key) recovery files and will silently fail on an FVEK):

sudo dislocker -V bitlocker-2.dd -K ./0x<addr>-Dislocker.fvek -- /mnt/dislocker
sudo mount -t ntfs-3g /mnt/dislocker/dislocker-file /mnt/decrypted
ls /mnt/decrypted/

The volume is mountable without knowing the password. Everything Jacky had on that drive is now browsable. The flag is one of the files, yes, but so is everything else.

strings retrieves a flag. Volatility reconstructs the state of a machine. Those are different jobs.
Side noteMost public Bitlocker-2 writeups (cbarkr, 4n6post) still drive Volatility 2 with breppo/Volatility-BitLocker, because the v2 plugin has more battle-testing and the v3 port is newer. If the v3 port throws a kernel-struct error on your dump, fall back to v2 with a matching --profile flag. No shame in mixing.

The deeper point isn't that Volatility was necessary for Bitlocker-2. It wasn't. The point is that the plugins reconstruct the machine. When a future picoCTF dump hides the flag inside a file that was opened and closed before capture, strings will return gigabytes of unrelated text and the FVEK path will still work.

When Volatility is the only path

Bitlocker-2 is a shortcut case: the scenario announces itself (encrypted drive, unlocked at capture time), so you can skip the triage tree and reach straight for the plugin built for exactly this shape of problem. That is honest, and it is why Bitlocker-2 alone does not teach you the tree. For a case where the scenario does not announce itself and the plugins actually carry the investigation, the reference sample is CyberDefenders Lab 65 DumpMe. Free download with an account. The scenario: an employee's Windows 7 workstation got compromised with a Meterpreter payload (Meterpreter is Metasploit's interactive payload framework). You have the triage memory dump. Find the attacker's process, the C2 (command-and-control) connection, and the injected code.

Why stringsdies here: the payload isn't flag text, it's a Meterpreter session. The evidence you care about is the live C2 connection and the RWX (read-write-execute) anonymous memory region holding the staged PE. Neither of those shows up as printable ASCII you can grep. A strings pass on the whole dump returns thousands of hits for anything vaguely hostname-shaped, none of which tell you which process was talking or what it was running.

The three-plugin path (reproduced from DFIRHive's Volatility 3 walkthrough, October 2025):

# Q2: what did each process talk to?
vol -f Triage-Memory.mem windows.netscan
# ... PID 3496 TCP ... 10.0.0.106:4444 ESTABLISHED UWkpjFjDzM.exe

One process has an outbound connection on port 4444, which is Metasploit's default handler port. That alone makes PID 3496 the suspect. The process name closes it: UWkpjFjDzM.exe. Legitimate binaries on a Windows 7 workstation don't have randomized ten-character names, and a dropper that couldn't bother picking a convincing filename has been running long enough to hold an active C2 session.

# Q3a: what arguments was it invoked with?
vol -f Triage-Memory.mem windows.cmdline | grep 3496
# (empty)
# Q3b: who spawned it?
vol -f Triage-Memory.mem windows.pstree
# hfs.exe (3952) -> wscript.exe (5116) -> UWkpjFjDzM.exe (3496) -> cmd.exe (4660)

cmdline for 3496 comes back empty, which for a running process is already a signal: benign binaries almost always have arguments or at least a path. The parent chain is what pstree draws, and the shape it draws is unambiguous. HFS (an HTTP file server) was handing a script to wscript.exe, wscript executed the script, the script dropped and ran the random-named binary, and the binary spawnedcmd.exe for whoever was on the other end of the C2 session. That is the Metasploit delivery shape: loader, dropper, shell.

# Q4: what did it hide?
vol -f Triage-Memory.mem windows.malfind --pid 3496
# RWX anonymous regions with MZ headers = injected PE

Shellcode found. MZ is the magic bytes of a Windows PE (Portable Executable); seeing them inside an anonymous RWX region means someone dropped a full executable image into memory that was never loaded from disk in the normal way. Dump the suspicious regions for offline analysis (pass --dump tomalfind itself; it writes one file per flagged region):

vol -f Triage-Memory.mem windows.malfind --pid 3496 --dump

Notice the sequence. From pslist alone, nothing obviously wrong. From strings, nothing either. It's netscan surfacing the suspect, pstree and cmdline drawing the parent chain, then malfind confirming the injected code, in that order, that tells the story. If a future picoCTF memory challenge ever looks like this, you now have the path through it.

TakeawayThe second plugin choice was decided by the first plugin's output, and the third by the second. That is the tree in action, not a cheat sheet read top-to-bottom.

Alternatives and escape hatches

A few honest caveats and follow-ups for when Volatility 3 isn't the right tool or when it breaks on your specific dump.

Volatility 2 is still relevant

Old plugins never got ported. The community breppo/Volatility-BitLocker for v2 has broader compatibility than its v3 successor. Same story for a handful of rootkit-specific and registry-specific plugins. Install v2 alongside v3. When the v3 plugin fails with a kernel-struct error, drop to v2 with --profile=Win10x64_19041(or whatever the dump's build dictates), run the old plugin, and go back to v3 for everything else.

MemProcFS for interactive work

MemProcFS (Ulf Frisk, current release v5.17 from February 2026) takes a different approach: it mounts the memory dump as a filesystem. You cd into /mnt/memprocfs/pid/3496/modules/ and every artefact is a file you can cat, grep, and diff. For CTFs with 10+ memory challenges and a tight clock, plugins are faster. For interactive exploration and learning what data even exists inside a memory dump, MemProcFS is genuinely nicer. Pick the one that matches the task.

Rekall is dead

Google archived Rekall in October 2020. Do not install Rekall in 2026. Older walkthroughs reference it; skip those sections.

Required reading

Halderman et al., 2008, Lest We Remember: Cold Boot Attacks on Encryption Keys. Alex Halderman and eight co-authors across Princeton, the EFF, and Wind River, USENIX Security 2008. The paper that showed RAM contents survive long enough after power-off that an attacker can chill the DIMMs with a compressed-air duster, pop them into a second machine, and read out encryption keys before they decay. That is the foundational insight of memory forensics as a discipline: if keys persist in RAM after power loss, they definitely persist in a dump taken while the system is running. Forty minutes of reading, and every memory-forensics tutorial since has been a footnote to it.

While you have the tool installed, solve Bitlocker-2 twice. Solve it with strings first because you can do it in eight seconds. Then install the v3 BitLocker plugin, extract the FVEK, mount the disk image with dislocker, and browse what Jacky had on that drive. The first solve gets you the flag. The second solve teaches you what memory forensics actually is.

When the next picoCTF memory dump drops and strings | grep picoCTFcomes back empty, that's not the end of the challenge. It's the start.

Quick reference

QuestionPluginOne-line purpose
Where am I?windows.infoOS version, build, kernel base
What ran?windows.pslistActive process list
Parent-child tree?windows.pstreeProcesses in a tree by PPID
What was it told to do?windows.cmdlineCommand-line args for each process
What libs did it load?windows.dlllistLoaded DLLs per process
What did it talk to?windows.netscanNetwork sockets and connections
What files did it open?windows.filescanFile objects in kernel memory
Any injected code?windows.malfindRWX anonymous regions (shellcode)
Registry hives in memory?windows.registry.hivelistOffsets of loaded registry hives
Hashes and secrets?windows.hashdumpSAM (Security Account Manager) hashes from memory
BitLocker volume?windows.bitlocker (community)FVEK extraction, pair with dislocker