
Reproduktions- und PoC-Skripte für CVE-2026-96512 (SudoTimeWarp), bei dem die TZ des Aufrufers die NOTBEFORE/NOTAFTER-Fenster von sudoers verschiebt, sowie Mitigationsprüfungen.
sudo: die TZ des Aufrufers entscheidet über NOTBEFORE/NOTAFTER
Reproduktionsmaterial für SudoTimeWarp (CVE-2026-96512). Eine sudoers-Regel, deren Date_Spec-Zeitstempel
das abschließende Z weglässt, wird von mktime() umgewandelt, das bei jedem Aufruf getenv("TZ") erneut liest.
Da sudo setuid-root ist und die environ des Aufrufers execve() unverändert überquert, wählt der
unprivilegierte Aufrufer die Zeitzone, in der sein eigener Gültigkeitsbereich ausgewertet wird.
| Name | 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 High |
| CWE | CWE-863 (registriert); Mechanismus ist CWE-807 |
| Betroffen | sudo 1.8.20 bis 1.9.17p2, und main vor 1820a349 |
| Behoben in | 1820a349 (2026-08-29) |
| Verschiebung | bis zu 24 h 59 m 59 s pro Richtung; Gesamtintervall 49 h 59 m 58 s |
Das Grundproblem — TZ beeinflusst NOTBEFORE/NOTAFTER — wurde privat gemeldet vom
XlabAI Team of Tencent Xuanwu Lab, der Atuin Automated Vulnerability Discovery
Engine und Guannan Wang, Zhanpeng Liu und Guancheng Li und in Commit
db669167c (2026-03-14) gewürdigt.
SudoTimeWarp (CVE-2026-96512) ist die Erkenntnis, dass db669167c unvollständig ist. Der Schutz, den er
installiert, deckt glibcs Zeitzonen-Cache ab — und damit die Log-Zeitstempel — erreicht aber nicht
das mktime() in gentime.c:156, wo über die Autorisierung entschieden wird. Auf einem Tree, der
diesen Commit bereits enthält, bleibt die Fensterverschiebung vollständig reproduzierbar. Der Maintainer
hält den Punkt in der Nachricht von 1820a349 fest:
die vorherige Änderung „war nicht wirksam, da die mktime()-Funktion die TZ Umgebungsvariable bei jedem Aufruf erneut liest“
Eine sudoers-Regel, die (1) dem Aufrufer einen Befehl gewährt, (2) NOTBEFORE= oder
NOTAFTER= trägt und (3) den Zeitstempel ohne Z-Suffix und ohne expliziten
Offset schreibt.
Punkt (3) ist nicht exotisch: Das sudoers-Handbuch dokumentiert die suffixlose Form als unterstützte
Erweiterung, gibt 20151201235900 als einen seiner vier Beispiel-Zeitstempel an
(docs/sudoers.man.in:1820), und die Form erscheint im eigenen Regression-Korpus des Projekts
(plugins/sudoers/regress/testsudoers/test13.sh).
Immer in einem Wegwerf-Container oder einer VM. Jedes Skript hier schreibt /etc/sudoers um.
Sie sichern sie und stellen sie wieder her, aber ein Fehler dort sperrt Sie aus einer echten Maschine aus — die
Skripte weigern sich, außerhalb eines Containers zu laufen, es sei denn, Sie übergeben --i-know.
docker build -t sudotimewarp .
docker run --rm -it sudotimewarp
Oder ohne ein Image zu bauen:
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'
Erwartete Ausgabe auf einem betroffenen Build:
=== 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 ist eine beliebige dreistellige Zeitzonen-Abkürzung und 24 ist ein POSIX-Offset. Es ist keine
Datei beteiligt und keine muss existieren — der ausnutzbare Kanal ist ausschließlich der Inline-POSIX-
String. Die tzfile-Form (TZ=:/tmp/evil.tz und Varianten) wird von glibcs
__libc_enable_secure-Guard unter setuid abgelehnt und misst exakt 0 s Verschiebung, was dies
von CVE-2014-9680 unterscheidet.
Zeilen 1–3 sind Kontrollen, und sie sind wichtig: Ein Parse-Fehler würde dasselbe ALLOW erzeugen wie der Bug.
| Zeile | Prüft |
|---|---|
| 1 | Die Regel funktioniert überhaupt innerhalb ihres Fensters |
| 2 | Die Verweigerung in Zeile 4 kommt wirklich von NOTAFTER |
| 3 | Das Setzen von TZ ist nicht selbst die Ursache — TZ=UTC entscheidet wie kein TZ |
| 4 | Der Bug: die abgelaufene Regel wird als root ausgeführt |
| 5 | Die Grenze: mit dem dokumentierten Z wird der timegm()-Zweig genommen und es stirbt |
poc.sh beendet mit 0, wenn betroffen, 1, wenn nicht, 2, wenn die Kontrollen nicht hielten.
poc.sh verwendet NOPASSWD, damit es nicht-interaktiv laufen kann. Das ist keine Bedingung des
Bugs. Die geteilten Skripte zeigen die Privilegiengrenze explizit — Teil 1 tut nur, was
ein Administrator legitim tut, Teil 2 läuft als unprivilegierter Benutzer und nutzt keinerlei
Privileg:
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 kennt vier Szenarien:
| Szenario | Policy |
|---|---|
expired (Standard) | eine Regel, NOTAFTER eine Stunde in der Vergangenheit, zonenlos |
valid | dieselbe Regel noch innerhalb ihres Fensters — Kontrolle |
expired-z | dieselbe abgelaufene Regel mit dem dokumentierten Z — Kontrolle, nicht betroffene Form |
escalation | eine enge dauerhafte Gewährung plus eine abgelaufene breite — die Form, die eine Wartungs- oder Break-Glass-Gewährung tatsächlich annimmt |
Bei einer passwortverlangenden Regel authentifiziert sich der Aufrufer weiterhin über PAM, und ein falsches Passwort schlägt weiterhin fehl. Dies ist kein Authentication Bypass — was sich verschiebt, ist die Autorisierungsentscheidung.
PR:L, nicht PR:N.TZ-Werte.C:L/I:N/A:N). Der 7.8-Vektor bewertet den Fall, in dem die datierte Regel
breiter ist als der dauerhafte Zugriff des Aufrufers.Die ~25-h-Grenze gilt für das Zugriffsfenster, nicht für die Dauer der Auswirkung: eine erfolgreiche Nutzung darin genügt, um Persistenz zu etablieren, die das Fenster überlebt.
Hängen Sie Z an jeden NOTBEFORE/NOTAFTER-Zeitstempel an — das erzwingt den timegm()-Zweig.
grep -rE 'NOT(BEFORE|AFTER)=' /etc/sudoers /etc/sudoers.d/
Auditieren Sie nicht mit sudo -l. Es formatiert jedes Date_Spec durch gmtime() und hängt immer
ein Z an (plugins/sudoers/display.c:229), sodass eine als
NOTAFTER=20260827221423 geschriebene Regel als NOTAFTER=20260827221423Z angezeigt wird. Die Ausgabe normalisiert
genau das Detail weg, das entscheidet. cvtsudoers und fmtsudoers verhalten sich gleich.
Lesen Sie /etc/sudoers direkt.
1820a349: https://github.com/sudo-project/sudo/commit/1820a349687522f51023d1ae5925125f59679a8cAm 2026-08-28 an den Maintainer gemeldet, ohne Frist. Noch am selben Tag kam ein Kandidaten-Patch zurück; der öffentliche Fix landete am 2026-08-29. CVE von Red Hat in der Rolle als CNA-LR vergeben und am 2026-09-23 veröffentlicht. Es gibt keine Embargo: alles hier ist seit dem Fix-Commit öffentlich.