Aller au contenu

Triage forensique Linux pour la réponse à incident

Un guide pratique de triage des serveurs et conteneurs Linux compromis : capture mémoire, analyse de /proc, clés SSH, journaux, recherche de persistance, contrôle des rootkits et collecte avec UAC.

ABYSSS Security Team25 min de lecture

Un terminal Linux et des artefacts comme proc, auth.log, cron, systemd et les clés autorisées rassemblés dans un dossier de preuves haché.
Sommaire

Introduction

Linux fait tourner la plupart des serveurs exposés à Internet, des conteneurs et des charges cloud visés par les attaquants, et ses incidents diffèrent de ceux de Windows. Il n’y a ni registre ni prefetch, la journalisation dépend beaucoup de la configuration de la machine, et de nombreuses intrusions aboutissent à un mineur de cryptomonnaie, un web shell ou une clé SSH discrète plutôt qu’à un rançongiciel. Ce guide couvre le triage d’un hôte Linux suspect, du premier contact au confinement : quoi collecter, dans quel ordre, avec quelles commandes et quels outils, et ce que signifient les preuves.

Règles de base du triage Linux

  • Ne faites pas confiance aux binaires de l’hôte. Les rootkits en espace utilisateur remplacent ps, ls, netstat et ss, et les rootkits LD_PRELOAD interceptent les appels de bibliothèque de ces outils. Apportez des outils liés statiquement sur un support en lecture seule et comparez leurs résultats avec /proc.
  • Collectez par ordre de volatilité : la mémoire, puis les connexions réseau et les processus, puis les utilisateurs connectés et les systèmes de fichiers temporaires comme /tmp, /dev/shm et /run, puis le disque et les journaux.
  • Limitez votre empreinte. Chaque commande modifie des dates d’accès, écrit dans l’historique du shell et peut écraser des données supprimées. Écrivez sur un support externe ou diffusez la sortie sur le réseau, jamais sur le système de fichiers suspect.
  • Ne redémarrez pas. La mémoire, les logiciels malveillants actifs, le contenu de /dev/shm et l’état du réseau disparaissent, et un redémarrage peut déclencher un nettoyage de l’attaquant ou une charge destructrice placée dans un script de démarrage.
  • Travaillez en UTC et notez chaque commande avec son horodatage. L’utilitaire script le fait pour vous.
  • Si l’hôte est une VM cloud, faites un instantané des disques via l’API du fournisseur avant toute action invasive. Cet instantané est votre copie de preuve si l’hôte est détruit.
Démarrer un shell de réponse propre et enregistré
sudo mkdir -p /mnt/ir && sudo mount -o ro /dev/sdb1 /mnt/ir      # read-only toolkit
export PATH=/mnt/ir/bin:$PATH                                   # trusted static binaries first
export LC_ALL=C TZ=UTC
unset HISTFILE                                                  # do not overwrite user history

OUT=/mnt/evidence/$(hostname)-$(date -u +%Y%m%dT%H%M%SZ)        # separate writable evidence disk
mkdir -p "$OUT"
script -q -a "$OUT/session.log"

# No spare disk? Stream each output to your analysis host instead:
ps auxwwf | ssh analyst@10.0.0.50 "cat > /cases/host01/ps.txt"
Rechercher un détournement de bibliothèques avant de se fier aux binaires dynamiques
cat /etc/ld.so.preload 2>/dev/null          # normally missing or empty
echo "LD_PRELOAD=$LD_PRELOAD"
grep -r LD_PRELOAD /etc/environment /etc/profile /etc/profile.d /etc/bash.bashrc 2>/dev/null
ldd /bin/ps /bin/ls /usr/bin/ss              # unexpected libraries are a warning sign

Si /etc/ld.so.preload existe et mentionne une bibliothèque inexpliquée, supposez qu’un rootkit en espace utilisateur est actif et que les outils ordinaires mentent. Passez entièrement aux binaires statiques et à la lecture directe de /proc, et prévoyez une analyse de la mémoire.

La boîte à outils de triage Linux

Boîte à outils de triage Linux
OutilFonctionRemarques
UAC (Unix-like Artifacts Collector)Réponse à chaud scriptée et collecte d’artefactsGratuit ; Linux, BSD, macOS, Solaris, AIX ; aucune installation
VelociraptorRéponse à chaud à distance et chasse sur tout le parcGratuit ; même serveur pour Windows, Linux et macOS
AVMLAcquisition de la mémoire sans module du noyauGratuit, par Microsoft ; fonctionne sur la plupart des noyaux modernes
LiMEAcquisition de la mémoire via un module du noyau chargeableDoit être compilé pour le noyau exact en cours d’exécution
Volatility 3Analyse de la mémoireExige une table de symboles (ISF) construite pour le noyau exact
busybox et coreutils statiquesRemplaçants de confiance pour ps, ls, netstat et autresÀ construire ou télécharger à l’avance, avec calcul des empreintes
unhide, chkrootkit, rkhunterContrôle des processus et ports cachés et des rootkitsFaux positifs à prévoir ; comparez à une référence saine
YARA, THOR Lite, LokiAnalyse par signatures des fichiers et de la mémoire des processusRègles communautaires pour les mineurs, les web shells et les logiciels malveillants Linux
bpftoolListe des programmes et maps eBPF chargésDétecte les rootkits eBPF et le traçage inattendu
The Sleuth Kit et PlasoAnalyse d’images disque et chronologiesfls, mactime, istat ; log2timeline pour les super chronologies
osqueryRequêtes SQL sur l’état du système en directUtile pour poser les mêmes questions sur de nombreux hôtes

Les premières minutes : le contexte

Identité de l’hôte, heure et durée de fonctionnement
{
  date -u; hostname -f; cat /etc/os-release; uname -a
  uptime; cat /proc/uptime; timedatectl 2>/dev/null
  ip -br addr; ip route
  cat /etc/resolv.conf
  df -hT; findmnt -D
  who -a; w
} > "$OUT/context.txt" 2>&1

Notez tout ce qui semble déjà anormal : une durée de fonctionnement correspondant au début de l’activité suspecte, des montages inhabituels comme un tmpfs mal placé, un résolveur DNS inconnu ou une session d’administrateur depuis une adresse inconnue. Si l’hôte fait partie d’un cluster ou se trouve derrière un répartiteur de charge, notez ses pairs ; vous devrez probablement les examiner aussi.

Acquisition et analyse de la mémoire

Les logiciels malveillants sans fichier, le code injecté, les binaires supprimés encore actifs et les configurations déchiffrées se trouvent tous en mémoire. AVML est l’outil le plus simple sur les systèmes modernes, car il n’exige aucun module du noyau ; LiME est la solution de repli lorsqu’AVML ne peut pas lire la mémoire, mais il doit être construit pour la version exacte du noyau, idéalement sur une machine saine équivalente plutôt que sur l’hôte suspect.

Acquérir la mémoire
# AVML: a single static binary
sudo /mnt/ir/bin/avml "$OUT/memory.lime"
sha256sum "$OUT/memory.lime" | tee -a "$OUT/hashes.txt"

# LiME: module compiled for $(uname -r) on a clean build host
sudo insmod /mnt/ir/lime/lime-$(uname -r).ko "path=$OUT/memory.lime format=lime"
sudo rmmod lime

Volatility 3 a besoin d’une table de symboles pour la version exacte du noyau. Générez-la avec dwarf2json à partir des symboles de débogage, publiés sous linux-image-*-dbgsym sur Ubuntu, linux-image-*-dbg sur Debian et kernel-debuginfo sur RHEL. Faites-le sur votre machine d’analyse, pas sur l’hôte suspect.

Construire les symboles et analyser avec Volatility 3
dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-6.8.0-45-generic > volatility3/symbols/linux/6.8.0-45-generic.json

vol -f memory.lime linux.pslist
vol -f memory.lime linux.pstree
vol -f memory.lime linux.psaux               # full command lines
vol -f memory.lime linux.bash                # bash history recovered from memory
vol -f memory.lime linux.sockstat            # sockets per process
vol -f memory.lime linux.lsof --pid 4127
vol -f memory.lime linux.malfind             # injected executable memory
vol -f memory.lime linux.lsmod
vol -f memory.lime linux.check_modules       # modules hidden from the module list
vol -f memory.lime linux.check_syscall       # hooked system call table entries
vol -f memory.lime linux.envars --pid 4127

Le plugin linux.bash est souvent le résultat le plus utile : il récupère l’historique des commandes dans la mémoire des processus bash actifs, y compris les commandes de sessions où l’attaquant a désactivé ou supprimé le fichier d’historique.

Processus

Liste des processus avec heures de démarrage et lignes de commande complètes
ps -eo pid,ppid,user,lstart,etime,stat,%cpu,%mem,cmd --forest > "$OUT/ps.txt"
ps auxwwf >> "$OUT/ps.txt"

# Raw view from /proc, which a userland rootkit cannot easily filter
for p in /proc/[0-9]*; do
  pid=$(basename "$p")
  printf '%s\t%s\t%s\t%s\n' "$pid" "$(awk '/^PPid/{print $2}' "$p/status" 2>/dev/null)" \
    "$(readlink "$p/exe" 2>/dev/null)" "$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null)"
done > "$OUT/proc-list.tsv"
Examen approfondi d’un processus suspect
P=4127
ls -l /proc/$P/exe /proc/$P/cwd                 # "(deleted)" means the binary was removed
tr '\0' ' ' < /proc/$P/cmdline; echo
tr '\0' '\n' < /proc/$P/environ                 # SSH_CLIENT, PWD, unusual variables
cat /proc/$P/status | grep -E 'Name|PPid|Uid|Gid'
ls -l /proc/$P/fd                               # open files, sockets, pipes
cat /proc/$P/maps | grep -v '\.so'              # anonymous and odd mappings
cat /proc/$P/net/tcp /proc/$P/net/tcp6

# Recover the binary even if it was deleted from disk
cp /proc/$P/exe "$OUT/pid-$P.bin" && sha256sum "$OUT/pid-$P.bin" | tee -a "$OUT/hashes.txt"
Recherches rapides sur tous les processus
# Processes whose binary has been deleted
ls -l /proc/[0-9]*/exe 2>/dev/null | grep deleted

# Processes running from temporary or hidden locations
ls -l /proc/[0-9]*/exe 2>/dev/null | grep -E '/tmp/|/var/tmp/|/dev/shm/|/run/user/|/\.'

# User processes pretending to be kernel threads: real kernel threads have PPID 2 and no exe
ps -eo pid,ppid,args | awk '$2 != 2 && $2 != 0 && $3 ~ /^\[/'

# Hash every running executable through /proc, including deleted ones
sha256sum /proc/[0-9]*/exe 2>/dev/null | sort -u -k1,1 > "$OUT/exe-hashes.txt"

# Processes hidden from ps
unhide proc; unhide sys
  • Interpréteurs avec sockets réseau : bash, sh, python, perl ou php tenant une connexion TCP signifie généralement un reverse shell. Recherchez /dev/tcp dans les lignes de commande.
  • Enfants du serveur web : nginx, apache2, httpd, php-fpm ou java lançant sh, bash, curl, wget ou nc indique une exécution de code via l’application web.
  • Une charge processeur élevée et durable d’un processus au nom inconnu est le signe classique d’un mineur, souvent nommé comme un service système (kworkerds, kthreaddi, systemd-udevd au mauvais emplacement).
  • Processus dont le répertoire de travail est /tmp, /dev/shm ou un dossier caché, et processus de comptes de service comme www-data ou postgres qui ne devraient jamais lancer d’outils interactifs.

État du réseau

Connexions, écoutes et routage
{
  ss -tunap; ss -xp
  ip addr; ip route; ip neigh
  ip link | grep -i promisc              # sniffers put interfaces in promiscuous mode
  iptables-save 2>/dev/null; nft list ruleset 2>/dev/null
  cat /etc/hosts
  conntrack -L 2>/dev/null | head -500
} > "$OUT/network.txt" 2>&1

# Compare against the kernel tables directly
cat /proc/net/tcp /proc/net/tcp6 /proc/net/udp > "$OUT/proc-net.txt"
unhide-tcp                                # ports in use but hidden from ss

Surveillez les connexions vers des ports inhabituels (3333, 4444, 5555, 7777 et 14444 sont des ports courants de pools de minage et de reverse shells), les connexions sortantes durables des processus serveur, les écoutes sur des ports élevés liées à toutes les interfaces et les règles de pare-feu non créées par la gestion de configuration. Une courte capture de paquets peut confirmer le contenu d’une connexion suspecte ; excluez-en votre propre session.

Courte capture de paquets ciblée
timeout 300 tcpdump -i any -nn -s 0 -w "$OUT/capture.pcap" 'not (host 10.0.0.50 and port 22)'

Utilisateurs, clés et authentification

Sous Linux, les attaquants conservent leur accès surtout via des comptes et des clés SSH plutôt que des logiciels malveillants. Une nouvelle clé autorisée survit à chaque correctif, à chaque nettoyage et à chaque changement de mot de passe : la revue des clés et des comptes est donc l’une des parties les plus importantes du triage Linux.

Revue des comptes
awk -F: '$3 == 0 {print "UID 0:", $1}' /etc/passwd             # only root expected
awk -F: '$3 >= 1000 || $7 !~ /(nologin|false)$/ {print $1, $3, $6, $7}' /etc/passwd
awk -F: '$2 !~ /^[!*]/ {print "has password:", $1}' /etc/shadow
grep -E '^(sudo|wheel|adm|docker|lxd|disk):' /etc/group
cat /etc/sudoers /etc/sudoers.d/* 2>/dev/null | grep -Ev '^\s*(#|$)'
ls -la --time-style=full-iso /etc/passwd /etc/shadow /etc/group /etc/sudoers /etc/sudoers.d/

L’appartenance à docker, lxd ou disk équivaut en pratique à root, et les attaquants s’y ajoutent parce que peu de gens le vérifient. Une règle NOPASSWD pour ALL, ou pour un éditeur, un interpréteur ou find, équivaut aussi à root. Comparez les dates de modification des fichiers d’identité à la fenêtre de l’incident.

Toutes les clés SSH autorisées du système
sshd -T 2>/dev/null | grep -Ei 'authorizedkeysfile|authorizedkeyscommand|permitrootlogin|passwordauthentication'

awk -F: '{print $1, $6}' /etc/passwd | while read -r user home; do
  for f in "$home/.ssh/authorized_keys" "$home/.ssh/authorized_keys2"; do
    [ -f "$f" ] || continue
    echo "===== $user $f"
    ls -la --time-style=full-iso "$f"
    cat "$f"
  done
done > "$OUT/authorized-keys.txt" 2>&1

lsattr /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys 2>/dev/null   # immutable flag set?

Recherchez les clés impossibles à relier à une personne ou à un système, les commentaires hors de votre convention (ou absents), les options forced command ou from que vous n’avez pas définies, les clés sur des comptes de service comme www-data ou postgres, et les fichiers authorized_keys rendus immuables avec chattr pour faire échouer le nettoyage. Vérifiez aussi que sshd ne lit que les fichiers de clés attendus ; un attaquant peut ajouter un chemin AuthorizedKeysFile dans sshd_config.

Historique des connexions et journaux d’authentification
last -Faiwx > "$OUT/last.txt"                   # wtmp: logins, reboots, shutdowns
lastb -Faiw > "$OUT/lastb.txt" 2>/dev/null       # btmp: failed logins
lastlog 2>/dev/null > "$OUT/lastlog.txt"
# Newer distributions record logins in wtmpdb and lastlog2 instead
wtmpdb last 2>/dev/null >> "$OUT/last.txt"; lastlog2 2>/dev/null >> "$OUT/lastlog.txt"

# Debian and Ubuntu: /var/log/auth.log    RHEL: /var/log/secure
zgrep -h 'Accepted' /var/log/auth.log* /var/log/secure* 2>/dev/null | sort > "$OUT/ssh-accepted.txt"
zgrep -h 'Failed password' /var/log/auth.log* /var/log/secure* 2>/dev/null |
  grep -oE 'from [0-9a-f.:]+' | sort | uniq -c | sort -rn | head -30
journalctl _COMM=sshd --since "2026-10-20" -o short-iso-precise --utc | grep -E 'Accepted|session opened'
journalctl _COMM=sudo --since "2026-10-20" -o short-iso-precise --utc

Avec LogLevel VERBOSE, chaque connexion par clé acceptée est journalisée avec l’empreinte de la clé, que vous pouvez comparer aux entrées d’authorized_keys avec ssh-keygen -lf. Une connexion réussie depuis une adresse présente aussi dans la liste des échecs de mot de passe est la signature d’une force brute réussie. Vérifiez la falsification de wtmp avec utmpdump : enregistrements mis à zéro, trous dans les entrées de redémarrage ou fichier bien plus petit que ne le suggère la rotation des journaux.

Journaux et historique

Où se trouvent les preuves sous Linux
SourceDebian et UbuntuFamille RHELCe qu’on y voit
Authentification/var/log/auth.log/var/log/secureSSH, sudo, su, PAM, changements d’utilisateurs
Système/var/log/syslog/var/log/messagesServices, cron, noyau, la plupart des démons
Journal systemd/var/log/journal/ (s’il est persistant)/var/log/journal/ (s’il est persistant)Tout ce qui précède, structuré, avec les métadonnées des processus
Audit/var/log/audit/audit.log/var/log/audit/audit.logAppels système, exécutions, surveillance de fichiers, connexions si configuré
Changements de paquets/var/log/dpkg.log, /var/log/apt/history.log/var/log/dnf.log, dnf historyLogiciels installés ou supprimés par l’attaquant
Cronsyslog, journal/var/log/cronExécutions des tâches planifiées
Serveurs web/var/log/nginx/, /var/log/apache2//var/log/nginx/, /var/log/httpd/Tentatives d’exploitation, usage de web shells
Connexions/var/log/wtmp, btmp, lastlog/var/log/wtmp, btmp, lastlogSessions, échecs, dernière connexion par utilisateur
Historique du shell~/.bash_history, ~/.zsh_history~/.bash_history, ~/.zsh_historyCommandes saisies, si non désactivé
Conteneurs/var/lib/docker/containers/*/*-json.log/var/lib/docker/containers/*/*-json.logstdout et stderr des conteneurs
Agents cloud/var/log/cloud-init.log, agents du fournisseur/var/log/cloud-init.log, agents du fournisseurScripts user data et changements de métadonnées
Requêtes du journal et de l’audit
journalctl --list-boots
journalctl --since "2026-10-20 00:00" --until "2026-10-28 00:00" -o short-iso-precise --utc > "$OUT/journal.txt"
journalctl -k --since "2026-10-20" --utc | grep -Ei 'module|taint|segfault|denied'
journalctl --verify 2>&1 | tail                     # detects corrupted or altered journal files

ausearch -i -m USER_LOGIN,USER_AUTH,USER_CMD --start 10/20/2026 00:00:00 > "$OUT/audit-auth.txt"
ausearch -i -m EXECVE --start recent | head -200
aureport -x --summary                               # most executed programs
aureport --login --failed --summary
# ausearch dates follow the system locale; use "recent", "today" or "this-week" to avoid surprises
Historiques de shell de chaque utilisateur
for h in /root /home/*; do
  for f in "$h"/.*history; do
    [ -f "$f" ] || continue
    echo "===== $f"
    ls -la --time-style=full-iso "$f"
    cat "$f"
  done
done > "$OUT/histories.txt" 2>&1

ls -la /root/.bash_history /home/*/.bash_history 2>/dev/null | grep -E ' -> |^l| 0 '   # symlinked to /dev/null or empty

Bash n’écrit l’historique qu’à la fermeture normale d’un shell, sans horodatage sauf si HISTTIMEFORMAT est défini, et il se désactive très facilement. Voyez-le comme un bonus plutôt qu’un enregistrement fiable. Les signes de falsification sont un fichier d’historique lié à /dev/null, HISTFILE ou HISTSIZE modifiés dans un script de profil, un historique qui s’arrête brusquement et des commandes comme history -c, unset HISTFILE ou shred dans ce qu’il reste.

Journaux du serveur web : exploitation et web shells
L=/var/log/nginx/access.log*
zcat -f $L | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
zcat -f $L | grep -E '"POST [^"]+\.(php|jsp|aspx?|cgi)' | awk '{print $7}' | sort | uniq -c | sort -rn | head -30
zcat -f $L | grep -Ei 'union.+select|\.\./\.\.|/etc/passwd|cmd=|exec=|base64_decode|wget%20|curl%20|\$\{jndi:' | head -50
zcat -f $L | awk '$9 == 200 && $7 ~ /\.(php|jsp)/' | grep -Ev 'index\.php|wp-cron' | awk '{print $7}' | sort | uniq -c | sort -n | head

Un web shell apparaît généralement comme un script rarement demandé, souvent dans un dossier d’envoi ou de cache, recevant des requêtes POST d’un petit nombre d’adresses et renvoyant 200. Dès que vous trouvez une requête, partez de l’adresse du client et de l’user agent pour trouver tout ce qu’il a fait, puis du nom du fichier pour savoir quand il a été créé.

Rechercher la persistance

Les mécanismes de persistance Linux sont de simples fichiers à des endroits connus, faciles à rechercher une fois la liste connue. Le tableau résume les emplacements courants ; les commandes ci-dessous les collectent. Examinez d’abord les dates de modification dans la fenêtre de l’incident et tout ce qui télécharge, décode ou lance quelque chose depuis un répertoire temporaire.

Emplacements courants de persistance sous Linux
MécanismeOù regarderSignaux d’alerte
Cron et at/etc/crontab, /etc/cron.*, /var/spool/cron/, /etc/anacrontab, atqcurl ou wget envoyés vers sh, Base64, tâches à chaque minute, tâches de comptes de service
Unités et timers systemd/etc/systemd/system/, /usr/lib/systemd/system/, ~/.config/systemd/user/, générateursNouvelles unités aux noms génériques, ExecStart dans /tmp ou un dossier personnel, Restart=always sur des binaires inconnus
SSHauthorized_keys, sshd_config, ~/.ssh/rc, /etc/ssh/sshrcClés inconnues, chemins AuthorizedKeysFile supplémentaires, scripts rc
Fichiers de démarrage du shell/etc/profile, /etc/profile.d/, /etc/bash.bashrc, ~/.bashrc, ~/.profile, ~/.bash_logoutCommandes en arrière-plan, alias remplaçant sudo ou ssh, LD_PRELOAD exporté
Éditeur de liens dynamique/etc/ld.so.preload, /etc/ld.so.conf.d/Toute entrée preload ; bibliothèques à des chemins inhabituels
PAM/etc/pam.d/, /lib/x86_64-linux-gnu/security/, /usr/lib64/security/pam_unix.so modifié, modules inconnus, lignes pam_exec
Modules du noyau/etc/modules, /etc/modules-load.d/, lsmod, /sys/moduleModules non signés ou hors arbre, noyau marqué tainted, modules absents de lsmod
Scripts d’init et rc.local/etc/rc.local, /etc/init.d/Scripts récemment ajoutés sur des hôtes systemd
MOTD et hooks de connexion/etc/update-motd.d/Scripts exécutés en root à chaque connexion SSH
Règles udev/etc/udev/rules.d/Entrées RUN+= lançant des scripts
Hooks du gestionnaire de paquets/etc/apt/apt.conf.d/, /etc/yum/pluginconf.d/, /etc/dnf/plugins/Commandes Pre-Invoke ou Post-Invoke
Web shellsRacines de documents, dossiers d’envoi, de plugins et de cacheScripts récemment modifiés avec eval, assert, system ou des charges Base64
setuid et capabilitiesPartout sur le disqueNouveaux binaires setuid, cap_setuid sur des interpréteurs
Cron, at et systemd
{
  ls -la --time-style=full-iso /etc/cron* /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
  cat /etc/crontab /etc/cron.d/* /etc/anacrontab 2>/dev/null
  for u in $(cut -d: -f1 /etc/passwd); do crontab -l -u "$u" 2>/dev/null | sed "s/^/[$u] /"; done
  atq 2>/dev/null; ls -la /var/spool/cron/atjobs /var/spool/at 2>/dev/null
} > "$OUT/cron.txt" 2>&1

{
  systemctl list-unit-files --state=enabled --no-pager
  systemctl list-timers --all --no-pager
  find /etc/systemd /usr/lib/systemd /lib/systemd /run/systemd /home/*/.config/systemd /root/.config/systemd \
    -type f -newermt "2026-10-01" -printf '%TY-%Tm-%Td %TT %p\n' 2>/dev/null | sort
  ls -la /etc/systemd/system-generators /usr/lib/systemd/system-generators 2>/dev/null
} > "$OUT/systemd.txt" 2>&1

systemctl cat suspicious.service
Fichiers de démarrage, éditeur de liens, PAM, MOTD et hooks
ls -la --time-style=full-iso /etc/profile /etc/profile.d/ /etc/bash.bashrc /etc/environment \
  /root/.bashrc /root/.profile /home/*/.bashrc /home/*/.profile /home/*/.bash_logout 2>/dev/null
grep -HnE 'curl|wget|nc |ncat|/dev/tcp|base64|python.*-c|nohup|setsid|LD_PRELOAD|alias (sudo|ssh)' \
  /etc/profile /etc/profile.d/* /etc/bash.bashrc /root/.bashrc /home/*/.bashrc /home/*/.profile 2>/dev/null

cat /etc/ld.so.preload 2>/dev/null; ls -la /etc/ld.so.conf.d/
grep -rn 'pam_exec\|pam_script' /etc/pam.d/ 2>/dev/null
ls -la --time-style=full-iso /lib/x86_64-linux-gnu/security/ /usr/lib64/security/ 2>/dev/null | sort -k6,7
ls -la --time-style=full-iso /etc/update-motd.d/ /etc/rc.local /etc/init.d/ 2>/dev/null
grep -rn 'RUN+=' /etc/udev/rules.d/ 2>/dev/null
grep -rn 'Invoke' /etc/apt/apt.conf.d/ 2>/dev/null

La vérification des paquets est le moyen le plus rapide de repérer des fichiers système modifiés, comme un pam_unix.so, un sshd ou un ps piégé. Elle compare les fichiers installés aux sommes de contrôle enregistrées à l’installation. Un attaquant root peut aussi falsifier la base de paquets : voyez un résultat propre comme un indice plutôt qu’une preuve, et vérifiez les binaires critiques sur un système sain.

Vérifier les fichiers système avec le gestionnaire de paquets
dpkg --verify 2>/dev/null | grep -v ' c /etc/' > "$OUT/dpkg-verify.txt"   # Debian and Ubuntu
debsums -c 2>/dev/null >> "$OUT/dpkg-verify.txt"
rpm -Va 2>/dev/null | grep -E '^..5' > "$OUT/rpm-verify.txt"                # RHEL: changed digest

# Who owns a suspicious file? Nothing means it did not come from a package
dpkg -S /usr/lib/x86_64-linux-gnu/security/pam_unix.so
rpm -qf /usr/lib64/security/pam_unix.so
Modules du noyau, eBPF et setuid
lsmod > "$OUT/lsmod.txt"
cat /proc/sys/kernel/tainted                     # 0 is clean; non-zero needs explaining
diff <(lsmod | awk 'NR>1 {print $1}' | sort) <(ls /sys/module | sort) | grep '^>'   # names in /sys only
for m in $(lsmod | awk 'NR>1 {print $1}'); do modinfo -F filename "$m"; done | grep -v '^/lib/modules'
dmesg -T | grep -Ei 'taint|module verification|loading out-of-tree'

bpftool prog list 2>/dev/null > "$OUT/bpf-progs.txt"     # eBPF programs; compare with expected agents
bpftool link list 2>/dev/null >> "$OUT/bpf-progs.txt"

find / -xdev \( -perm -4000 -o -perm -2000 \) -type f -newermt "2026-10-01" -ls 2>/dev/null
getcap -r / 2>/dev/null

De nombreuses entrées de /sys/module sont des composants intégrés au noyau et non des modules chargeables : la comparaison ci-dessus demande donc du discernement, mais un module chargé (avec un fichier initstate) absent de lsmod a été caché volontairement. Les rootkits soigneux disparaissent des deux vues, d’où l’importance de l’analyse mémoire avec linux.check_modules. Les programmes eBPF sont normaux pour les agents EDR, Cilium, systemd et les outils d’observabilité ; un programme eBPF que vous ne pouvez attribuer à aucun logiciel installé mérite une escalade, car les rootkits eBPF peuvent cacher des processus, des fichiers et du trafic réseau.

Recherche de web shells dans les racines de documents
find /var/www /srv /usr/share/nginx /opt -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.jsp' -o -name '*.jspx' \) \
  -newermt "2026-10-01" -printf '%TY-%Tm-%Td %TT %u %p\n' 2>/dev/null | sort

grep -rlE 'eval\s*\(\s*(base64_decode|gzinflate|str_rot13|\$_(POST|GET|REQUEST|COOKIE))|assert\s*\(\s*\$_|system\s*\(\s*\$_|passthru|shell_exec|Runtime\.getRuntime\(\)\.exec' \
  /var/www 2>/dev/null

# PHP files in upload folders should not exist at all
find /var/www -path '*upload*' -name '*.php*' 2>/dev/null

Triage du système de fichiers

Les systèmes de fichiers Linux gardent jusqu’à quatre dates par fichier : modification (mtime), accès (atime, souvent peu fiable à cause de relatime), changement d’inode (ctime) et, sur ext4, XFS et Btrfs, création (birth time). Les attaquants réinitialisent souvent mtime avec touch, mais ctime ne peut pas être défini depuis l’espace utilisateur sans changer l’horloge système : un fichier dont le ctime est bien plus récent que le mtime a donc été modifié récemment.

Fichiers modifiés pendant la fenêtre de l’incident
# Content modified in the window
find / -xdev -type f -newermt "2026-10-20" ! -newermt "2026-10-28" \
  -printf '%TY-%Tm-%Td %TT  %u:%g  %m  %s  %p\n' 2>/dev/null | sort > "$OUT/mtime-window.txt"

# Inode changed in the window, which catches timestomped files
find / -xdev -type f -newerct "2026-10-20" ! -newerct "2026-10-28" \
  -printf '%CY-%Cm-%Cd %CT  %p\n' 2>/dev/null | sort > "$OUT/ctime-window.txt"

# All four timestamps for one file, including birth time on current coreutils
stat --format='%n  birth=%w  modify=%y  change=%z  access=%x' /usr/local/bin/kswapd1
Répertoires temporaires, fichiers cachés et exécutables à des endroits inhabituels
ls -laR --time-style=full-iso /tmp /var/tmp /dev/shm /run/user 2>/dev/null > "$OUT/tmp-listing.txt"

# ELF binaries where software should not live
find /tmp /var/tmp /dev/shm /run /var/www /home -type f -size -20M -exec file {} + 2>/dev/null | grep -i 'ELF'

# Hidden names designed to be overlooked: "...", ". ", names starting with a dot in system folders
find / -xdev \( -name '...' -o -name '.. ' -o -name '. ' -o -name ' ' \) 2>/dev/null
find /usr /etc /var/lib /lib -xdev -name '.*' -type f 2>/dev/null | grep -v -E '\.(placeholder|keep|gitignore|build-id)'

# Immutable or append-only flags used to block cleanup
lsattr -R /etc /root /home /var/spool/cron 2>/dev/null | grep -E '^[^ ]*[ia][^ ]* '
Calculer l’empreinte et analyser les fichiers suspects
sha256sum /tmp/.x /dev/shm/kworker /usr/local/bin/kswapd1 | tee -a "$OUT/hashes.txt"
file /tmp/.x; strings -n 8 /tmp/.x | head -100      # mining pool URLs, C2 addresses, wallet strings

yara -r -s -w /mnt/ir/rules/linux.yar /tmp /var/tmp /dev/shm /home /var/www /usr/local/bin 2>/dev/null
yara -s /mnt/ir/rules/linux.yar 4127                 # scan a running process by PID
/mnt/ir/thor-lite/thor-lite-linux-64 -p /tmp

Copiez les binaires suspects dans votre dossier de preuves avant toute autre action, et soumettez aux services publics les empreintes plutôt que les fichiers : envoyer un échantillon à VirusTotal le rend accessible à tous les abonnés, y compris l’attaquant, qui apprend ainsi que vous avez trouvé son outil.

Collecte avec UAC, Velociraptor et images disque

UAC réunit tout ce qui précède en une seule commande reproductible. Il s’exécute depuis un dossier du support d’outils, utilise les outils présents sur la cible, collecte les sorties de réponse à chaud, les journaux, la configuration, les historiques et un fichier body pour la chronologie, et produit une seule archive avec une empreinte et un journal d’acquisition.

UAC (Unix-like Artifacts Collector)
cd /mnt/ir/uac
sudo ./uac -p ir_triage /mnt/evidence                                   # standard triage profile
sudo ./uac -a ./artifacts/memory_dump/avml.yaml -p ir_triage /mnt/evidence   # plus memory
sudo ./uac -p full /mnt/evidence                                        # broader, slower

# Stream the output when there is no local disk to write to
sudo ./uac -p ir_triage --sftp analyst@10.0.0.50:/cases/host01 /tmp

Pour un parc, Velociraptor offre la même capacité à distance, avec des chasses qui exécutent un artefact sur tous les serveurs Linux à la fois. Parmi les artefacts utiles figurent Linux.Sys.Pslist, Linux.Ssh.AuthorizedKeys, Linux.Sys.Crontab, Linux.Sys.Users et Linux.Search.FileFinder pour la recherche d’indicateurs, ainsi que les artefacts d’analyse YARA des fichiers et de la mémoire des processus.

Collecte manuelle diffusée vers l’hôte d’analyse
sudo tar -czf - --ignore-failed-read \
  /etc /var/log /var/spool/cron /root/.ssh /root/.bash_history \
  /home/*/.ssh /home/*/.bash_history /tmp /var/tmp /dev/shm 2>/dev/null |
  ssh analyst@10.0.0.50 "tee /cases/host01/host01-triage.tgz | sha256sum > /cases/host01/host01-triage.tgz.sha256"

Faites une image complète du disque si vous avez besoin de récupérer des fichiers supprimés, de l’espace résiduel ou de preuves de niveau judiciaire. Sur une machine physique, créez l’image avec dc3dd ou ewfacquire sur un stockage externe. Sur une VM cloud, faites un instantané du volume via le fournisseur, créez un nouveau volume à partir de cet instantané et attachez-le à une instance forensique dédiée dans un réseau isolé. Montez-le en lecture seule et avec noload sur ext4, ce qui empêche le noyau de rejouer le journal et de modifier la preuve.

Image disque et instantanés cloud
# Physical or local virtual disk
sudo dc3dd if=/dev/sda hof=/mnt/evidence/host01-sda.dd hash=sha256 log=/mnt/evidence/host01-sda.log

# AWS
aws ec2 create-snapshot --volume-id vol-0abc123 --description "IR-2026-031 host01 root" \
  --tag-specifications 'ResourceType=snapshot,Tags=[{Key=case,Value=IR-2026-031}]'

# On the forensic instance, with the copied volume attached as /dev/nvme1n1
sudo mount -o ro,noload,noexec,nodev /dev/nvme1n1p1 /mnt/case
sudo fls -r -m / /dev/nvme1n1p1 > host01.body
sudo mactime -b host01.body -d -z UTC 2026-10-20..2026-10-28 > host01-timeline.csv

Conteneurs et Kubernetes

Dans les environnements de conteneurs, la charge compromise peut disparaître en quelques minutes lorsque l’orchestrateur remplace les pods défaillants. Capturez d’abord, puis contenez : mettez le conteneur en pause au lieu de l’arrêter, exportez son système de fichiers et notez sa configuration, car les preuves disparaissent avec le conteneur.

Triage de conteneurs
docker ps -a --no-trunc
docker inspect web-1 > "$OUT/web-1-inspect.json"     # privileged? host mounts? capabilities?
docker top web-1
docker diff web-1 > "$OUT/web-1-diff.txt"             # files added (A), changed (C), deleted (D)
docker logs --timestamps web-1 > "$OUT/web-1.log" 2>&1
docker pause web-1                                     # freeze processes, keep memory and state
docker export web-1 -o "$OUT/web-1-fs.tar"

# Kubernetes
kubectl get pods -A -o wide
kubectl get events -A --sort-by=.lastTimestamp | tail -50
kubectl describe pod web-7d9f -n prod
kubectl logs web-7d9f -n prod --all-containers --timestamps --previous
kubectl get clusterrolebindings -o wide | grep -v system:   # unexpected cluster-admin grants

docker diff est l’une des commandes de triage les plus rapides : une image de conteneur doit être immuable, donc chaque fichier ajouté ou modifié dans un conteneur actif est soit un état normal de l’application, soit une activité de l’attaquant. Vérifiez avec docker inspect le mode privilégié, les espaces de noms PID ou réseau de l’hôte et les montages de /, /var/run/docker.sock ou /etc, qui transforment chacun une compromission de conteneur en compromission de l’hôte. Dans le cloud, examinez aussi les journaux d’audit du fournisseur (CloudTrail, Azure Activity Log, Google Cloud Audit Logs) pour repérer l’usage des identifiants du rôle d’instance depuis l’extérieur.

Contrôle des rootkits

Les rootkits Linux existent sous trois formes : en espace utilisateur, remplaçant des binaires ou préchargeant des bibliothèques ; sous forme de modules du noyau, comme Diamorphine et Reptile ; et, plus récemment, fondés sur eBPF. Chacun cache des processus, des fichiers et des connexions aux outils ordinaires : la détection repose donc sur la comparaison de deux vues d’une même chose et sur leurs désaccords.

  • Processus : la sortie de ps, un parcours brut de /proc et les vues mémoire de Volatility de type linux.pslist et linux.psscan.
  • Ports : la sortie de ss, /proc/net/tcp et unhide-tcp, qui teste chaque port en essayant de s’y lier.
  • Modules : lsmod, /sys/module et linux.check_modules en mémoire, ainsi que l’indicateur taint du noyau.
  • Appels système : linux.check_syscall et linux.check_idt en mémoire révèlent les points d’entrée du noyau détournés.
  • Fichiers : une liste de répertoire sur l’hôte actif et le même répertoire sur un instantané de disque monté, que le noyau actif ne contrôle pas.
Scanners de rootkits (faux positifs à prévoir ; comparez à une référence saine)
/mnt/ir/bin/chkrootkit -q
rkhunter --check --sk --rwo --nocolors
unhide proc; unhide sys; unhide brute
unhide-tcp

Schémas d’intrusion Linux courants

Mineur de cryptomonnaie sur un service exposé

La compromission Linux la plus courante. L’entrée est généralement un mot de passe SSH faible, un Redis, une API Docker ou un tableau de bord Kubernetes exposés, ou une application web non corrigée. Attendez-vous à un binaire comme XMRig renommé en thread du noyau dans /tmp, /dev/shm ou /var/tmp, à une tâche cron ou une unité systemd qui le retélécharge toutes les quelques minutes, à une nouvelle clé autorisée, à des mineurs concurrents arrêtés par un script et à des connexions vers des ports de pool. Récupérez le script de dépôt : il documente tout ce que l’attaquant a changé. Supprimez toute la persistance avant d’arrêter le processus, sinon il reviendra.

Web shell via une application vulnérable

Recherchez un script dans la racine de documents ou le dossier d’envoi, des requêtes POST vers celui-ci dans le journal d’accès et des processus enfants de l’utilisateur du serveur web. Ensuite, l’attaquant lit généralement les fichiers de configuration pour obtenir les identifiants de bases de données et de cloud : supposez donc que tout secret lisible par cet utilisateur est compromis et changez-le. Vérifiez s’il est passé root via une vulnérabilité locale du noyau ou de sudo, en recherchant des fichiers appartenant à root créés peu après l’apparition du web shell.

Identifiants ou clés SSH volés

Une connexion acceptée depuis une adresse inconnue à une heure inhabituelle, avec un nom d’utilisateur connu et une empreinte correspondant à la clé d’un vrai employé, indique le vol d’une clé sur un poste ou un système CI plutôt qu’une force brute. Examinez la source de la clé autant que le serveur. Vérifiez si l’attaquant a utilisé le transfert d’agent SSH ou trouvé des clés privées sur ce serveur pour aller plus loin ; recherchez la même adresse source dans les historiques de shell et les journaux d’authentification des hôtes voisins.

Vol d’identifiants cloud

Sur les VM cloud, la cible la plus précieuse est souvent le rôle d’instance. Recherchez les requêtes vers le service de métadonnées à 169.254.169.254 dans les journaux web (falsification de requête côté serveur), les fichiers d’identifiants dans ~/.aws, ~/.azure, ~/.config/gcloud et les variables d’environnement des processus applicatifs, puis vérifiez dans le journal d’audit du fournisseur si ces identifiants ont été utilisés depuis des adresses extérieures à votre environnement.

Confinement et reprise

  1. Isolez d’abord au niveau du réseau : remplacez les règles du groupe de sécurité ou du pare-feu du fournisseur par une autorisation pour votre seul hôte d’analyse. Cela fonctionne même si l’attaquant est root sur la VM et peut modifier le pare-feu de l’hôte.
  2. Figez plutôt que d’arrêter. kill -STOP sur un processus malveillant le stoppe en gardant sa mémoire pour la collecte ; mettre un conteneur en pause fait de même. N’arrêtez qu’après avoir collecté la mémoire et les binaires.
  3. Faites un instantané des disques avant l’éradication, pour que les preuves survivent à la reconstruction.
  4. Changez tout ce que l’hôte pouvait atteindre : clés SSH d’hôte et d’utilisateur, jetons d’API, mots de passe de bases de données, clés d’accès cloud et sessions de rôle d’instance, clés privées TLS et secrets dans les variables d’environnement ou les fichiers de configuration.
  5. Reconstruisez à partir d’une image saine ou de la gestion de configuration plutôt que de nettoyer sur place. Une compromission root d’un hôte Linux ne peut pas être nettoyée de façon fiable.
  6. Corrigez le point d’entrée avant la remise en service : corrigez l’application, fermez le service exposé, imposez SSH par clés uniquement et la MFA sur la console cloud.
  7. Balayez le parc avec les indicateurs trouvés (empreintes, noms de fichiers, clés, adresses, entrées cron) et surveillez de près pendant au moins 30 jours.

Liste de contrôle rapide

  1. Faites un instantané des disques cloud ; montez une boîte à outils de confiance en lecture seule ; démarrez un shell enregistré en UTC avec l’historique désactivé.
  2. Vérifiez /etc/ld.so.preload et LD_PRELOAD avant de vous fier à un binaire dynamique.
  3. Notez l’identité de l’hôte, la durée de fonctionnement, les montages, les interfaces, les routes et les utilisateurs connectés.
  4. Capturez la mémoire avec AVML ou LiME et calculez son empreinte.
  5. Notez les processus depuis ps et /proc ; récupérez les binaires supprimés depuis /proc/PID/exe.
  6. Notez les sockets, les règles de pare-feu et l’ARP ; comparez ss à /proc/net et à unhide-tcp.
  7. Examinez les comptes, sudoers, les groupes à privilèges et chaque fichier authorized_keys.
  8. Récupérez les journaux d’authentification, wtmp, btmp, le journal systemd, le journal d’audit et les historiques de shell.
  9. Recherchez la persistance : cron, systemd, profils de shell, éditeur de liens, PAM, modules du noyau, eBPF, MOTD, udev, hooks de paquets, web shells, setuid.
  10. Vérifiez les paquets, trouvez les fichiers modifiés dans la fenêtre par mtime et ctime, analysez avec YARA.
  11. Lancez UAC ou Velociraptor pour une collecte complète et hachée.
  12. Pour les conteneurs : inspect, diff, logs, pause et export avant que l’orchestrateur ne les remplace.
  13. Isolez au niveau du fournisseur, figez les processus, changez chaque secret accessible, reconstruisez, balayez et surveillez.

Conclusion

Sur un hôte Linux compromis, commencez par vérifier si vous pouvez vous fier aux outils du système. Capturez ensuite la mémoire et l’état en direct, examinez les comptes, les journaux et la persistance, et collectez les preuves avant de contenir ou de reconstruire. Gardez le même ordre sur les serveurs et les conteneurs, et entraînez-vous sur un système de test pour que les commandes vous soient familières le jour d’un incident.

  • Linux
  • DFIR
  • Réponse à incident
  • Triage
  • Forensique
  • UAC
  • Volatility
  • Rootkits
  • Conteneurs
  • Chasse aux menaces
  • Direction de la sécurité32 min de lecture

    Vous serez piraté. Votre rôle est de rendre cela coûteux.

    La sécurité parfaite n’existe pas et l’IA réduit le coût des attaques. Un guide pour les RSSI sur les niveaux de menace, la défense par l’IA, le dialogue avec le conseil d’administration et la façon d’augmenter le prix d’une compromission.

  • Guide de durcissement23 min de lecture

    Durcissement d’un VPS Linux : guide pratique complet

    Le durcissement pas à pas d’un serveur Ubuntu, Debian ou RHEL exposé à Internet : SSH, pare-feu, paramètres du noyau, sandbox, audit, contrôle d’intégrité, sauvegardes et vérification.

  • Réponse à incident28 min de lecture

    Triage forensique Windows pour la réponse à incident

    Un guide pratique de triage des hôtes Windows compromis : capture mémoire, réponse à chaud, recherche de persistance, journaux d’événements, artefacts d’exécution, collecte avec KAPE et Velociraptor.

Triage forensique Linux pour la réponse à incident | ABYSSS