Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
sudotimewarp-cve-2026-96512- — Reproduction and PoC scripts for CVE-2026-96512 (SudoTimeWarp), where the caller's TZ shifts sudoers NOTBEFORE/NOTAFTER windows, plus mitigation checks. | Kitploit
Tools/GitHubGitHub/ermensonx/sudotimewarp-cve-2026-96512-
Defensive ToolsPrivilege EscalationVulnerability AnalysisExploitationConfiguration AuditingPenetration TestingLearning & Education
GitHub

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
ermensonx/sudotimewarp-cve-2026-96512-

sudotimewarp-cve-2026-96512-

Reproduction and PoC scripts for CVE-2026-96512 (SudoTimeWarp), where the caller's TZ shifts sudoers NOTBEFORE/NOTAFTER windows, plus mitigation checks.

View Repository
22h 59m agoNot yet reviewed
Share

SudoTimeWarp — CVE-2026-96512

sudo: the caller's TZ decides NOTBEFORE/NOTAFTER

Reproduction material for SudoTimeWarp (CVE-2026-96512). A sudoers rule whose Date_Spec timestamp omits the trailing Z is converted by mktime(), which re-reads getenv("TZ") on every call. Because sudo is setuid-root and the caller's environ crosses execve() intact, the unprivileged caller chooses the timezone in which their own validity window is evaluated.

What this actually is

The base issue — TZ influences NOTBEFORE/NOTAFTER — was reported privately by the XlabAI Team of Tencent Xuanwu Lab, the Atuin Automated Vulnerability Discovery Engine, and Guannan Wang, Zhanpeng Liu and Guancheng Li, and credited in commit db669167c (2026-03-14).

SudoTimeWarp (CVE-2026-96512) is the finding that db669167c is incomplete. The protection it installs covers glibc's timezone cache — and with it the log timestamps — but does not reach the mktime() at gentime.c:156, where authorization is decided. On a tree that already contains that commit, the window shift remains fully reproducible. The maintainer records the point in the message of 1820a349:

the previous change "was not effective since the mktime() function re-reads the TZ environment variable each time it is called"

Precondition

A sudoers rule that (1) grants a command to the caller, (2) carries NOTBEFORE= or NOTAFTER=, and (3) writes the timestamp without a Z suffix and without an explicit offset.

Item (3) is not exotic: the sudoers manual documents the suffix-less form as a supported extension, prints 20151201235900 as one of its four example timestamps (docs/sudoers.man.in:1820), and the form appears in the project's own regression corpus (plugins/sudoers/regress/testsudoers/test13.sh).

Run it

Always in a disposable container or VM. Every script here rewrites /etc/sudoers. They back it up and restore it, but a mistake there locks you out of a real machine — the scripts refuse to run outside a container unless you pass --i-know.

root@kitploit:~
docker build -t sudotimewarp .
docker run --rm -it sudotimewarp

Or without building an image:

root@kitploit:~
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'

Expected output on an affected build:

root@kitploit:~
=== 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 is an arbitrary three-letter timezone abbreviation and 24 is a POSIX offset. No file is involved and none needs to exist — the exploitable channel is the inline POSIX string only. The tzfile form (TZ=:/tmp/evil.tz and variants) is refused by glibc's __libc_enable_secure guard under setuid and measures exactly 0 s of shift, which is what separates this from CVE-2014-9680.

Reading the probes

Lines 1–3 are controls, and they matter: a parse error would produce the same ALLOW as the bug.

poc.sh exits 0 when affected, 1 when not, 2 when the controls did not hold.

The two-part version

poc.sh uses NOPASSWD so it can run non-interactively. That is not a condition of the bug. The split scripts show the privilege boundary explicitly — part 1 does only what an administrator legitimately does, part 2 runs as the unprivileged user and uses no privilege of any kind:

root@kitploit:~
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 takes four scenarios:

With a password-requiring rule the caller still authenticates through PAM, and a wrong password still fails. This is not an authentication bypass — what moves is the authorization decision.

Scope

  • Not an authentication bypass. PAM keeps requiring and validating the password.
  • Not escalation from no privilege. A user with no sudoers rule gains nothing; the bug restores a grant that already existed. Hence PR:L, not PR:N.
  • Not unbounded in time. A rule that expired weeks ago is not reachable. Measured: four days past expiry, both maximal TZ values deny.
  • Where the dated rule grants a single non-escapable command, impact is bounded by that grant (roughly C:L/I:N/A:N). The 7.8 vector scores the case where the dated rule is broader than the caller's permanent access.

The ~25 h bound applies to the access window, not to the duration of the impact: one successful use inside it is enough to establish persistence that outlives the window.

Mitigation without upgrading

Append Z to every NOTBEFORE/NOTAFTER timestamp — it forces the timegm() branch.

root@kitploit:~
grep -rE 'NOT(BEFORE|AFTER)=' /etc/sudoers /etc/sudoers.d/

Do not audit with sudo -l. It formats every Date_Spec through gmtime() and always appends a Z (plugins/sudoers/display.c:229), so a rule written NOTAFTER=20260827221423 is displayed as NOTAFTER=20260827221423Z. The output normalises away exactly the detail that decides it. cvtsudoers and fmtsudoers behave the same way. Read /etc/sudoers directly.

References

  • Red Hat (CNA): https://access.redhat.com/security/cve/CVE-2026-96512
  • Fix commit 1820a349: https://github.com/sudo-project/sudo/commit/1820a349687522f51023d1ae5925125f59679a8c
  • oss-security: http://www.openwall.com/lists/oss-security/2026/09/24/4
  • CVE Program: https://www.cve.org/CVERecord?id=CVE-2026-96512

Disclosure

Reported to the maintainer on 2026-08-28, with no deadline. A candidate patch came back the same day; the public fix landed on 2026-08-29. CVE assigned by Red Hat, acting as CNA-LR, and published 2026-09-23. There is no embargo: everything here has been public since the fix commit.

Credit

SudoTimeWarp / CVE-2026-96512: Ermenson Junior, independent research, recorded by Red Hat as "Independent security research". The original report of the underlying issue belongs to the XlabAI Team of Tencent Xuanwu Lab, the Atuin Automated Vulnerability Discovery Engine, and Guannan Wang, Zhanpeng Liu and Guancheng Li.

Published for defensive use: verifying whether a host is affected, and validating the Z mitigation.

Download Tool
NameSudoTimeWarp
CVECVE-2026-96512
CVSS v3.1AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — 7.8 High
CWECWE-863 (registered); mechanism is CWE-807
Affectedsudo 1.8.20 through 1.9.17p2, and main before 1820a349
Fixed in1820a349 (2026-08-29)
Shiftup to 24 h 59 m 59 s per direction; total interval 49 h 59 m 58 s
LineChecks
1The rule works at all inside its window
2The denial in line 4's absence really comes from NOTAFTER
3Setting TZ is not itself the cause — TZ=UTC decides as no TZ does
4The bug: the expired rule executes as root
5The boundary: with the documented Z, the timegm() branch is taken and it dies
ScenarioPolicy
expired (default)one rule, NOTAFTER one hour in the past, zone-less
validthe same rule still inside its window — control
expired-zthe same expired rule with the documented Z — control, unaffected form
escalationa narrow permanent grant plus an expired broad one — the shape a maintenance or break-glass grant actually takes