
نصوص إعادة الإنتاج وPoC لـ CVE-2026-96512 (SudoTimeWarp)، حيث يؤدي تغيير TZ الخاص بالمستدعي إلى إزاحة نوافذ NOTBEFORE/NOTAFTER في sudoers، بالإضافة إلى فحوصات التخفيف.
sudo: المنطقة الزمنية TZ الخاصة بالمستدعي تحدد NOTBEFORE/NOTAFTER
مواد إعادة الإنتاج لـ SudoTimeWarp (CVE-2026-96512). قاعدة sudoers التي يحذف فيها الطابع الزمني Date_Spec اللاحقة
Z يتم تحويلها بواسطة mktime()، التي تعيد قراءة getenv("TZ") في كل استدعاء.
ولأن sudo يعمل بـ setuid-root وبيئة المستدعي environ تعبر execve() سليمة، فإن
المستدعي غير المتمتع بالصلاحيات يختار المنطقة الزمنية التي تُقيَّم فيها نافذة صلاحيته الخاصة.
المشكلة الأساسية — تأثير TZ على NOTBEFORE/NOTAFTER — أُبلغ عنها بشكل خاص من قبل
XlabAI Team of Tencent Xuanwu Lab، وAtuin Automated Vulnerability Discovery
Engine، وGuannan Wang وZhanpeng Liu وGuancheng Li، وتم الإشارة إليهم في الالتزام
db669167c (2026-03-14).
SudoTimeWarp (CVE-2026-96512) هو الاكتشاف بأن db669167c غير مكتمل. الحماية التي
يُثبّتها تغطي ذاكرة التخزين المؤقت للمنطقة الزمنية في glibc — ومعها الطوابع الزمنية للسجلات — لكنها لا
تصل إلى mktime() في gentime.c:156، حيث يُتخذ قرار التصريح. على شجرة تحتوي بالفعل على ذلك
الالتزام، تظل إزاحة النافذة قابلة لإعادة الإنتاج بالكامل. ويسجّل المشرف هذه النقطة في رسالة 1820a349:
التغيير السابق "لم يكن فعّالاً لأن دالة mktime() تعيد قراءة متغير البيئة TZ في كل مرة يتم استدعاؤها"
قاعدة sudoers التي (1) تمنح أمراً للمستدعي، و(2) تحمل NOTBEFORE= أو
NOTAFTER=، و(3) تكتب الطابع الزمني بدون لاحقة Z وبدون إزاحة
صريحة.
البند (3) ليس غريباً: دليل sudoers يوثّق الصيغة بدون لاحقة كامتداد مدعوم، ويطبع
20151201235900 كأحد أمثلة الطوابع الزمنية الأربعة
(docs/sudoers.man.in:1820)، وتظهر هذه الصيغة في مجموعة الاختبارات الانحدارية الخاصة بالمشروع
(plugins/sudoers/regress/testsudoers/test13.sh).
دائماً في حاوية أو جهاز افتراضي قابل للتخلص منه. كل سكربت هنا يعيد كتابة /etc/sudoers.
يقومون بعمل نسخة احتياطية واستعادتها، لكن خطأ هناك يقفلك خارج جهاز حقيقي — ترفض
السكربتات العمل خارج حاوية إلا إذا مرّرت --i-know.
docker build -t sudotimewarp .
docker run --rm -it sudotimewarp
أو بدون بناء صورة:
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'
المخرجات المتوقعة على بناء متأثر:
=== 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 هو اختصار منطقة زمنية عشوائي من ثلاثة أحرف و24 هو إزاحة POSIX. لا
يوجد ملف متضمن ولا يحتاج أي ملف إلى الوجود — القناة القابلة للاستغلال هي سلسلة POSIX المضمّنة
فقط. صيغة tzfile (TZ=:/tmp/evil.tz والمتغيرات) ترفضها حماية
__libc_enable_secure في glibc تحت setuid وتقيس بالضبط 0 ث من الإزاحة، وهذا ما
يفصل هذا عن CVE-2014-9680.
الأسطر 1–3 هي عناصر تحكم، وهي مهمة: خطأ في التحليل سينتج نفس ALLOW الذي ينتجه الخطأ البرمجي.
poc.sh يخرج بـ 0 عند التأثر، و1 عند عدمه، و2 عندما لا تصمد عناصر التحكم.
يستخدم poc.sh خيار NOPASSWD ليتمكن من العمل دون تفاعل. هذا ليس شرطاً من شروط
الخطأ البرمجي. السكربتات المنفصلة تُظهر حدود الصلاحيات صراحةً — الجزء 1 يفعل فقط ما
يفعله المسؤول بشكل مشروع، والجزء 2 يعمل كمستخدم غير متمتع بالصلاحيات ولا يستخدم أي
صلاحية من أي نوع:
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 أربعة سيناريوهات:
مع قاعدة تتطلب كلمة مرور، لا يزال المستدعي يُصادَق عبر PAM، وكلمة المرور الخاطئة لا تزال تفشل. هذا ليس تجاوزاً للمصادقة — ما يتحرك هو قرار التصريح.
PR:L، وليس PR:N.TZ القصوى ترفضان.C:L/I:N/A:N). متجه 7.8 يقيّم الحالة التي تكون فيها القاعدة المؤرخة
أوسع من وصول المستدعي الدائم.حد الـ ~25 س ينطبق على نافذة الوصول، وليس على مدة التأثير: استخدام ناجح واحد داخلها يكفي لإنشاء استمرارية تتجاوز النافذة.
أضف Z إلى كل طابع زمني NOTBEFORE/NOTAFTER — فهو يفرض فرع timegm().
grep -rE 'NOT(BEFORE|AFTER)=' /etc/sudoers /etc/sudoers.d/
لا تفحص باستخدام sudo -l. فهو ينسّق كل Date_Spec عبر gmtime() ويضيف دائماً
Z (plugins/sudoers/display.c:229)، لذا قاعدة مكتوبة
NOTAFTER=20260827221423 تُعرض كـ NOTAFTER=20260827221423Z. المخرجات تُطبّع
بالضبط التفصيل الذي يحدد النتيجة. cvtsudoers وfmtsudoers يتصرفان بنفس الطريقة.
اقرأ /etc/sudoers مباشرة.
1820a349: https://github.com/sudo-project/sudo/commit/1820a349687522f51023d1ae5925125f59679a8cأُبلغ عنه للمشرف في 2026-08-28، بدون موعد نهائي. عادت رقعة مرشحة في نفس اليوم؛ ووصل الإصلاح العام في 2026-08-29. تم تعيين CVE من قبل Red Hat، بصفته CNA-LR، ونُشر في 2026-09-23. لا يوجد حظر: كل ما هنا علني منذ التزام الإصلاح.
SudoTimeWarp / CVE-2026-96512: Ermenson Junior، بحث مستقل، سجّلته Red Hat كـ "Independent security research". التقرير الأصلي للمشكلة الأساسية يعود إلى XlabAI Team of Tencent Xuanwu Lab، وAtuin Automated Vulnerability Discovery Engine، و Guannan Wang وZhanpeng Liu وGuancheng Li.
نُشر للاستخدام الدفاعي: التحقق مما إذا كان مضيف ما متأثراً، والتحقق من صحة تخفيف Z.
| الاسم | 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 (مسجّل)؛ الآلية هي CWE-807 |
| المتأثر | sudo 1.8.20 حتى 1.9.17p2، وmain قبل 1820a349 |
| تم الإصلاح في | 1820a349 (2026-08-29) |
| الإزاحة | حتى 24 س 59 د 59 ث لكل اتجاه؛ الفاصل الإجمالي 49 س 59 د 58 ث |
| السطر | يتحقق من |
|---|
| 1 | أن القاعدة تعمل أصلاً داخل نافذتها |
| 2 | أن الرفض في غياب السطر 4 يأتي فعلاً من NOTAFTER |
| 3 | أن ضبط TZ ليس هو السبب بحد ذاته — TZ=UTC يقرر كما لو لم يكن هناك TZ |
| 4 | الخطأ البرمجي: القاعدة المنتهية تُنفَّذ كـ root |
| 5 | الحدود: مع Z الموثّقة، يُتخذ فرع timegm() ويفشل |
| السيناريو | السياسة |
|---|
expired (افتراضي) | قاعدة واحدة، NOTAFTER قبل ساعة في الماضي، بدون منطقة زمنية |
valid | نفس القاعدة لا تزال داخل نافذتها — عنصر تحكم |
expired-z | نفس القاعدة المنتهية مع Z الموثّقة — عنصر تحكم، صيغة غير متأثرة |
escalation | منح دائم ضيق بالإضافة إلى منح واسع منتهٍ — الشكل الذي يتخذه فعلاً منح الصيانة أو الطوارئ |