système : OPÉRATIONNEL
← retour à tous les hacks
SUPPLY CHAIN MEDIUM NEW

Les fichiers GGUF sont des entrées non fiables : les RCE récurrentes du parseur de llama.cpp

CVE-2026-33298 (mars 2026) et une divulgation oss-sec du 15 mai 2026 montrent que le parseur GGUF de llama.cpp enchaîne les corruptions de tas par dépassement d'entier : charger un modèle piégé peut suffire à exécuter du code.

2026-06-05 // 6 min affects: llama.cpp, ggml, gguf, ollama, lm-studio

En bref Le format de fichier qui fait tourner les LLM en local — GGUF — est analysé par du C/C++ sans garde-fou mémoire dans llama.cpp et son cœur ggml. CVE-2026-33298, publiée le 18 mars 2026 (CVSS 7.8), est un dépassement d’entier dans le calcul de la taille des tenseurs : un fichier de modèle piégé peut corrompre le tas et, potentiellement, exécuter du code. Ce n’est pas la première : CVE-2025-53630 (juillet 2025) relevait de la même classe, et un avis de full disclosure daté du 15 mai 2026 recense six autres faiblesses du parseur GGUF. La leçon est structurelle : un fichier de modèle est une entrée non fiable, et le chargeur est une surface d’attaque.

De quoi s’agit-il ?

GGUF est le format conteneur de facto des grands modèles de langage exécutés en local. Lorsque vous récupérez un modèle quantisé sur Hugging Face et que vous le lancez avec llama.cpp, Ollama, LM Studio ou tout outil bâti sur la bibliothèque ggml, ce fichier .gguf est analysé par du code C/C++ sur votre machine avant toute inférence.

CVE-2026-33298, divulguée dans l’avis de sécurité GitHub GHSA-96jg-mvhq-q7q7 le 18 mars 2026 (CWE-122 / CWE-190, CVSS 7.8, AV:L/AC:L/PR:N/UI:R), est un dépassement d’entier dans la fonction ggml_nbytes — la routine qui calcule le nombre d’octets nécessaires à un tenseur. Un fichier aux dimensions de tenseur forgées fait « déborder » ce calcul, et le parseur alloue un tampon bien plus petit que la taille réelle du tenseur. Ce qui ressemble à un bug arithmétique devient une corruption mémoire.

La vraie histoire, c’est la récurrence. La même classe de dépassement avait touché gguf_init_from_file_impl un an plus tôt avec CVE-2025-53630 (publiée le 10 juillet 2025), et le 15 mai 2026, un chercheur indépendant a publié un avis de full disclosure sur oss-sec décrivant six faiblesses supplémentaires dans gguf.cpp et la référence Python gguf_reader.py — depuis l’absence de borne supérieure sur la valeur d’alignement jusqu’à des champs d’énumération et de longueur de chaîne non vérifiés.

Comment ça marche

Le schéma est le même pour toutes ces failles. Une taille ou un décalage est calculé à partir de valeurs lues directement dans le fichier contrôlé par l’attaquant. Sur un hôte 64 bits, multiplier de grandes dimensions de tenseur fait déborder size_t qui retombe sur un petit nombre ; le chargeur fait ensuite confiance à ce petit nombre pour dimensionner une allocation sur le tas. Quand le code copie ou indexe ensuite le tenseur à son étendue réelle (bien plus grande) — ou suit un offset de tenseur qui pointe au-delà du tampon sous-dimensionné — il lit ou écrit hors de l’allocation. Cette écriture hors limites sur le tas est la primitive sur laquelle un attaquant s’appuie pour corrompre les structures adjacentes et, dans le pire des cas, détourner l’exécution.

Le vecteur CVSS compte pour le tri : l’attaque est locale avec interaction utilisateur requise (UI:R). Personne n’atteint votre processus à distance — le déclencheur, c’est vous qui chargez le fichier. Mais dans l’univers des LLM locaux, télécharger la quantisation GGUF d’un inconnu et la charger est un geste quotidien, souvent automatisé par un simple pull. Le fichier est traité comme une donnée inerte ; le parseur, lui, prend ses champs pour argent comptant.

Pourquoi c’est important

ggml et llama.cpp se trouvent sous une large part de l’écosystème de l’IA locale — Ollama, LM Studio, GPT4All et bien d’autres outils embarquent le même code d’analyse. Une faiblesse dans l’analyse GGUF n’est donc pas le bug d’un produit, mais celui d’une dépendance partagée, héritée par tout l’aval. Et comme les hubs de modèles communautaires rendent triviale la publication et le partage de fichiers GGUF, le canal de distribution d’un modèle malveillant est déjà en place et largement digne de confiance aux yeux des utilisateurs.

Cela reflète la leçon ancienne des checkpoints Python à base de pickle : les artefacts de modèle sont proches du code, ce ne sont pas des données passives. Les efforts de format « sûr par construction » (comme éviter pickle) répondent au problème de la désérialisation de code, mais ils ne rendent pas pour autant le parseur binaire lui-même sûr en mémoire. CVE-2026-33298 et ses cousines montrent qu’un format pourtant « données seulement » devient dangereux dès lors que son parseur compte mal les octets.

Défenses

  1. Mettez à jour. CVE-2026-33298 est corrigée dans la build llama.cpp b7824. Si vous distribuez ou embarquez ggml/llama.cpp, repartez d’une release récente et suivez ses avis de sécurité — cette classe se répète, mieux vaut épingler et surveiller que corriger une fois pour toutes.
  2. Traitez GGUF comme non fiable. Ne chargez des modèles que d’éditeurs de confiance ; vérifiez sommes de contrôle et provenance avant tout chargement. La quantisation communautaire d’un uploadeur inconnu mérite la même méfiance qu’un binaire non signé.
  3. Isolez le chargeur. Faites tourner le chargement de modèle et l’inférence dans un conteneur minimal, sans accès aux secrets ni aux données sensibles et avec un trafic sortant restreint. Imposez des limites mémoire et CPU pour qu’un dépassement ou un épuisement mémoire dégénère en crash, pas en pivot.
  4. Validez avant de charger quand c’est possible. Privilégiez des chargeurs maintenus qui vérifient les bornes des tailles de tenseur, des décalages, de l’alignement et des longueurs de chaîne par rapport à la taille réelle du fichier. Si vous bâtissez du tooling sur ggml, fuzzez le chemin GGUF et utilisez de l’arithmétique vérifiée (p. ex. __builtin_mul_overflow) sur chaque calcul de taille.
  5. Surveillez. Segfaults, abandons de sanitizer ou pics mémoire inattendus pendant le chargement d’un modèle sont un signal de chasse utile : un fichier a peut-être cherché à casser le parseur plutôt qu’à être servi par lui.

Statut

ÉlémentRéférenceDateNotes
CVE-2026-33298GHSA-96jg-mvhq-q7q72026-03-18Dépassement d’entier dans ggml_nbytes, débordement de tas, CVSS 7.8, corrigé en b7824
Avis GGUF oss-secseclists.org (V-01…V-06)2026-05-15Six autres faiblesses du parseur gguf.cpp / gguf_reader.py, full disclosure
CVE-2025-53630GHSA-vgg9-87g3-85w82025-07-10Même classe de dépassement dans gguf_init_from_file_impl, lecture/écriture hors limites

Le bon cadrage n’est pas « corriger un CVE ». C’est que l’analyse GGUF dans ggml/llama.cpp a produit à plusieurs reprises la même classe « dépassement d’entier → corruption de tas » — donc toute pile qui charge des fichiers de modèle depuis l’extérieur de sa frontière de confiance devrait ajouter des contrôles de provenance et de l’isolation qui ne dépendent pas du parseur calculant chaque taille correctement.

Sources