Shell snapshots base64-encode function bodies, making obfuscated content indistinguishable from a malicious payload

Resolved 💬 2 comments Opened Apr 19, 2026 by daira Closed May 26, 2026

Summary

Claude Code's shell-snapshot files (at ~/.claude/shell-snapshots/snapshot-bash-<id>-<token>.sh) serialize captured shell functions by base64-encoding the function body and wrapping it in an eval "$(echo '<base64>' | base64 -d)" invocation.

This happens even for entirely benign functions captured from the user's interactive shell environment. Example — a literal line from one of my snapshot files:

eval "$(echo 'Z2F3a2xpYnBhdGhfYXBwZW5kICgpIAp7IAogICAgWyAteiAiJEFXS0xJQlBBVEgiIF0gJiYgQVdL
TElCUEFUSD1gZ2F3ayAnQkVHSU4ge3ByaW50IEVOVklST05bIkFXS0xJQlBBVEgiXX0nYAogIGV4cG9y
dCBBV0tMSUJQQVRIPSIkQVdLTElCUEFUSDokKiIKfQo=' | base64 -d)" > /dev/null 2>&1

That decodes to a perfectly ordinary function from the gawk Debian package's /etc/profile.d/gawk.sh:

gawklibpath_append ()
{
    [ -z "$AWKLIBPATH" ] && AWKLIBPATH=`gawk 'BEGIN {print ENVIRON["AWKLIBPATH"]}'`
  export AWKLIBPATH="$AWKLIBPATH:$*"
}

A single snapshot file on my system had 6 such blobs; I have ~10 snapshot files sitting in ~/.claude/shell-snapshots/.

Why this is a security concern

The obfuscation looks suspicious even when it isn't. A reviewer glancing at the snapshot cannot distinguish between:

  1. A legitimate shell function captured by Claude Code's snapshotter and re-serialized through base64, and
  2. A malicious payload injected by something Claude Code has sourced or read from the user's environment.

This matters because:

  • A compromised entry in a directory the user considers "trusted" — an attacker-supplied .bashrc fragment, a dotfile from a third-party setup script, a direnv-style file sourced at shell startup, an entry in /etc/profile.d/ from a compromised package — could smuggle arbitrary code into the snapshot, and it would blend in perfectly with the existing base64 blobs.
  • A user who inspects the snapshot file (as users should be encouraged to do when evaluating the trust surface of any tool that loads their shell state) has no easy way to audit it. They have to base64-decode every blob individually to see what it actually executes on every subsequent Bash tool call.
  • The format effectively trains the user to accept that Claude Code's internal files contain opaque obfuscated code, undermining one of the natural defenses against a supply-chain or environment-level attack. "This is normal for Claude Code" becomes a cover story for an attacker.

This is defense-in-depth: even if the snapshot content itself is never malicious today, the presence of opaque obfuscated code in a tool's own state directory degrades the user's ability to notice when something else has been compromised.

Suggested remediation

Preferred: store captured function bodies as plain shell text, matching what the user would see from declare -f directly, so a reviewer can read them in place. If certain edge cases (embedded single quotes, trailing newlines, unusual escapes) require careful quoting, prefer heredocs or explicit escaping rather than opaque base64 wrapping.

If base64 wrapping is necessary, please at minimum:

  • Put a clear banner at the top of the snapshot file explaining that base64 is used for serialization of specific constructs, pointing the user at how to audit it, and listing the function names included so a user can spot-check by name without having to decode each blob.
  • Produce a deterministic, diffable format — the current format is hard to diff across sessions, which also hurts audit.

Environment

  • Claude Code running Opus 4.7 (1M context) on a Linux (Debian-family) host
  • bash shell, declare -f is the capture mechanism based on the surrounding snapshot file structure
  • Snapshot format observed: # Snapshot file header, unalias -a, then many eval "$(echo '<base64>' | base64 -d)" > /dev/null 2>&1 lines

Attribution

This report was drafted by Claude (Opus 4.7) at the user's request during a Claude Code session, after the user flagged the obfuscated-looking content in ~/.claude/shell-snapshots/.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗