CLI/TUI Python pour le triage forensique des systèmes Ubuntu — détecte et remédie aux mécanismes de persistance avec collecte d'artefacts, corrélation temporelle et intégration Wazuh.
Lorsque vous soupçonnez qu'un système Linux est compromis, les 30 à 40 premières minutes sont généralement consacrées à exécuter les mêmes dix commandes en séquence : vérifier les processus en cours, rechercher des tâches cron suspectes, faire un grep sur LD_PRELOAD, analyser authorized_keys à la recherche de nouvelles entrées, auditer sudoers. Chaque étape est manuelle, implique des changements de contexte et est source d'erreurs sous pression. Oubliez une seule source — par exemple /etc/sudoers.d/ au lieu de simplement /etc/sudoers, ou les crontabs utilisateur en plus de /etc/cron.d/ — et vous obtenez une image incomplète.
Les options existantes ne résolvent pas ce problème proprement. lynis est un auditeur de durcissement, pas un outil de triage — il signale les faiblesses de configuration sur un système sain et génère du bruit sur un système compromis. chkrootkit et rkhunter vérifient les signatures de rootkits connus mais sont aveugles aux techniques de persistance inédites comme les timers systemd détournés ou les entrées cron d'apparence légitime. Les requêtes SIEM génériques nécessitent une infrastructure de journalisation qui peut ne pas exister sur le système que vous examinez. Et les suites forensiques comme Volatility ciblent les images mémoire, pas un shell actif sur un hôte en fonctionnement.
Le manque, c'est un outil qui s'exécute sur le système en production immédiatement, couvre les vecteurs de persistance les plus courants, corrèle l'activité entre les sources de journaux dans une chronologie, et vous indique exactement ce qu'il faut examiner — sans nécessiter d'agent externe, de base de données ni de connexion internet.
ubuntils s'exécute en quatre étapes séquentielles :
/proc, les tables cron, les unités systemd, les clés SSH, les fichiers sudoers, les définitions d'environnement, l'intégrité des paquets (dpkg --verify), la configuration PAM/NSS et les modules du noyau chargés. Prend environ 2,5 secondes sur un système typique.--rules — sur les artefacts collectés, produisant une liste de résultats classés et notés par niveau de confiance en environ une seconde.--json).ubuntils lui-même n'effectue aucun appel réseau, et chaque fonctionnalité — règles personnalisées et corrélation incluses — s'exécute sur des artefacts collectés localement. La seule façon dont les résultats peuvent quitter l'hôte est l'intégration Wazuh : si un agent Wazuh est installé, un scan en direct écrit ses résultats dans un fichier local que l'agent transmet ensuite à son manager. Passez --no-wazuh pour désactiver cela pour une exécution.
ubuntils scan n'est pas modifié par tout ce qui suit — il reste 100 % en direct, mono-hôte, et chaque option existante fonctionne à l'identique. Deux commandes supplémentaires, collect et analyze, divisent le même pipeline de détection/chronologie en un flux de travail acquire-then-analyze adapté au mode hors ligne pour les cas où vous ne pouvez pas (ou ne voulez pas) exécuter la détection directement sur l'hôte examiné — voir Analyse hors ligne : collect et analyze ci-dessous, y compris ses réserves sur la couverture de détection.
Ubuntu 22.04+ et tout système avec PEP 668 (recommandé) :
Ubuntu 22.04+ bloque pip install à l'échelle du système. Utilisez pipx — il gère l'environnement de manière transparente pour que vous n'ayez jamais à y penser :```bash
sudo apt install pipx -y
cd ubuntils
pipx install -e .
ubuntils scan
**Systèmes plus anciens / installation manuelle :**```bash
git clone https://github.com/asmitdesai/ubuntils.git
cd ubuntils
pip install -r requirements.txt -e .
ubuntils scan
ubuntils nécessite les droits root pour un accès complet aux artefacts. Si vous exécutez ubuntils scan en tant qu'utilisateur non-root, il se réinvoquera automatiquement avec sudo en utilisant le même interpréteur Python (par chemin absolu), afin que l'environnement correct soit utilisé sans transmettre votre PATH au processus root. Chaque commande externe (ss, dpkg, systemctl, …) est résolue sur un chemin de recherche fixe, appartenant à root, jamais votre PATH. Une exécution sans root ignorera /etc/shadow, certaines entrées /proc et les fichiers cron protégés, et enregistrera des avertissements pour chacun.
Détection uniquement — TUI interactive :```bash sudo ubuntils scan
**Détection avec sortie JSON enregistrée dans un fichier :**```bash
sudo ubuntils scan --json > /tmp/triage-$(hostname)-$(date +%Y%m%d).json
Détection avec aperçu de remédiation CLI (exécution à blanc — aucune modification appliquée) :```bash sudo ubuntils scan --remediate
**Détection avec remédiation CLI appliquée :**```bash
sudo ubuntils scan --remediate --confirm
Version imprimable :```bash ubuntils version
**Collectez un bundle inviolable pour une analyse ultérieure ou hors ligne :**```bash
sudo ubuntils collect --output /path/to/bundle.tar.gz
Analyser un bundle précédemment collecté (aucun accès root requis) :```bash ubuntils analyze /path/to/bundle.tar.gz --json
**Analyser une image forensique montée ou une arborescence de système de fichiers extraite au lieu d'un bundle :**```bash
ubuntils analyze --root /mnt/forensic-image --json
Voir Analyse hors ligne : collecter et analyser pour le format du bundle et — surtout — ce que l'analyse hors ligne ne peut pas détecter par rapport à un scan en direct.
ubuntils scan [OPTIONS] --json Output JSON to stdout instead of launching the TUI --output FILE Write the JSON report to FILE (implies --json) --remediate Run the remediation engine after detection --confirm Required with --remediate to actually apply changes (else dry-run) --min-confidence N Only auto-remediate findings with confidence >= N (default 40) --no-wazuh Never forward findings to a local Wazuh agent --config FILE YAML allowlist of findings to suppress (see below) --baseline FILE YAML baseline of environment-specific known-good fingerprints to suppress (see below) --since TIME Limit the timeline to events since TIME (e.g. '24h', '7d', '2026-05-20') --rules FILE YAML file of custom detection rules to add (see below) --verbose Verbose structlog output
ubuntils collect [OPTIONS] --output FILE Bundle path to write (default ./ubuntils-bundle-.tar.gz) --verbose Verbose structlog output
ubuntils analyze (BUNDLE | --root PATH) [OPTIONS] --root PATH Analyze a mounted image / artifact tree instead of a bundle --json Output JSON instead of launching the TUI --output FILE Write the JSON report to FILE (implies --json) --config FILE YAML allowlist of findings to suppress (same format as scan) --baseline FILE YAML baseline of environment-specific known-good fingerprints to suppress (same format as scan) --rules FILE YAML file of custom detection rules to add (same format as scan) --since TIME Limit the timeline to events since TIME (e.g. '24h', '7d', '2026-05-20') --verbose Verbose structlog output
ubuntils version Print version string and exit
### Liste d'autorisation des faux positifs (`--config`)
Un hôte fraîchement provisionné ou géré par CI génère du bruit attendu — clés de déploiement, crontabs de provisionnement, init shell intégré. Plutôt que d'apprendre aux intervenants à le filtrer mentalement, supprimez-le explicitement avec une liste d'autorisation YAML :```yaml
# allowlist.yaml
allowlist:
rules:
- SHELL_RC_MODIFICATION # suppress this rule entirely
paths:
- /home/ci/.ssh/authorized_keys # suppress any finding on this exact path
I'm ready to translate the Kitploit tool content from English to French. Please provide chunk 25 of 90.```bash sudo ubuntils scan --json --config allowlist.yaml
La suppression est toujours explicite — par identifiant de règle et/ou chemin d'artefact exact. Il n'existe pas d'interrupteur global « tout ignorer ». Un exemple se trouve dans [`examples/allowlist.yaml`](https://github.com/asmitdesai/ubuntils/blob/main/examples/allowlist.yaml).
### Établissement d'une base de référence de confiance (`--baseline`)
`--config` supprime une règle ou un chemin *partout, pour quiconque exécute ubuntils sur cette base de code*. `--baseline` est plus restreint et propre à un environnement : il indique que « dans *cet* environnement, cet artefact précis — cette clé SSH, ce fichier RC — est connu comme sain », sans désactiver la règle ou le chemin pour tous les autres hôtes que vous analysez avec le même outil. Il est conservé dans un fichier distinct de `--config` pour la même raison que `--rules` est séparé : la suppression et l'autorisation à l'échelle d'une règle sont des préoccupations différentes qui ne devraient pas cohabiter dans un même fichier.```yaml
# baseline.yaml
baseline:
- rule_id: SSH_UNAUTHORIZED_KEY
fingerprint: ci@ci-runner # substring match against the finding's raw_value
- rule_id: SHELL_RC_MODIFICATION
fingerprint: /home/deploy/.bashrc # exact match against the finding's artifact_path
I don't have the source content to translate. You mentioned "INPUT:" but no actual Markdown text was included after it.
Please paste the chunk 29/90 content you'd like translated from English to French, and I'll return only the translated Markdown with all structure, code, paths, URLs, and identifiers preserved exactly as-is.```bash sudo ubuntils scan --json --baseline baseline.yaml
Une entrée de référence correspond par `rule_id` plus un `fingerprint` testé comme sous-chaîne de la `raw_value` de la trouvaille ou une correspondance exacte avec son `artifact_path`. Une correspondance retire entièrement la trouvaille du rapport — la suppression n'est jamais silencieuse, cependant : le nombre de trouvailles qu'une référence a retirées est toujours visible dans `scan_metadata.suppressed_by_baseline`. La suppression par liste d'autorisation (`--config`) s'applique toujours en plus de la suppression par référence. Fonctionne de manière identique sur `scan` et `analyze`. Un exemple se trouve à [`examples/baseline.yaml`](https://github.com/asmitdesai/ubuntils/blob/main/examples/baseline.yaml).
### Règles de détection personnalisées (`--rules`)
`--config` *supprime* des trouvailles ; `--rules` en *ajoute*. Ce sont délibérément des fichiers séparés car ce sont des préoccupations opposées.
Un fichier de règles est uniquement basé sur la correspondance de motifs — pas d'expressions, pas de conditionnels, et pas d'exécution de code, donc le chargement d'un tel fichier ne peut jamais exécuter de logique fournie par un attaquant. Chaque règle nomme une `source` d'artefact, un mode `match`, et un `pattern` :```yaml
# custom_rules.yaml
rules:
- id: CUSTOM_KNOWN_MINER
severity: HIGH # HIGH | MEDIUM | LOW
title: Known cryptominer in process cmdline
description: A running process command line matches a known miner.
source: process # cron | environment | ssh | process | network
match: substring # regex | substring | glob
pattern: xmrig
Cloner le dépôt
git clone https://github.com/yourusername/kitploit-tool.git
cd kitploit-tool
Créer un environnement virtuel
python3 -m venv venv
source venv/bin/activate # Sur Windows : venv\Scripts\activate
Installer les dépendances
pip install -r requirements.txt
Configurer les variables d'environnement
cp .env.example .env
# Modifiez .env avec vos paramètres
Lancer l'outil
python main.py --help
# Afficher l'aide
python main.py --help
# Lancer un scan
python main.py scan --target example.com
# Générer un rapport
python main.py report --format json --output report.json
| Option | Description | Valeur par défaut |
|---|---|---|
--verbose | Activer la sortie détaillée | false |
--timeout | Délai d'expiration en secondes | 30 |
--threads | Nombre de threads | 10 |
--output | Fichier de sortie | stdout |
Scanner un réseau :
python main.py scan --target 192.168.1.0/24 --threads 20
Exporter les résultats :
python main.py scan --target example.com --output results.json
Le fichier de configuration se trouve dans config/settings.yaml :
scanner:
timeout: 30
threads: 10
retries: 3
output:
format: json
verbose: false
logging:
level: INFO
file: logs/app.log
Erreur : Module introuvable
pip install -r requirements.txt
Erreur de permission
chmod +x main.py
Conflit de port
Modifiez le port dans config/settings.yaml ou utilisez l'option --port.```bash
sudo ubuntils scan --json --rules custom_rules.yaml
| `source` | Correspond à |
|---|---|
| `cron` | La commande cron (chemin : le fichier crontab) |
| `environment` | La ligne brute d'environnement/init de shell (chemin : le fichier de définition) |
| `ssh` | Le type de clé, les données de clé et le commentaire (chemin : le fichier `authorized_keys`) |
| `process` | La ligne de commande du processus (chemin : le chemin de l'exe) |
| `network` | La description de la connexion (chemin : `remote_addr:remote_port`) |
`regex` et `substring` correspondent à la colonne de texte ; `glob` correspond à la colonne de chemin — ainsi un glob `network` tel que `203.0.113.*:*` cible le point de terminaison distant. Les résultats des règles personnalisées sont uniquement signalés (jamais remédiés automatiquement) et restent soumis à la suppression par `--config`. Un exemple se trouve dans [`examples/custom_rules.yaml`](https://github.com/asmitdesai/ubuntils/blob/main/examples/custom_rules.yaml).
### Intégrité du rapport
Chaque rapport `--json` porte un champ `report_sha256` — un SHA-256 calculé sur le contenu canonique du rapport. Cela rend un artefact de triage collecté infalsifiable et vous permet de référencer une analyse spécifique par son empreinte dans un dossier. Le rapport enregistre également `tool_version`, `hostname` et un horodatage UTC `generated_at` sous `scan_metadata`. Pour `scan`, `hostname`/`ubuntu_version` décrivent la machine sur laquelle `ubuntils` s'exécute ; pour `analyze --root`, ils sont lus depuis les propres fichiers `/etc/hostname` et `/etc/os-release` de l'image. Pour `analyze BUNDLE`, ils proviennent à la place du manifeste propre du bundle — l'hôte qui a été *collecté*, et non l'hôte exécutant `analyze` — ainsi que de `collection_run_id` et des `collected_at_utc_start`/`collected_at_utc_end` de la collecte, de sorte que l'enregistrement de chaîne de custody du rapport suive les preuves plutôt que le poste de travail de l'analyste.
**Vérification d'un rapport.** Le rapport est émis sous forme canonique (clés triées, indentation de 2 espaces), et l'empreinte couvre tout sauf `report_sha256` lui-même :```python
import hashlib, json
doc = json.load(open("report.json"))
claimed = doc.pop("report_sha256")
assert hashlib.sha256(json.dumps(doc, indent=2, sort_keys=True).encode()).hexdigest() == claimed
ubuntils scan exécute la collecte, la détection et la chronologie ensemble sur l'hôte actif. collect et analyze scindent ce pipeline en deux : collect acquiert un bundle inviolable depuis un hôte (aucune détection n'est exécutée), et analyze exécute le même pipeline de détection/chronologie utilisé par scan sur un bundle, ou sur une image montée via --root, sans nécessiter root et sans toucher à nouveau l'hôte d'origine. Cela concerne les cas où vous souhaitez acquérir des artefacts une fois et les analyser plus tard, ailleurs ou de manière répétée — ou lorsque vous triez une image disque plutôt qu'un système en cours d'exécution.
sudo ubuntils collect --output /path/to/bundle.tar.gz
Nécessite les droits root, comme `scan`. Lit une liste fixe de fichiers (`/etc/passwd`, `/etc/group`, `/etc/shadow`, `/etc/sudoers`, `/etc/ld.so.preload`, `/etc/environment`, `/etc/crontab`, `/etc/profile`, `/var/log/syslog`, `/var/log/messages`, `/var/log/audit/audit.log`) et exécute une liste fixe de commandes (`ss -tunap`, `netstat -tunap`, `systemctl list-timers` sous forme JSON et texte, et `journalctl -o json` pour les 7 derniers jours), calcule le hachage de chaque élément capturé, et écrit le tout plus un `manifest.json` dans un paquet `.tar.gz`. Les fichiers journaux capturés et la sortie de journalctl sont ce qui permet à `analyze BUNDLE` de construire une chronologie réelle hors ligne. Si `--output` est omis, le paquet est écrit dans `./ubuntils-bundle-<UTC timestamp>.tar.gz` dans le répertoire courant.
### `ubuntils analyze````bash
ubuntils analyze BUNDLE.tar.gz [--json] [--output FILE] [--config FILE] [--baseline FILE] [--rules FILE] [--since TIME]
ubuntils analyze --root /mnt/forensic-image [--json] [--output FILE] [--config FILE] [--baseline FILE] [--rules FILE] [--since TIME]
Prend soit un chemin de bundle comme argument positionnel, soit --root PATH pointant vers une image montée / une arborescence de système de fichiers extraite — pas les deux. Exécute le moteur de détection identique, les règles personnalisées, la liste d'autorisation et la logique de référence utilisés par scan. Ne nécessite pas root. Le bundle est extrait dans un répertoire temporaire privé qui est supprimé dès que l'analyse se termine (il peut contenir /etc/shadow).
La couverture diffère entre les deux modes hors ligne, car un bundle transporte un état capturé rejoué au moment de collect tandis que --root ne dispose que de ce qui se trouve sur le système de fichiers monté :
analyze BUNDLE rejoue la sortie réelle des commandes ss/systemctl list-timers/journalctl capturée au moment de collect, de sorte que NetworkCollector et SystemdCollector produisent de véritables résultats à partir de cet instantané — ils ne sont pas ignorés. La chronologie est construite à partir des syslog/messages/audit.log/journalctl capturés par le bundle, elle est donc entièrement peuplée et les résultats obtiennent une véritable corrélation related_events.analyze --root PATH pointe vers une image montée, morte, sans état de processus ou de noyau actif à interroger, donc l'exécution de commandes est entièrement désactivée : NetworkCollector et SystemdCollector sont ignorés, enregistrés dans scan_metadata.command_collectors_skipped. La chronologie est toujours construite, mais à partir des fichiers de journal statiques présents sur l'image (/var/log/syslog, /var/log/messages, /var/log/audit/audit.log) — le rejeu journald n'est pas disponible ici puisqu'il n'y a pas de journalctl actif pour interroger une image morte.Voir Analyse hors ligne : collect et analyze pour la liste complète des lacunes de couverture de détection hors ligne.
Un bundle est une archive tar gzippée avec tout sous un préfixe bundle/ :```
bundle/
├── manifest.json
├── files/
│ ├── etc/passwd
│ ├── etc/shadow
│ ├── var/log/syslog
│ └── ... # every captured file, path-flattened under files/
└── commands/
├── ss.txt
├── netstat.txt
├── systemctl_list_timers_json.txt
├── systemctl_list_timers_text.txt
└── journalctl.txt
Schéma de `manifest.json` :
| Champ | Type | Description |
|---|---|---|
| `run_id` | string | UUID généré à nouveau pour chaque exécution de `collect` |
| `host_id` | string | Réservé pour une future corrélation multi-hôtes ; actuellement vide |
| `hostname` | string | `socket.gethostname()` au moment de la collecte |
| `ubuntu_version` | string | Chaîne de version Ubuntu détectée |
| `collected_at_utc_start` / `collected_at_utc_end` | string (ISO 8601) | Bornes temporelles de l'exécution de collecte |
| `tool_version` | string | Version d'ubuntils ayant produit le bundle |
| `files[]` | array | Une entrée par fichier capturé : `source_path`, `bundle_path`, `sha256`, `size`, `mtime`, `ctime` (un fichier absent/illisible sur l'hôte source est enregistré avec `sha256: ""`, `size: -1` plutôt que d'interrompre la collecte) |
| `commands[]` | array | Une entrée par commande capturée : `name`, `argv`, `bundle_path`, `sha256`, `exit_code` |
| `bundle_sha256` | string | SHA-256 sur le reste du manifeste (tout ce qui précède, sérialisé de manière canonique) — l'ancre de preuve d'intégrité pour l'ensemble du bundle |
### Intégrité du bundle dans la sortie JSON
Le champ `scan_metadata.bundle_integrity` d'`analyze` rapporte l'une des trois valeurs suivantes :
- `"live"` — `scan` et `analyze --root` rapportent cette valeur ; il n'y a pas de bundle à vérifier.
- `"ok"` — `analyze BUNDLE` a vérifié `bundle_sha256` par rapport au manifeste et le SHA-256 de chaque fichier capturé par rapport à son contenu dans le bundle ; rien n'a été altéré depuis que `collect` l'a écrit.
- `"mismatch"` — le digest du manifeste, le hash d'un fichier capturé ou le hash de la sortie d'une commande capturée ne correspondait pas. Quelque chose dans le bundle a été modifié, tronqué ou corrompu après la collecte, et rien qui en dérive ne doit être considéré comme fiable au sens de la chaîne de possession. `analyze` produit quand même son rapport, mais affiche un avertissement rouge sur stderr, montre une bannière d'intégrité en haut de l'onglet Summary de la TUI, et **se termine avec le statut 3**, afin que les scripts ne puissent pas confondre les résultats d'un bundle altéré avec des résultats faisant autorité.
### ⚠️ L'analyse hors ligne présente de réelles lacunes de détection — à lire avant de s'y fier
**Une exécution d'`analyze` à partir d'un bundle ou de `--root` n'a pas la parité de détection avec un `scan` en direct.** Ce ne sont pas des cas limites ; ce sont des limitations structurelles de l'acquisition statique et hors ligne, et elles produisent moins (ou zéro) de résultats pour les règles concernées plutôt qu'une erreur. Lorsqu'un collecteur *sait* qu'il n'a pas pu regarder (une commande échouée, un fichier illisible), cela est enregistré dans `scan_metadata.collectors_degraded` et signalé dans l'onglet Summary de la TUI — mais un fichier qui n'a simplement pas été capturé ressemble à un fichier qui n'existe pas. (La timeline elle-même ne fait *plus* partie de ces lacunes : `analyze BUNDLE` rejoue les syslog/messages/audit.log/journalctl capturés au moment de `collect`, et `analyze --root` lit les fichiers de log statiques présents sur l'image montée, donc les deux produisent une vraie timeline et une vraie corrélation `related_events` — voir [`ubuntils analyze`](#ubuntils-analyze) ci-dessus.)
- **`PROCESS_MASQUERADE` et `PROCESS_SUSPICIOUS_CONNECTION` rapporteront toujours zéro résultat en mode hors ligne.** Les deux règles s'appuient sur le champ `exe` d'un processus, qui est peuplé en lisant la cible du lien symbolique `/proc/<pid>/exe` via la source d'artefacts (jamais le `/proc` de l'analyste lui-même). Un bundle n'a pas de `/proc` en direct à lire, et `--root` pointe vers une arborescence de système de fichiers montée sans `/proc` non plus — il n'existe actuellement aucun mécanisme pour capturer ou reconstruire hors ligne une cible de lien symbolique exe résolue, donc `exe` est toujours vide et les deux règles ne se déclenchent jamais, quel que soit ce qui se trouve réellement sur l'hôte.
- **L'énumération des processus n'a pas lieu du tout hors ligne.** `collect` n'a aucune étape de capture par PID (`/proc/*/status`, `/proc/*/cmdline`), donc aucun processus n'existe dans un bundle à analyser au départ — c'est la même cause racine que le point ci-dessus, du côté de l'acquisition.
- **`CRON_TMP_PATH`, `SUDOERS_NOPASSWD` et `SSH_UNAUTHORIZED_KEY` sont limités ou absents de l'analyse issue d'un bundle.** La liste de fichiers de `collect` est statique et ne peut pas étendre par glob `/etc/cron.d/*`, `/etc/sudoers.d/*`, `/etc/profile.d/*`, ou les `~/.ssh/authorized_keys` par utilisateur — seuls `/etc/crontab`, `/etc/sudoers` et `/etc/environment`/`/etc/profile` sont capturés. (`--root` sur une arborescence de système de fichiers complète montée n'a pas cette lacune, puisque les vrais répertoires sont présents sur le disque.) Lorsque `SSH_UNAUTHORIZED_KEY` ou `SHELL_RC_MODIFICATION` *se* déclenchent (scan en direct, ou `--root` avec les vrais répertoires par utilisateur présents), ils calculent désormais aussi un score de confiance à partir du ctime et du contenu du fichier, et non du mtime seul — voir [Confidence scoring](#json-output) ci-dessous. Cela améliore la confiance à accorder à un résultat qui se déclenche ; cela ne change pas si la règle se déclenche hors ligne au départ.
- **La détection de `SUSPICIOUS_SYSTEMD_TIMER` est affaiblie pour les bundles.** Les unités de service sont lues directement depuis les répertoires d'unités (`/etc/systemd/system`, `/usr/lib/systemd/system`, `~/.config/systemd/user` par utilisateur, …), donc `--root` obtient une couverture complète des services. Un bundle ne capture pas ces répertoires, cependant : les timers apparaissent à partir de la sortie capturée de `systemctl list-timers`, mais l'`ExecStart` de chaque timer provient d'un appel `systemctl show` par unité que `collect` n'effectue pas, donc la règle ne peut pas évaluer ce qu'exécute un timer présent dans un bundle.
**Quand cela compte :** si vous triez un hôte en direct et joignable, utilisez `sudo ubuntils scan` — il dispose d'une couverture de détection complète. Utilisez `collect`/`analyze` lorsque vous devez acquérir une fois et analyser ailleurs, devez analyser sans root, ou travaillez à partir d'une image disque où `scan` n'est pas du tout une option — et considérez un résultat `analyze` propre pour les règles ci-dessus comme « non vérifié », et non « vérifié et propre ».
---
## La TUI
Lancer `sudo ubuntils scan` (sans `--json`) démarre une TUI interactive plein écran.
### Écran de scan
Pendant que les collecteurs s'exécutent, ubuntils affiche une liste de contrôle en direct — une ligne par collecteur. Chaque ligne se met à jour en temps réel à mesure que le collecteur se termine :```
Scanning system…
✓ Process
✓ Network
✓ Users
⠹ Cron
Systemd
SSH
Sudoers
Environment
✓ indique un succès, ✗ indique un échec, le spinner indique le collecteur actif, et les lignes vides sont en attente. Une fois que tous les collecteurs ont terminé et que la détection + la chronologie sont complètes, le TUI bascule automatiquement vers l'écran des résultats.
L'écran des résultats comporte quatre onglets navigables par les touches numériques :
| Touche | Onglet | Contenu |
|---|---|---|
1 | Résumé | Statistiques du scan + principales conclusions en un coup d'œil |
2 | Conclusions | Liste complète des conclusions avec détails en ligne et remédiation |
3 | Chronologie | Événements de journal corrélés par ordre chronologique |
4 | Statistiques | Version d'Ubuntu, architecture, durée, nombre de collecteurs |
Appuyez sur q ou Ctrl+C pour quitter.
1)Affiche les métadonnées du scan et les principales conclusions sur un seul écran :``` Collectors: 8 run · 0 failed Findings: 2 HIGH · 1 MEDIUM · 0 LOW Timeline: 47 events Duration: 2.8s
● HIGH CRON_TMP_PATH /etc/cron.d/cleanup ● HIGH LD_PRELOAD_INJECT /home/alice/.bashrc ○ MED SSH_UNAUTHORIZED_KEY /home/bob/.ssh/authorized_keys
Sur un système propre, cet onglet affiche `System appears clean.`
### Onglet Findings (touche `2`)
Une liste défilante de tous les résultats, triés HIGH → MEDIUM → LOW. Sélectionner un résultat (Entrée ou touches fléchées) déploie un volet de détails en bas affichant la description complète, le chemin de l'artefact, la valeur brute déclenchante et les informations de remédiation.```
HIGH CRON_TMP_PATH /etc/cron.d/cleanup
HIGH LD_PRELOAD_INJECT /home/alice/.bashrc
MED SSH_UNAUTHORIZED_KEY /home/bob/.ssh/authorized_keys
───────────────────────────────────────────────────────
A cron job was found referencing /tmp, /var/tmp, or /dev/shm.
These directories are world-writable and commonly used as
attacker staging grounds.
Artifact: /etc/cron.d/cleanup
Raw: 0 * * * * root /tmp/.update
Fix: Will remove the offending cron entry from
/etc/cron.d/cleanup after creating a timestamped backup.
R: remediate
Pour les résultats disposant d'une remédiation automatisée, appuyez sur R lorsque le résultat est sélectionné. Une fenêtre de confirmation apparaît :```
┌─────────────────────────────────────────────────┐
│ Remediate CRON_TMP_PATH? │
│ │
│ Will remove the offending cron entry from │
│ /etc/cron.d/cleanup after creating a backup. │
│ Backup will be created at /var/backups/ubuntils/…│
│ │
│ Y: confirm Esc: cancel │
└─────────────────────────────────────────────────┘
Appuyez sur `Y` pour confirmer. Le correcteur s'exécute dans un thread en arrière-plan. Lorsqu'il se termine, la ligne de la vulnérabilité dans la liste est mise à jour en `[fixed]` et le volet de détails affiche le résultat :```
✓ Remediated
Backup: /var/backups/ubuntils/20260115_142201/etc_cron.d_cleanup
Rollback: cp /var/backups/ubuntils/20260115_142201/etc_cron.d_cleanup /etc/cron.d/cleanup
En cas d'échec, le volet de détail affiche l'erreur. La sauvegarde est toujours créée avant toute tentative de modification.
Appuyez sur Esc pour réduire le volet de détail.
3)Une liste chronologique défilable des événements de journal corrélés. Chaque ligne affiche l'horodatage, la source et la description. Les événements sont extraits de syslog, journald et auditd puis dédupliqués.
4)Une vue récapitulative du scan : version d'Ubuntu détectée, architecture, durée du scan, nombre de collecteurs et échecs, et nombre de détections par sévérité ainsi que le total des événements de la timeline.
| ID de règle | Sévérité | Corrigeable | Ce qu'il vérifie |
|---|---|---|---|
| CRON_ROOT_EXEC | HIGH | Oui | Crontabs d'utilisateurs non-root exécutant des commandes dans des chemins appartenant à root ou intégrant sudo en ligne |
| CRON_TMP_PATH | HIGH | Oui* | Toute tâche cron (y compris les entrées @reboot/@daily et les scripts dans /etc/cron.{hourly,daily,weekly,monthly}) référençant /tmp, /var/tmp ou /dev/shm. *Les lignes de script sont signalées uniquement |
| LD_PRELOAD_INJECT | HIGH | Oui | Toute entrée dans /etc/ld.so.preload (vide sur une Ubuntu standard), ou LD_PRELOAD dans n'importe quel fichier d'initialisation de shell avec une bibliothèque listée en dehors de /lib, /usr/lib, /lib64, /usr/lib64 |
| SUSPICIOUS_SYSTEMD_TIMER | HIGH | Non | Timers systemd et unités de service dont ExecStart référence un répertoire accessible en écriture à tous ou exécute un binaire qui n'appartient pas à root |
| SSH_UNAUTHORIZED_KEY | MEDIUM | Oui | Fichiers authorized_keys modifiés au cours des 7 derniers jours |
| USER_UID_ZERO | HIGH | Non | Tout compte autre que root avec l'UID 0 (un second superutilisateur caché) |
| USER_EMPTY_PASSWORD | HIGH | Non | Un compte avec shell de connexion dont le champ mot de passe dans /etc/shadow est vide (le nullok par défaut de PAM sur Ubuntu lui permet de se connecter sans mot de passe) |
| SUDOERS_NOPASSWD | MEDIUM | Oui* | Autorisations sudoers NOPASSWD pour des utilisateurs avec UID ≥ 1000 et un shell de connexion, directement ou via une règle %group ; les fichiers inclus sont suivis. *Les règles de groupe sont signalées uniquement (supprimer %sudo pourrait supprimer tout accès sudo) |
| PROCESS_MASQUERADE | MEDIUM | Non | Processus dont le nom correspond à un binaire système connu mais dont l'exécutable se trouve en dehors des répertoires système standard (/usr/bin, /usr/sbin, /bin, /sbin, /usr/local/{bin,sbin}, /usr/lib, /usr/libexec, /lib, /snap) |
| PROCESS_SUSPICIOUS_CONNECTION | HIGH / MEDIUM | Non | Processus détenant une connexion sortante dont l'exécutable se trouve dans un répertoire temporaire accessible en écriture à tous ou a été supprimé du disque (HIGH), ou se trouve en dehors des répertoires système standard ou communique avec un port distant non standard (MEDIUM) |
CRON_ROOT_EXEC — Les crontabs utilisateur s'exécutent en tant que propriétaire du crontab. Une entrée invoquant sudo ou un interpréteur appartenant à root signifie que l'utilisateur a organisé l'exécution de code avec les privilèges root selon un planning, sans avoir besoin d'un accès sudo persistant. Cela survit aux changements de mot de passe.
Exemple de détection :``` [HIGH] CRON_ROOT_EXEC Title: User crontab executing with sudo Artifact: /var/spool/cron/crontabs/alice Raw value: */5 * * * * sudo /usr/bin/python3 /tmp/beacon.py Remediation: available
**CRON_TMP_PATH** — Les répertoires accessibles en écriture à tous comme /tmp et /dev/shm sont des zones de préparation standard pour les attaquants. Une tâche cron pointant vers cet emplacement signifie qu'une charge utile peut être remplacée entre les invocations sans toucher à un chemin persistant. Cela couvre les entrées de type `@reboot`/`@daily` et les scripts dans `/etc/cron.{hourly,daily,weekly,monthly}` ; les détections sur ces scripts sont en mode signalement uniquement, car supprimer une ligne d'un script shell n'est pas une correction automatique sûre.
*Exemple de détection :*```
[HIGH] CRON_TMP_PATH
Title: Cron job references writable temp directory
Artifact: /etc/cron.d/cleanup
Raw value: 0 * * * * root /tmp/.update
Remediation: available
LD_PRELOAD_INJECT — LD_PRELOAD amène l'éditeur de liens dynamique à charger une bibliothèque partagée spécifiée avant toutes les autres, permettant l'interception arbitraire de fonctions dans tout binaire lié dynamiquement. Une valeur pointant en dehors des chemins de bibliothèques standard est un indicateur quasi certain de rootkit en espace utilisateur ; chaque bibliothèque d'une liste séparée par des espaces ou des deux-points est vérifiée. /etc/ld.so.preload s'injecte dans chaque processus et est vide sur une installation Ubuntu standard, donc toute entrée y figurant est signalée — même une entrée placée dans /lib, une astuce courante de rootkit. La remédiation supprime les entrées de /etc/ld.so.preload plutôt que de les commenter, car le chargeur n'a pas de syntaxe de commentaire dans ce fichier.
Exemple de détection :``` [HIGH] LD_PRELOAD_INJECT Title: LD_PRELOAD set to non-standard library path Artifact: /home/alice/.bashrc Raw value: export LD_PRELOAD=/tmp/.libssl.so Remediation: available
**SUSPICIOUS_SYSTEMD_TIMER** — Les timers systemd sont plus persistants et moins visibles que les tâches cron pour la plupart des intervenants. Un timer — ou une simple unité `.service`, qui constitue la persistance la plus courante et ne nécessite aucun timer — dont le ExecStart fait référence à un répertoire temporaire ou exécute un binaire qui n'appartient pas à root est un signe de persistance créée par un attaquant. Les unités de service sont lues directement depuis les répertoires d'unités, y compris le répertoire par utilisateur `~/.config/systemd/user`. Signalement uniquement — la suppression d'une unité systemd nécessite un jugement humain.
*Exemple de résultat :*```
[HIGH] SUSPICIOUS_SYSTEMD_TIMER
Title: Systemd timer ExecStart points to suspicious path
Artifact: /etc/systemd/system/update-check.timer
Raw value: ExecStart=/tmp/.sys/update
Remediation: not available
SSH_UNAUTHORIZED_KEY — Une clé SSH nouvellement ajoutée accorde un accès distant persistant indépendant des mots de passe. La fenêtre de 7 jours détecte les ajouts récents tout en évitant le bruit lié au provisionnement initial sur les systèmes plus anciens. Remarque : la règle utilise le mtime du fichier, qui reflète la dernière écriture dans le fichier authorized_keys, et non l'horodatage d'insertion de chaque clé individuelle.
Exemple de résultat :``` [MEDIUM] SSH_UNAUTHORIZED_KEY Title: SSH authorized key added in last 7 days Artifact: /home/bob/.ssh/authorized_keys Raw value: ssh-rsa AAAAB3NzaC1... attacker@evil Remediation: available
**SUDOERS_NOPASSWD** — Le sudo sans mot de passe pour un compte utilisateur humain (UID ≥ 1000 avec un shell de connexion) est un vecteur d'élévation de privilèges qui survit à la suppression d'autres mécanismes de persistance. Les autorisations NOPASSWD légitimes concernent presque toujours des comptes de service sans shell de connexion. Les règles de groupe (`%sudo ALL=(ALL) NOPASSWD:ALL`) sont résolues vers leurs membres, et les fichiers `#include`/`@includedir` sont suivis. Les détections de groupe sont uniquement signalées : supprimer une règle comme `%sudo` pourrait retirer toutes les autorisations sudo du système.
*Exemple de détection :*```
[MEDIUM] SUDOERS_NOPASSWD
Title: NOPASSWD sudo grant for regular user
Artifact: /etc/sudoers.d/alice
Raw value: alice ALL=(ALL) NOPASSWD: ALL
Remediation: available
PROCESS_MASQUERADE — Nommer un binaire malveillant d'après un processus système connu (sshd, python3, bash) est une technique de base pour éviter la détection dans la sortie de ps. Cette règle croise le nom du processus issu de /proc/<pid>/status avec le chemin exe résolu depuis /proc/<pid>/exe. Les emplacements standard incluent /usr/local/{bin,sbin}, /usr/lib, /usr/libexec et /snap, de sorte que systemd (/usr/lib/systemd/systemd) et les paquets snap ne la déclenchent pas. Signalement uniquement — tuer un processus requiert un jugement humain.
Exemple de détection :``` [MEDIUM] PROCESS_MASQUERADE Title: Process masquerading as system binary Artifact: /proc/1337/exe Raw value: name=sshd, exe=/tmp/.sshd Remediation: not available
**USER_UID_ZERO** — Seul `root` doit détenir l'UID 0. Un second compte mappé sur l'UID 0 (CIS Ubuntu Benchmark 6.2.x) est une porte dérobée à haute confiance : il accorde tous les droits de superutilisateur sans modifier les identifiants de root lui-même, et survit à une réinitialisation du mot de passe root. Taux de faux positifs quasi nul. Signalement uniquement — la suppression d'un compte UID-0 nécessite un jugement humain.
*Exemple de résultat :*```
[HIGH] USER_UID_ZERO
Title: Non-root account with UID 0
Artifact: /etc/passwd
Raw value: toor:x:0:0:...:/bin/bash
Remediation: not available
USER_EMPTY_PASSWORD — Un compte dont le champ mot de passe dans /etc/shadow est vide n'a aucun mot de passe, et la pile PAM par défaut d'Ubuntu (pam_unix ... nullok) lui permet de se connecter sans mot de passe. Sur un compte disposant d'un shell de connexion, c'est une porte ouverte. Signalement uniquement — verrouillez-le avec passwd -l pendant votre investigation.
Exemple de constat :``` [HIGH] USER_EMPTY_PASSWORD Title: Login account with no password Artifact: /etc/shadow Raw value: eve:: Remediation: not available
**PROCESS_SUSPICIOUS_CONNECTION** — La persistance n'est que la moitié du tableau ; un point d'ancrage qui ne communique jamais avec rien est rarement celui qui vous intéresse. Cette règle joint les collecteurs de processus et de réseau par PID afin qu'un processus signalé arrive avec ses connexions actuelles attachées. Un exécutable déposé dans `/tmp` détenant un socket sortant établi est HIGH ; un binaire légitime atteignant un port distant non standard est MEDIUM et mérite un coup d'œil. Il s'agit d'un instantané de l'état actuel, pas d'une surveillance continue — un beacon qui dort au moment de votre scan n'apparaîtra pas. Flag-only.
*Exemple de résultat :*```
[HIGH] PROCESS_SUSPICIOUS_CONNECTION
Title: Process with suspicious outbound connection
Artifact: /proc/1337/exe
Raw value: 203.0.113.9:4444
Remediation: not available
SHELL_RC_MODIFICATION — Les fichiers d'initialisation du shell constituent un vecteur de persistance fiable car ils s'exécutent à chaque connexion de l'utilisateur. Cette règle fait remonter les modifications récentes pour examen humain. Signalement uniquement — le contenu des fichiers RC du shell doit être lu avant toute action.
Exemple de résultat :``` [LOW] SHELL_RC_MODIFICATION Title: Shell init file recently modified Artifact: /root/.bashrc Raw value: mtime=2024-01-15 14:22:01 (6 hours ago) Remediation: not available
**PACKAGE_TAMPERED** — Les binaires et fichiers de configuration appartenant au système constituent le fondement de la confiance. Cette règle détecte lorsque des fichiers appartenant à des paquets ont été modifiés, supprimés, ou présentent des incohérences de contenu/mode/taille à l'aide de `dpkg --verify`. Les modifications portant uniquement sur les conffiles (changements de configuration locale attendus) sont exclues du rapport afin d'éviter le bruit. Signalement uniquement — une altération peut être légitime (modifications locales personnalisées) ou malveillante (remplacement de fichier) ; la décision nécessite un jugement humain.
*Exemple de constat :*```
[HIGH] PACKAGE_TAMPERED
Title: Package-owned file modified since installation
Artifact: /usr/bin/sshd
Raw value: ....5..T. (content and mtime differ)
Remediation: not available
IMMUTABLE_FLAG_SET — Les attaquants définissent souvent l'attribut immuable (i) ou l'attribut append-only (a) sur des fichiers afin d'empêcher leur modification ou leur suppression, même par root, y compris pour dissimuler une altération vis-à-vis d'éditions ultérieures ou de la rotation des journaux. Définir ces attributs sur des fichiers système sensibles comme /etc/passwd, /etc/sudoers, /etc/pam.d/*, ou les fichiers de journal auth/syslog/wtmp/btmp est un fort indicateur de durcissement par un attaquant. Cette règle détecte les attributs immuable et append-only via lsattr sur cette liste fixe de chemins sensibles. Signalement uniquement — les modifications d'attributs nécessitent une revue humaine.
Exemple de détection :``` [MEDIUM] IMMUTABLE_FLAG_SET Title: Sensitive file has an unexpected chattr flag Artifact: /etc/ld.so.preload Raw value: ----i--------e--- Remediation: not available
**PAM_BACKDOOR** — PAM (Pluggable Authentication Modules) et NSS (Name Service Switch) sont les systèmes centraux d'authentification et d'identité sous Linux. Cette règle ne fait PAS de détection basée sur le mtime du type « ce fichier a-t-il été modifié » — elle effectue une correspondance de motifs sur le *contenu* du fichier : (1) une ligne littérale `pam_permit.so` dans n'importe quel fichier /etc/pam.d/* (ce module réussit toujours et constitue une porte dérobée classique de contournement d'authentification), ou (2) un module NSS listé dans /etc/nsswitch.conf qui ne figure pas dans la liste d'autorisation intégrée d'ubuntils. **Remarque :** la vérification NSS produira des faux positifs sur les hôtes joints au domaine/SSSD/LDAP/Winbind utilisant un module hors de la liste d'autorisation intégrée — elle est intentionnellement notée avec une confiance plus faible que la correspondance pam_permit.so pour cette raison ; utilisez `--config` pour ajouter les noms de modules non reconnus à la liste d'autorisation de votre environnement. Signalement uniquement — les modifications de configuration d'authentification nécessitent une vérification minutieuse.
*Exemples de résultats :*```
[HIGH] PAM_BACKDOOR
Title: PAM config unconditionally permits authentication
Artifact: /etc/pam.d/sshd
Raw value: auth required pam_permit.so
Remediation: not available
[HIGH] PAM_BACKDOOR
Title: Unexpected NSS module in nsswitch.conf
Artifact: /etc/nsswitch.conf
Raw value: passwd: files evilmod
Remediation: not available
KERNEL_MODULE_SUSPICIOUS — Les modules du noyau s'exécutent en ring 0 avec un accès illimité. Les attaquants chargent fréquemment des modules noyau personnalisés pour des rootkits, l'analyse de paquets ou la dissimulation de processus. Cette règle compare les modules actuellement chargés à une petite liste d'autorisation de modules intégrés attendus (communs à la plupart des systèmes). Sa sévérité est LOW car cette liste d'autorisation est délibérément restreinte. Remarque : les hôtes fortement équipés en matériel avec des pilotes GPU, des cartes Wi-Fi ou des pilotes propriétaires généreront des faux positifs. Les intervenants doivent ajouter les modules attendus de leur hôte via --config, en autorisant par nom de module (utilisé comme artifact_path). Flag-only — l'investigation des modules noyau nécessite des outils forensiques et une expertise humaine.
Exemple de résultat :``` [LOW] KERNEL_MODULE_SUSPICIOUS Title: Loaded kernel module not in the expected set Artifact: implant_rootkit Raw value: {'name': 'implant_rootkit', 'size': '12288', 'used_by': []} Remediation: not available
**SETUID_INVENTORY** — Les binaires setuid et setgid élèvent automatiquement les privilèges lors de leur exécution. Les attaquants créent des binaires setuid/setgid personnalisés pour persister une élévation de privilèges, le plus souvent en dehors des répertoires standards des binaires système (un chemin d'installation classique comme /opt, le /home d'un utilisateur, /srv, ou un répertoire temporaire accessible en écriture à tous). Cette règle inventorie les binaires setuid/setgid via `find -perm -4000 -o -perm -2000` dans `/usr /bin /sbin /opt /home /srv /tmp /var/tmp /dev/shm` et signale tout binaire en dehors d'un ensemble de référence connu d'utilitaires système légitimes. Signalement uniquement — les binaires setuid/setgid inattendus nécessitent une investigation mais peuvent être des binaires légitimes installés par une application.
*Exemple de détection :*```
[LOW] SETUID_INVENTORY
Title: Unexpected setuid binary
Artifact: /tmp/.hidden/backdoor
Raw value: setuid
Remediation: not available
--json écrit un seul objet JSON sur stdout. Rien d'autre n'est imprimé.```json
{
"scan_metadata": {
"tool_version": "2.1.0",
"hostname": "web-01",
"generated_at": "2026-06-10T08:22:03.114523+00:00",
"ubuntu_version": "Ubuntu 22.04.3 LTS",
"architecture": "x86_64",
"duration_s": 2.84,
"collector_failures": 0,
"bundle_integrity": "live",
"command_collectors_skipped": [],
"collectors_degraded": {},
"rules_failed": [],
"timeline_error": null,
"suppressed_by_baseline": 1
},
"artifact_counts": {
"ProcessCollector": 142,
"NetworkCollector": 23,
"UserCollector": 4,
"CronCollector": 7,
"SystemdCollector": 12,
"SSHCollector": 3,
"SudoersCollector": 5,
"EnvironmentCollector": 18
},
"findings": [
{
"rule_id": "CRON_TMP_PATH",
"severity": "HIGH",
"title": "Cron job references writable temp directory",
"description": "A cron job was found referencing /tmp, /var/tmp, or /dev/shm. These directories are world-writable and commonly used as attacker staging grounds.",
"artifact_path": "/etc/cron.d/cleanup",
"raw_value": "0 * * * * root /tmp/.update",
"remediation_available": true,
"remediation_description": "Will remove the offending cron entry from /etc/cron.d/cleanup after creating a timestamped backup.",
"related_events": [
{
"timestamp": "2024-01-15T08:20:00+00:00",
"source": "syslog",
"description": "CRON[2841]: (root) CMD (/tmp/.update)"
}
],
"confidence": 75,
"confidence_band": "HIGH",
"signals": [
{"name": "timeline_corroboration", "weight": 25, "detail": "1 nearby timeline event(s)"}
]
},
{
"rule_id": "PROCESS_MASQUERADE",
"severity": "MEDIUM",
"title": "Process masquerading as system binary",
"description": "Process 'sshd' (pid=1337) has exe path outside standard binary directories: /tmp/.sshd",
"artifact_path": "/proc/1337/exe",
"raw_value": "/tmp/.sshd",
"remediation_available": false,
"guided_remediation": "Confirm pid 1337 is malicious (ls -l /proc/1337/exe, cat /proc/1337/cmdline), then terminate it: kill -9 1337.",
"confidence": 50,
"confidence_band": "MEDIUM",
"signals": []
}
],
"timeline": [
{
"timestamp": "2024-01-15T08:22:01+00:00",
"source": "syslog",
"description": "sshd: Accepted publickey for alice from 10.0.0.42 port 52341"
}
],
"report_sha256": "a3f1c9…(64 hex chars)"
}
`remediation_results` apparaît comme une clé de premier niveau supplémentaire uniquement lorsque `--remediate` est passé. `report_sha256` est toujours présent et est calculé sur le reste du document. `scan_metadata.bundle_integrity` vaut `"live"` pour `scan` et `analyze --root`, `"ok"` pour un bundle vérifié passé à `analyze`, et `"mismatch"` si le contenu d'un bundle ne correspond pas à son manifeste — voir [Bundle integrity in JSON output](#bundle-integrity-in-json-output). `scan_metadata.command_collectors_skipped` nomme tout collecteur basé sur des commandes (`NetworkCollector`, `SystemdCollector`, `PackageCollector`, `KernelCollector`) ignoré pour une exécution `--root` — toujours vide pour `scan` et `analyze BUNDLE`. `scan_metadata.suppressed_by_baseline` est le nombre de findings qu'un fichier `--baseline` a retirés de ce rapport — voir [Known-good baselining](#known-good-baselining---baseline). `scan_metadata.collectors_degraded` associe un nom de collecteur aux raisons pour lesquelles ses données sont incomplètes (une commande qui a échoué ou expiré, un fichier illisible, une ligne malformée ignorée), `rules_failed` liste toute règle de détection qui a planté, et `timeline_error` est défini si la timeline n'a pas pu être construite. Les trois sont vides lors d'une exécution saine. Vérifiez-les avant d'interpréter une liste de findings vide comme « propre ».
`related_events` et `guided_remediation` n'apparaissent sur un finding que lorsqu'ils ont du contenu. `related_events` contient jusqu'à cinq événements de timeline mis en correspondance avec le finding par chemin d'artefact et mots-clés de règle, du plus récent au plus ancien — une aide à la mise en évidence, pas une affirmation de causalité. `guided_remediation` est une séquence de commandes revue que vous exécutez manuellement ; ubuntils ne l'exécute jamais.
### Notation de confiance
Chaque finding porte un score `confidence` (0–100, par défaut 50) et une `confidence_band` (`HIGH` ≥ 75, `MEDIUM` ≥ 40, `LOW` en dessous), plus une liste `signals` montrant exactement comment ce score a été atteint — chaque entrée est `{"name", "weight", "detail"}`, de sorte que le score est toujours explicable, jamais une boîte noire. Les signaux s'additionnent à une confiance de base de 50 et sont appliqués par la règle ou l'étape de pipeline qui les a produits :
- Les règles de détection appliquent leurs propres signaux au moment du finding — par ex. `SSH_UNAUTHORIZED_KEY` et `SHELL_RC_MODIFICATION` ajoutent `content_match` (+30) lorsque le contenu de l'artefact correspond à un motif connu comme dangereux (une option de clé SSH dangereuse ; une ligne curl/wget-vers-shell ou base64-decode dans un fichier RC de shell), `ctime_corroborates_mtime` (+20) lorsque le ctime du fichier est également dans la fenêtre de détection (plus difficile à falsifier que le mtime seul), ou `mtime_only` (−20) lorsque la récence est le *seul* signal et que le ctime ne le corrobore pas — un indice que le mtime a peut-être été antidaté.
- Le pipeline applique `timeline_corroboration` (+25) après la corrélation finding↔timeline, lorsqu'un finding a un ou plusieurs `related_events`.
Un finding de bande `LOW` n'est ni écarté ni masqué — il apparaît toujours dans la liste des findings et la sortie JSON exactement comme n'importe quel autre — mais la bande vous indique quel poids lui accorder avant d'investiguer plus avant. Cela remplace l'ancienne heuristique basée uniquement sur le mtime pour `SSH_UNAUTHORIZED_KEY`/`SHELL_RC_MODIFICATION`, où un fichier ancien mais légitimement touché (par ex. un outil de gestion de configuration réécrivant `.bashrc` à chaque exécution) semblait identique à une véritable nouvelle backdoor.
**Limitation connue :** seuls les signaux de motif de contenu et de ctime sont actifs aujourd'hui. Les signaux basés sur la propriété/l'empreinte — une empreinte de clé SSH inconnue, l'option de restriction `from=` d'une clé, ou une incohérence propriétaire/mode d'un fichier RC (un signal `ownership_anomaly`) — ne sont pas encore implémentés. Il s'agit d'une lacune de couverture différée, suivie pour une future version, que le score de confiance actuel ne prend pas en compte.
---
## Remédiation
Cinq des seize règles de détection disposent d'une remédiation automatisée : `CRON_ROOT_EXEC`, `CRON_TMP_PATH`, `LD_PRELOAD_INJECT`, `SSH_UNAUTHORIZED_KEY` et `SUDOERS_NOPASSWD`. Les autres sont en signalement seul et ne seront jamais auto-remédiées, car agir dessus en toute sécurité nécessite qu'un humain regarde d'abord.
### Remédiation guidée
`SUSPICIOUS_SYSTEMD_TIMER`, `PROCESS_MASQUERADE` et `SHELL_RC_MODIFICATION` portent une chaîne `guided_remediation` : les commandes exactes à exécuter une fois le finding confirmé — `systemctl disable --now <unit>`, `kill -9 <pid>`, ou la revue et le retour arrière du fichier RC. Elle s'affiche dans le volet de détail de la TUI et dans le JSON. ubuntils ne l'exécute jamais pour vous ; ces règles restent hors du balayage `--remediate --confirm` par conception.
### Dans la TUI
Sélectionnez n'importe quel finding disposant d'une remédiation dans l'onglet Findings, puis appuyez sur `R`. Une modale de confirmation prévisualise l'action planifiée. Appuyez sur `Y` pour l'appliquer — le remédiateur s'exécute dans un thread d'arrière-plan afin que la TUI reste réactive. La ligne du finding passe à `[fixed]` une fois terminé, avec le chemin de sauvegarde et la commande de rollback exacte affichés en ligne.
### Depuis la CLI
`--remediate` sans `--confirm` est un dry run sûr : les sauvegardes sont créées et la validation s'exécute, mais aucun changement n'est appliqué. Passez les deux flags pour réellement effectuer des changements. Le pipeline s'exécute avant le lancement de la TUI dans ce mode, et l'onglet Summary liste chaque résultat de remédiation avec son chemin de sauvegarde et sa commande de rollback.
`--remediate --confirm` n'agit que sur les findings ayant un score de confiance d'au moins 40 (la bande MEDIUM) — un `SSH_UNAUTHORIZED_KEY` à faible confiance, basé uniquement sur le mtime, est signalé comme `SKIPPED` plutôt que de voir sa clé supprimée. Ajustez le seuil avec `--min-confidence N`.```bash
sudo ubuntils scan --remediate # dry run
sudo ubuntils scan --remediate --confirm # apply changes, then open TUI
sudo ubuntils scan --remediate --confirm --min-confidence 75 # only HIGH-confidence findings
Chaque remédiation suit le même schéma, quel que soit son déclencheur :
/var/backups/ubuntils/YYYYMMDD_HHMMSS/ avec le mode 0700/etc/ld.so.preload sont supprimées (le loader n'a pas de syntaxe de commentaire à cet endroit, donc une entrée commentée serait quand même chargée). Pour sudoers, le contenu modifié est vérifié avec visudo -cf sur une copie temporaire avant que le fichier réel ne soit touché/etc/sudoers tronquéSi une étape échoue, la remédiation s'arrête immédiatement, le système est laissé inchangé, et l'erreur complète est signalée avec le chemin de sauvegarde et la commande de rollback. L'accès sudo est protégé de deux manières : les règles NOPASSWD %group (comme %sudo) sont en lecture seule et ne sont jamais supprimées automatiquement, et le remédiateur sudoers refuse de supprimer la dernière règle du fichier sudoers principal.
ubuntils est conçu pour un triage mono-hôte et ponctuel — vous l'exécutez quand vous suspectez déjà qu'il y a un problème, et il ne communique jamais vers l'extérieur ni ne continue à surveiller après la fin du scan. C'est délibéré, mais cela signifie aussi qu'une découverte d'ubuntils ne vit que dans ce seul rapport, à moins que quelque chose ne la transmette. La plupart des équipes exploitant Ubuntu à quelque échelle que ce soit disposent déjà d'un SIEM assurant le volet continu de la détection, donc plutôt que d'intégrer ubuntils dans son propre agent de surveillance longue durée, il transmet ses découvertes à celui que vous exécutez probablement déjà : Wazuh.
Si un agent Wazuh est présent sur l'hôte (/var/ossec/bin/wazuh-agentd ou
/var/ossec/etc/ossec.conf existe), ubuntils scan (sauf exécution avec --no-wazuh) ajoute
chaque découverte sous forme d'une ligne JSON à /var/log/ubuntils/wazuh-alerts.json pour
que l'agent la récupère — c'est un pur transmetteur, pas un module Wazuh : aucun
appel réseau, aucune clé API, rien d'autre que les mêmes écritures d'artefacts locales
qu'ubuntils effectue déjà — bien que l'agent expédiera bien sûr ces lignes
hors de l'hôte vers son manager ; c'est le but. Il est auto-détecté sans
flag requis (utilisez --no-wazuh pour exclure une exécution), donc un
ubuntils scan scripté ou planifié sur un parc d'hôtes enrôlés auprès d'un agent
commence à alimenter le SIEM immédiatement sans câblage supplémentaire. Cela ne se produit jamais
pendant un ubuntils analyze hors ligne (bundle ou --root), car
ces découvertes décrivent un hôte différent de celui exécutant l'agent Wazuh
local — transmettre les découvertes d'un bundle à l'agent de l'analyste lui-même
les attribuerait à la mauvaise machine.
L'intention est d'intégrer ubuntils dans un pipeline d'alerte/escalade existant plutôt que de demander à un intervenant de surveiller un second outil : une fois les règles d'exemple ci-dessous chargées, une découverte ubuntils de sévérité HIGH (un nouveau compte UID-0, un rootkit LD_PRELOAD, une backdoor PAM) apparaît comme une alerte Wazuh normale, hérite de tout routage de notification que le manager a déjà configuré, et se place aux côtés de tous les autres signaux dans la même chronologie au lieu d'un fichier JSON autonome que quelqu'un doit penser à consulter.
Pour que Wazuh analyse et alerte sur ces découvertes, copiez les règles d'exemple
depuis examples/wazuh/ sur votre manager Wazuh, et ajoutez le bloc <localfile>
depuis examples/wazuh/ossec_localfile_snippet.xml au fichier
/var/ossec/etc/ossec.conf de l'agent. Aucune installation de décodeur personnalisé n'est nécessaire : le
localfile est configuré avec log_format json, donc le décodeur JSON intégré de Wazuh
analyse chaque ligne et mappe chaque clé JSON de premier niveau k vers data.k,
que local_rules.xml fait correspondre directement.
examples/wazuh/local_rules.xml → /var/ossec/etc/rules/ du manager<localfile> de examples/wazuh/ossec_localfile_snippet.xml → /var/ossec/etc/ossec.conf de l'agentsystemctl restart wazuh-manager (manager), systemctl restart wazuh-agent (hôte de l'agent)Ce ne sont que des modèles d'exemple, fournis comme point de départ — ils n'ont pas été testés contre un manager Wazuh en production et doivent être vérifiés dans un environnement non-production avant de s'y fier.
Schéma JSON par ligne :
| Champ | Type | Description |
|---|---|---|
timestamp | string (ISO 8601) | Quand la découverte a été transmise |
hostname | string | L'hôte scanné |
rule_id | string | Correspond à l'ID de règle de détection d'ubuntils (voir le tableau des règles de détection ci-dessus) |
severity | string | HIGH | MEDIUM | LOW |
title | string | Titre court lisible par un humain |
description | string | Description complète de la découverte |
artifact_path | string | Chemin du fichier/ressource où le problème a été trouvé |
raw_value | string | La ligne/valeur brute qui a déclenché la règle |
remediation_available | bool | Indique si ubuntils dispose d'un remédiateur pour cette règle |
related_events | array (optionnel) | Événements de chronologie corrélés, le cas échéant |
| Collecteur | Artefacts collectés |
|---|---|
| ProcessCollector | Processus en cours d'exécution depuis /proc et la sortie de ps |
| NetworkCollector | Connexions ouvertes et écouteurs depuis ss/netstat |
| UserCollector | /etc/passwd, /etc/shadow, /etc/group |
| CronCollector | Répertoires /etc/cron* et /var/spool/cron/crontabs/* |
| SystemdCollector | Sortie de systemctl list-timers et list-units |
| SSHCollector | ~/.ssh/authorized_keys pour tous les utilisateurs |
| SudoersCollector | /etc/sudoers et tous les fichiers sous /etc/sudoers.d/ |
| EnvironmentCollector | /etc/environment, /etc/profile.d/*, fichiers d'initialisation du shell utilisateur |
| PackageCollector | Intégrité des paquets système via dpkg --verify, attributs de flag immuable via lsattr, et binaires setuid/setgid via find |
| PamCollector | Fichiers /etc/pam.d/* et /etc/nsswitch.conf |
| KernelCollector | Modules noyau chargés via lsmod |
PackageCollector nécessite trois outils Ubuntu standard sur l'hôte actif (dpkg, lsattr, find — tous présents sur les installations Ubuntu standard). Si une commande est indisponible, PackageCollector produit gracieusement des données vides pour cette portion plutôt que de planter. L'analyse hors ligne (analyze BUNDLE) rejoue la sortie de commande capturée au moment de collect, donc la disponibilité des commandes sur l'hôte de l'analyseur n'est pas requise.
Le scan find setuid/setgid passe -xdev pour rester borné — il ne descend pas dans les systèmes de fichiers montés séparément (un montage distinct sous /opt, un /home monté en NFS, etc.). C'est un compromis délibéré entre temps d'exécution et couverture : sans -xdev, le scan pourrait se bloquer en scannant des montages réseau ou des systèmes de fichiers virtuels. Si votre environnement monte ces chemins sur des systèmes de fichiers séparés, sachez qu'ils ne seront pas scannés.
dpkg --verify et le scan find setuid/setgid utilisent des timeouts non-défaut généreux (10 minutes et 5 minutes respectivement, voir ubuntils/collectors/packages.py) car les deux peuvent légitimement dépasser largement la valeur par défaut de la bibliothèque de 30 secondes sur un hôte réel avec une grande base de données de paquets ou une grande arborescence de fichiers ; ubuntils collect utilise les mêmes timeouts lors de la capture de ces commandes dans un bundle.
| Pris en charge | |
|---|---|
| Ubuntu | 20.04, 22.04, 24.04 |
| Architecture | amd64, arm64 |
| Python | 3.9+ |
| Privilèges | Root requis pour un accès complet aux artefacts |
Une exécution sans root produit un scan partiel avec des avertissements. Les chemins critiques comme /etc/shadow, les répertoires crontab protégés et certaines entrées /proc seront ignorés.
v1.0.0
v1.1.0
--config)--output FILE pour écrire les rapports directement--sincereport_sha256, hostname, timestamp)USER_UID_ZEROv1.5.0
--rules)PROCESS_SUSPICIOUS_CONNECTION — corrélation processus↔réseau par PIDrelated_events)Les recherches de hachages VirusTotal et l'export IOC MISP ont été abandonnés dans cette version. VirusTotal ne répond que pour les hachages connus — le cas que rkhunter couvre déjà, et l'opposé du vide de techniques nouvelles que ubuntils cible — et les deux fonctionnalités auraient placé un appel réseau à l'intérieur d'un outil dont la valeur repose sur l'absence de tout appel. ubuntils lui-même n'effectue toujours aucun appel réseau (voir l'intégration Wazuh pour la seule manière, désactivable, dont les découvertes peuvent quitter l'hôte, via un agent local).
v2.0.0 — séparation collect/analyze hors ligne
ubuntils collect — acquiert un bundle inviolable (manifest.json + fichiers/commandes hachés) depuis un hôte actif, sans détectionubuntils analyze (BUNDLE | --root PATH) — exécute le même pipeline de détection/chronologie que scan contre un bundle ou une image montée, sans root requisbundle_integrity (live/ok/mismatch) exposé dans scan_metadataPROCESS_MASQUERADE, PROCESS_SUSPICIOUS_CONNECTION, et couverture réduite pour les chemins glob cron/sudoers/SSH et ExecStart des timers systemd)confidence/confidence_band/signals), suppression --baseline, remplacement des heuristiques basées uniquement sur mtime dans SSH_UNAUTHORIZED_KEY/SHELL_RC_MODIFICATION par des signaux ctime + contenu, et une véritable chronologie hors ligne pour analyze BUNDLE et analyze --rootPACKAGE_TAMPERED, IMMUTABLE_FLAG_SET, PAM_BACKDOOR, KERNEL_MODULE_SUSPICIOUS, SETUID_INVENTORY (via PackageCollector, PamCollector, KernelCollector ; tous en lecture seule par conception)v2.1.0 — transmission SIEM
ubuntils scan en direct transmises à un agent Wazuh local en JSONL, auto-détecté (aucun flag requis)<localfile> de ossec.conf (examples/wazuh/)scan en direct uniquement — ne se déclenche jamais pendant analyze hors ligne, car un bundle ou une image décrit un hôte différent de celui exécutant l'agent--no-wazuh pour exclure un scan de la transmissionv2.1.0 — durcissement (audit complet du code)
PATH de l'appelant, et les commandes se résolvent sur un chemin sécurisé fixe ; les modifications sudoers sont vérifiées par visudo avant de toucher le fichier ; écritures de remédiation atomiques ; sortie de rapport/bundle sûre vis-à-vis des liens symboliques ; bundles extraits supprimés après analyze/etc/ld.so.preload analysé, entrées cron @reboot/@daily et scripts /etc/cron.{hourly,daily,weekly,monthly}, unités systemd .service et binaires ExecStart non détenus par root, règles sudoers %group et #include/@includedir, chaque élément d'une liste LD_PRELOADUSER_EMPTY_PASSWORD (compte de connexion sans mot de passe)/usr/lib traités comme standard, KERNEL_MODULE_SUSPICIOUS rétrogradé en LOW, une découverte NSS par modulescan_metadata et la TUI ; un échec de chronologie ne rejette plus les découvertes ; les bundles altérés avertissent et sortent avec le code 3 ; année/fuseau horaire syslog gérés correctement--min-confidence (défaut 40), résultats affichés dans la TUI après scan --remediatev3.0.0 / v4.0.0 (exploratoire)
Les contributions les plus utiles actuellement sont de nouvelles règles de détection (ajoutées comme fonctions autonomes dans detectors/rules.py avec un test correspondant), des collecteurs supplémentaires pour des types d'artefacts non encore couverts, des modules de remédiation pour SUSPICIOUS_SYSTEMD_TIMER et SHELL_RC_MODIFICATION (tous deux actuellement en lecture seule par conception, mais des chemins de remédiation automatique sûrs peuvent exister), des cas de test pour des cas limites sur des configurations Ubuntu spécifiques, et des améliorations de la documentation.
Ouvrez une issue avant de commencer une contribution importante pour éviter le travail en double.
MIT
Créé par Asmit — BTech Computer Science, PES University, Bengaluru. L'outil est né de la frustration face au temps que prend le triage manuel d'Ubuntu comparé à ce qu'un script bien délimité peut automatiser.
| SHELL_RC_MODIFICATION | LOW | Non | Fichiers d'initialisation de shell (bashrc, profile, zshrc, etc.) modifiés au cours des 48 dernières heures pour tout utilisateur avec un shell de connexion |
| PACKAGE_TAMPERED | HIGH | Non | Fichiers de paquets appartenant au système modifiés, manquants, ou dont le contenu/mode/taille ne correspond pas au manifeste du paquet (via dpkg --verify) |
| IMMUTABLE_FLAG_SET | MEDIUM | Non | Attributs immuable (i) ou append-only (a) définis sur des fichiers sensibles comme /etc/passwd, /etc/sudoers ou /etc/pam.d/* (détecté via lsattr) |
| PAM_BACKDOOR | HIGH | Non | Une ligne littérale pam_permit.so dans n'importe quel fichier /etc/pam.d/*, ou un module NSS dans /etc/nsswitch.conf en dehors d'une liste autorisée (files/sss/ldap/winbind/...) |
| KERNEL_MODULE_SUSPICIOUS | LOW | Non | Modules noyau chargés en dehors d'une liste autorisée de modules intégrés courants — remarque : les hôtes riches en matériel (GPU, cartes Wi-Fi, pilotes propriétaires) produiront des faux positifs ; ajoutez les modules attendus via --config |
| SETUID_INVENTORY | LOW | Non | Binaires setuid ou setgid inattendus en dehors d'un ensemble de référence connu (les deux bits sont vérifiés et signalés séparément) |