CVE, SBOM & chaîne d'approvisionnementintermédiaire6 min de lecture

Trouver les CVE dans vos paquets Ubuntu et Debian avec OSV.dev — sans Nessus

Un script de quarante lignes qui confronte chaque paquet installé à l'API gratuite OSV.dev, en tenant compte des backports, et enrichit la liste avec le score EPSS et le statut CISA KEV. De 1 800 paquets aux cinq à corriger cette semaine. Sans licence, sans appliance de scan.

Sommaire
  1. Étape 1 : l'inventaire tel qu'OSV le veut
  2. Étape 2 : interroger OSV.dev par lots
  3. Étape 3 : de l'identifiant OSV à la CVE, la sévérité et la version corrigée
  4. Étape 4 : prioriser avec EPSS et KEV
  5. Étape 5 : automatiser et conserver
  6. Pièges
  7. Ce qu'il vous manque encore
  8. Comment monsys fait
  9. FAQ

Un scanner de vulnérabilités qui voit nginx 1.24.0 et rapporte 14 CVE a généralement tort : Ubuntu et Debian rétroportent les correctifs dans la version existante, et 1.24.0-2ubuntu7.3 contient le patch que le scanner rate. Résultat : un rapport de centaines de lignes dont personne ne sait lesquelles sont réelles. Cet article fait l'inverse : partir de ce que dpkg sait, interroger OSV.dev — qui utilise les Ubuntu Security Notices et le Debian Security Tracker comme source — et n'enrichir que ce qui reste.

Étape 1 : l'inventaire tel qu'OSV le veut

OSV connaît les paquets Ubuntu et Debian sous le nom du paquet source, avec la chaîne d'écosystème Ubuntu:24.04 ou Debian:12. Le nom binaire (libssl3t64) n'est pas ce que vous voulez ; le paquet source (openssl), si. dpkg-query peut le fournir directement :

# Ubuntu 24.04 → "Ubuntu:24.04" ; Debian 12 → "Debian:12"
. /etc/os-release
case "$ID" in
  ubuntu) ECO="Ubuntu:$VERSION_ID" ;;
  debian) ECO="Debian:$VERSION_ID" ;;
  *) echo "unsupported: $ID"; exit 1 ;;
esac

# paquet source + version, dédupliqué (une source donne souvent 5 binaires)
dpkg-query -W -f='${source:Package} ${source:Version}\n' | sort -u > /tmp/pkgs.txt
wc -l /tmp/pkgs.txt      # typiquement 400-900 paquets source sur un serveur

Attention à la version : 1:2.4.7-1ubuntu5 — ce 1: est l'epoch et en fait partie. OSV compare exactement selon les règles de version Debian ; laissez-le.

Étape 2 : interroger OSV.dev par lots

Le point de terminaison https://api.osv.dev/v1/querybatch accepte jusqu'à 1 000 requêtes par appel, sans clé API. La réponse contient, par requête, la liste des identifiants OSV (UBUNTU-CVE-…, USN-…, DSA-…, DLA-…) pour lesquels votre version est encore vulnérable — un backport corrigé ne compte pas, car l'USN indique quelle version contient le correctif.

sudo apt install -y jq curl
# Construire le corps JSON (max 1000 par lot ; on découpe)
split -l 900 /tmp/pkgs.txt /tmp/pkgbatch.
: > /tmp/osv-raw.jsonl
for f in /tmp/pkgbatch.*; do
  jq -Rn --arg eco "$ECO" '
    {queries: [inputs | split(" ") | {package: {name: .[0], ecosystem: $eco}, version: .[1]}]}' "$f" \
  | curl -s -X POST https://api.osv.dev/v1/querybatch -H 'Content-Type: application/json' -d @- \
  | jq -c --slurpfile q <(jq -Rn '[inputs | split(" ")]' "$f") '
      .results | to_entries[] | select(.value.vulns != null)
      | {pkg: $q[0][.key][0], version: $q[0][.key][1], vulns: [.value.vulns[].id]}' \
  >> /tmp/osv-raw.jsonl
done
jq -r '"\(.pkg) \(.version) \(.vulns|length)"' /tmp/osv-raw.jsonl | sort -k3 -rn | head -20

Sur un serveur Ubuntu 24.04 à jour, la liste est courte : une poignée de paquets pour lesquels Ubuntu n'a pas encore publié de correctif (ou qui sont dans universe, sans support de sécurité). Sur un serveur non patché depuis trois mois, elle est longue. Les deux sont exactement la réponse que vous voulez.

Étape 3 : de l'identifiant OSV à la CVE, la sévérité et la version corrigée

Un identifiant USN ou UBUNTU-CVE ne dit rien sur la gravité. Récupérez les détails par vulnérabilité — le numéro CVE (dans les alias, ou dans l'identifiant lui-même pour UBUNTU-CVE-…), la sévérité, et la version qui corrige :

: > /tmp/osv-detail.jsonl
for id in $(jq -r '.vulns[]' /tmp/osv-raw.jsonl | sort -u); do
  curl -s "https://api.osv.dev/v1/vulns/$id" | jq -c --arg eco "$ECO" '{
    id,
    cve: (((.aliases // []) + [.id | ltrimstr("UBUNTU-")]) | map(select(startswith("CVE-"))) | first),
    severity: ((.database_specific.severity // .severity[0].score // "unknown") | tostring),
    fixed: ([ .affected[] | select(.package.ecosystem == $eco) | .ranges[]?.events[]? | .fixed? // empty ] | first),
    summary }' >> /tmp/osv-detail.jsonl
  sleep 0.1   # rester poli ; OSV n'a pas de limite stricte mais a une équipe ops
done
jq -r '[.id, (.cve // "-"), .severity, (.fixed // "no fix")] | @tsv' /tmp/osv-detail.jsonl | column -t

Le champ fixed est la clé : s'il y a une version, apt upgrade est la solution. S'il dit no fix, c'est une vulnérabilité ouverte pour laquelle vous devez documenter une atténuation ou une acceptation du risque.

Étape 4 : prioriser avec EPSS et KEV

Une liste de 60 CVE n'est toujours pas un plan de travail. Deux sources gratuites en font un :

  • EPSS (FIRST.org) : la probabilité qu'une CVE soit exploitée dans les 30 prochains jours, de 0 à 1. Au-dessus de 0,1, c'est exceptionnel.
  • KEV (CISA Known Exploited Vulnerabilities) : les CVE dont l'exploitation active est confirmée. Si une CVE y figure, la discussion est close.
# KEV : un JSON, rafraîchi chaque jour
curl -s https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json \
  | jq -r '.vulnerabilities[].cveID' | sort -u > /tmp/kev.txt

# EPSS : lot de max 100 CVE par appel
jq -r '.cve // empty' /tmp/osv-detail.jsonl | sort -u > /tmp/cves.txt
: > /tmp/epss.tsv
split -l 100 /tmp/cves.txt /tmp/cvebatch.
for f in /tmp/cvebatch.*; do
  curl -s "https://api.first.org/data/v1/epss?cve=$(paste -sd, "$f")" \
    | jq -r '.data[] | [.cve, .epss] | @tsv' >> /tmp/epss.tsv
done

# Fusion : CVE, EPSS, KEV, paquet, correctif
jq -r 'select(.cve) | [.cve, .id, (.fixed // "no fix")] | @tsv' /tmp/osv-detail.jsonl \
| while IFS=$'\t' read -r cve osv fix; do
    epss=$(awk -v c="$cve" '$1==c{print $2}' /tmp/epss.tsv); epss=${epss:-0}
    kev=$(grep -qx "$cve" /tmp/kev.txt && echo KEV || echo -)
    printf '%s\t%s\t%s\t%s\t%s\n' "$cve" "$epss" "$kev" "$osv" "$fix"
  done | sort -k3,3r -k2,2gr | column -t

Le tri est délibéré : d'abord tout ce qui est dans KEV, puis par EPSS décroissant. Les cinq premières lignes sont votre semaine. Les quarante dernières, vous pouvez les laisser sereinement jusqu'au prochain cycle de correctifs — et c'est une décision que vous pouvez expliquer en audit.

Étape 5 : automatiser et conserver

Mettez les étapes 1 à 4 dans un seul script, lancez-le chaque semaine et conservez la sortie avec la date. Deux raisons : vous voulez voir depuis quand une CVE est ouverte (pour la question MTTR d'un auditeur), et vous voulez savoir si quelque chose de nouveau est apparu ou si la liste s'est seulement déplacée.

sudo install -d /var/lib/cve-scan
echo '15 6 * * 1 root /usr/local/sbin/cve-scan.sh > /var/lib/cve-scan/$(date +\%F).tsv 2>/var/lib/cve-scan/last-error.log' \
  | sudo tee /etc/cron.d/cve-scan

# Quoi de neuf depuis la semaine dernière ?
comm -13 <(cut -f1 /var/lib/cve-scan/2026-09-08.tsv | sort) <(cut -f1 /var/lib/cve-scan/2026-09-15.tsv | sort)

Pièges

  • universe n'a pas de support de sécurité sur Ubuntu. Les paquets de universe (vérifiez avec apt-cache policy <pkg>) ne reçoivent pas d'USN, donc OSV voit « pas de correctif » alors qu'upstream a corrigé depuis longtemps. Ubuntu Pro (gratuit jusqu'à 5 machines) couvre universe via ESM.
  • Le tracker Debian connaît <unfixed> et <no-dsa>. Une CVE en no-dsa a été jugée à faible risque par Debian et ne sera corrigée qu'à la prochaine point release. À ne pas ignorer, mais à pondérer autrement.
  • Plusieurs versions du même paquet source. Après une mise à niveau partielle, vous pouvez avoir libssl3t64 en nouvelle version et openssl en ancienne. sort -u sur source+version montre les deux ; apt list --upgradable dit pourquoi.
  • Vous ne voyez pas les paquets snap et pip. dpkg ignore tout de snap list ou pip list. Les dépendances applicatives (npm, pip, composer) nécessitent un scan séparé avec les écosystèmes npm, PyPI, Packagist.
  • Le noyau est une histoire à part. linux-image-* est dans la liste, mais la question « est-ce que je fais tourner le noyau corrigé » concerne uname -r, pas dpkg. Voir CVE noyau et backports.

Ce qu'il vous manque encore

  • La vue de flotte. Trente serveurs, ce sont trente fichiers .tsv. « Quels hôtes ont encore CVE-2026-1234 ouverte ? » est un grep -l sur ssh.
  • Le contexte. Une CVE critique dans libxml2 sur un serveur de build interne pèse autrement que la même CVE sur le reverse proxy exposé sur le port 443. Le script ne sait pas quel hôte est exposé à internet.
  • L'historique et la preuve. Les tsv vous donnent « depuis quand », mais pas sous une forme qu'un auditeur accepte (signée, immuable, avec la décision « risque accepté, parce que… » attachée).

Comment monsys fait

L'agent monsys envoie l'inventaire des paquets (paquet source + version) au hub ; le worker CVE des paquets OS le confronte à OSV.dev et enrichit chaque correspondance avec EPSS et KEV, exactement comme ci-dessus. S'y ajoute ce qu'un script ne peut pas faire : une distance en sauts vers internet issue de la topologie réseau (une CVE sur un hôte exposé est classée plus haut), un journal de résolution qui enregistre quand une CVE a disparu pour rendre le MTTR mesurable, et un audit pack signé chaque mois. Pour les paquets que vous ne corrigez délibérément pas, vous enregistrez une déclaration VEX — datée.

FAQ

OSV.dev est-il fiable pour Ubuntu et Debian ?

Oui. OSV importe directement les Ubuntu Security Notices et le Debian Security Tracker, avec les versions corrigées. Ce sont les mêmes données que ubuntu-security-status et debsecan utilisent, mais via une seule API pour les deux distributions.

Pourquoi mon scanner rapporte-t-il plus de CVE que ce script ?

Parce que la plupart des scanners comparent la version upstream (nginx 1.24.0) et ne savent pas que 1.24.0-2ubuntu7.3 contient un backport du correctif. C'est un faux positif dû à l'absence de connaissance des backports ; OSV a cette connaissance parce que la distribution la fournit elle-même.

À quelle fréquence dois-je lancer cela ?

Hebdomadaire est un bon minimum pour une flotte de serveurs ; KEV est mis à jour chaque jour, donc lors d'une actualité critique (une nouvelle CVE OpenSSH ou noyau), vous le lancez à la demande. Conservez chaque exécution — l'historique vaut plus que le dernier état.

Rédigé par l'équipe monsys — des sysadmins qui font cela tous les jours.

Fait à la main ? Laissez monsys s'en charger en continu.

Tout ce que contient ce guide tourne dans monsys comme contrôle permanent, avec historique, alertes et preuves d'audit. 5 serveurs gratuits, hébergés en Belgique, installés en 60 secondes.