Scripts de reproduction et de PoC pour CVE-2026-96512 (SudoTimeWarp), où le TZ de l'appelant décale les fenêtres NOTBEFORE/NOTAFTER de sudoers, ainsi que des vérifications d'atténuation.
sudo : le TZ de l'appelant décide de NOTBEFORE/NOTAFTER
Matériel de reproduction pour SudoTimeWarp (CVE-2026-96512). Une règle sudoers dont l'horodatage Date_Spec omet
le Z final est convertie par mktime(), qui relit getenv("TZ") à chaque appel.
Comme sudo est setuid-root et que l'environ de l'appelant traverse execve() intact, l'appelant
non privilégié choisit le fuseau horaire dans lequel sa propre fenêtre de validité est évaluée.
| Nom | SudoTimeWarp |
| CVE | CVE-2026-96512 |
| CVSS v3.1 | AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — 7.8 Élevé |
| CWE | CWE-863 (enregistré) ; le mécanisme est CWE-807 |
| Affecté | sudo 1.8.20 jusqu'à 1.9.17p2, et main avant 1820a349 |
| Corrigé dans | 1820a349 (2026-08-29) |
| Décalage | jusqu'à 24 h 59 m 59 s par direction ; intervalle total 49 h 59 m 58 s |
Le problème de base — TZ influence NOTBEFORE/NOTAFTER — a été signalé en privé par
l'XlabAI Team of Tencent Xuanwu Lab, l'Atuin Automated Vulnerability Discovery
Engine, et Guannan Wang, Zhanpeng Liu et Guancheng Li, et crédité dans le commit
db669167c (2026-03-14).
SudoTimeWarp (CVE-2026-96512) est la découverte que db669167c est incomplet. La protection qu'il
installe couvre le cache de fuseaux horaires de glibc — et avec lui les horodatages des journaux — mais n'atteint pas
le mktime() à gentime.c:156, où l'autorisation est décidée. Sur un arbre qui
contient déjà ce commit, le décalage de fenêtre reste entièrement reproductible. Le mainteneur
consigne ce point dans le message de 1820a349 :
le changement précédent « n'était pas efficace puisque la fonction mktime() relit la variable d'environnement TZ à chaque appel »
Une règle sudoers qui (1) accorde une commande à l'appelant, (2) porte NOTBEFORE= ou
NOTAFTER=, et (3) écrit l'horodatage sans suffixe Z et sans décalage
explicite.
Le point (3) n'est pas exotique : le manuel sudoers documente la forme sans suffixe comme une extension
prise en charge, affiche 20151201235900 comme l'un de ses quatre horodatages d'exemple
(docs/sudoers.man.in:1820), et la forme apparaît dans le corpus de régression du projet lui-même
(plugins/sudoers/regress/testsudoers/test13.sh).
Toujours dans un conteneur ou une VM jetable. Chaque script ici réécrit /etc/sudoers.
Ils le sauvegardent et le restaurent, mais une erreur là-dedans vous enferme hors d'une vraie machine — les
scripts refusent de s'exécuter hors d'un conteneur sauf si vous passez --i-know.
docker build -t sudotimewarp .
docker run --rm -it sudotimewarp
Ou sans construire d'image :
docker run --rm -it -v "$PWD:/m" debian:trixie bash -c \
'apt-get update >/dev/null && apt-get install -y sudo >/dev/null && bash /m/poc.sh'
Sortie attendue sur une version affectée :
=== probes ===
rule valid, no TZ : uid=0(root) gid=0(root) groups=0(root)
rule expired, no TZ : sudo: a password is required
rule expired, TZ=UTC : sudo: a password is required
rule expired, TZ=XXX24 : uid=0(root) gid=0(root) groups=0(root)
expired + 'Z', TZ=XXX24 : sudo: a password is required
XXX est une abréviation de fuseau horaire arbitraire de trois lettres et 24 est un décalage POSIX. Aucun
fichier n'est impliqué et aucun n'a besoin d'exister — le canal exploitable est uniquement la chaîne POSIX
en ligne. La forme tzfile (TZ=:/tmp/evil.tz et variantes) est refusée par la garde
__libc_enable_secure de glibc sous setuid et mesure exactement 0 s de décalage, ce qui
distingue ceci de CVE-2014-9680.
Les lignes 1–3 sont des témoins, et ils comptent : une erreur d'analyse produirait le même ALLOW que le bug.
| Ligne | Vérifie |
|---|---|
| 1 | La règle fonctionne du tout dans sa fenêtre |
| 2 | Le refus en l'absence de la ligne 4 provient bien de NOTAFTER |
| 3 | Définir TZ n'est pas en soi la cause — TZ=UTC décide comme sans TZ |
| 4 | Le bug : la règle expirée s'exécute en root |
| 5 | La frontière : avec le Z documenté, la branche timegm() est prise et il meurt |
poc.sh sort 0 si affecté, 1 sinon, 2 si les témoins n'ont pas tenu.
poc.sh utilise NOPASSWD pour pouvoir s'exécuter sans interaction. Ce n'est pas une condition du
bug. Les scripts séparés montrent explicitement la frontière de privilèges — la partie 1 ne fait que ce
qu'un administrateur fait légitimement, la partie 2 s'exécute en tant qu'utilisateur non privilégié et n'utilise aucun
privilège d'aucune sorte :
bash repro-admin.sh escalation # as root: writes the policy
su - poc -c 'bash /poc/repro-attacker.sh'
bash repro-admin.sh --cleanup
repro-admin.sh prend quatre scénarios :
| Scénario | Politique |
|---|---|
expired (par défaut) | une règle, NOTAFTER une heure dans le passé, sans zone |
valid | la même règle encore dans sa fenêtre — témoin |
expired-z | la même règle expirée avec le Z documenté — témoin, forme non affectée |
escalation | une autorisation permanente étroite plus une large expirée — la forme que prend réellement une autorisation de maintenance ou d'urgence |
Avec une règle exigeant un mot de passe, l'appelant s'authentifie toujours via PAM, et un mauvais mot de passe échoue toujours. Ce n'est pas un contournement d'authentification — ce qui bouge est la décision d'autorisation.
PR:L, pas PR:N.TZ maximales refusent.C:L/I:N/A:N). Le vecteur 7.8 note le cas où la règle datée est
plus large que l'accès permanent de l'appelant.La borne d'environ 25 h s'applique à la fenêtre d'accès, pas à la durée de l'impact : une utilisation réussie à l'intérieur suffit à établir une persistance qui survit à la fenêtre.
Ajoutez Z à chaque horodatage NOTBEFORE/NOTAFTER — cela force la branche timegm().
grep -rE 'NOT(BEFORE|AFTER)=' /etc/sudoers /etc/sudoers.d/
N'auditez pas avec sudo -l. Il formate chaque Date_Spec via gmtime() et ajoute toujours
un Z (plugins/sudoers/display.c:229), donc une règle écrite
NOTAFTER=20260827221423 est affichée comme NOTAFTER=20260827221423Z. La sortie normalise
exactement le détail qui la décide. cvtsudoers et fmtsudoers se comportent de la même manière.
Lisez /etc/sudoers directement.
1820a349 : https://github.com/sudo-project/sudo/commit/1820a349687522f51023d1ae5925125f59679a8cSignalé au mainteneur le 2026-08-28, sans échéance. Un correctif candidat est revenu le même jour ; la correction publique est arrivée le 2026-08-29. CVE attribué par Red Hat, agissant comme CNA-LR, et publié le 2026-09-23. Il n'y a pas d'embargo : tout ici est public depuis le commit de correction.
SudoTimeWarp / CVE-2026-96512 : Ermenson Junior, recherche indépendante, enregistrée par Red Hat comme « Independent security research ». Le rapport original du problème sous-jacent appartient à l'XlabAI Team of Tencent Xuanwu Lab, l'Atuin Automated Vulnerability Discovery Engine, et Guannan Wang, Zhanpeng Liu et Guancheng Li.
Publié pour un usage défensif : vérifier si un hôte est affecté, et valider l'atténuation
Z.