Skip to content
KitploitKITPLOIT
أدواتالمدونة
Log in
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
schrodingers-toctou — اكتشاف تحميلات الذاكرة التي يستحدثها المترجم وتحوّل الكود الآمن المكتوب بلغة C إلى ثغرات TOCTOU. يشمل تدقيقات آلية لمصدر الكود، وتحليلًا ثنائيًا قائمًا على Unicorn، ومسوحات للمترجمات/المعماريات/الأعلام عبر أكثر من 100 مشروع. | Kitploit
أدوات/GitHubGitHub/xoreaxeaxeax/schrodingers-toctou
التحليل الديناميكي (عزل)تحليل الشفرة الثابت (SAST)تحليل الثغرات الأمنيةالاستغلالتحليل الملفات الثنائيةالتعلم والتعليم
GitHubxoreaxeaxeax/schrodingers-toctou

schrodingers-toctou

اكتشاف تحميلات الذاكرة التي يستحدثها المترجم وتحوّل الكود الآمن المكتوب بلغة C إلى ثغرات TOCTOU. يشمل تدقيقات آلية لمصدر الكود، وتحليلًا ثنائيًا قائمًا على Unicorn، ومسوحات للمترجمات/المعماريات/الأعلام عبر أكثر من 100 مشروع.

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة
عرض المستودع
94429منذ شهر واحدتمت المراجعة من قبل Kitploit

Schrödinger's TOCTOU

"...تعريف «المُصرّف السليم» يزداد تراخيًا."

الملف التنفيذي الذي تُشغّله ليس البرنامج الذي كتبته. يُعيد مُحسِّن المُصرّف كتابة شفرتك المصدرية بطرق لا تراها أبدًا — وبعض هذه التغييرات يمكنها، بصمت وبشكل مشروع، أن تحوّل شيفرة تبدو آمنة إلى ملفات تنفيذية قابلة للاستغلال. قد يكون السطر نفسه آمنًا مع مُصرّف وقابلًا للاستغلال مع آخر، دون ما في الشيفرة المصدرية يُخبرك بأيهما: ثغرة معلّقة في تراكب كمّي، لا تنهار إلا عند البناء. يستكشف Schrödinger's TOCTOU التحميلات التي يخترعها المُصرّف وآثارها الواسعة على ثغرات وقت الفحص إلى وقت الاستخدام (TOCTOU) — الموجودة عبر الأنوية والمُشغِّلات الافتراضية والبيئات المعزولة والبرمجيات الثابتة والمكتبات. في كل مكان ننظر إليه، تظل الشيفرة التي تبدو آمنة معرّضة لأهواء المُصرّف. لكن تلك مجرد عيّنة، لا حدّ؛ فمن المرجح جدًا أن تكون الثغرات نفسها في شيفرتك أنت أيضًا.

التحدي

"ابدأ بشيء سهل."

كم مرة تُحمّل هذه الدالة *p؟```c unsigned int g(unsigned short *p) { short t = p; / copy *p into a local for safekeeping */ return (unsigned short)t - t; }

التلميح: الإجابة هي 1 — يقوم المصدر بتحميل `*p` مرة واحدة فقط في `t`.

الصقه في [Compiler Explorer](https://godbolt.org/z/c5K9P4dPd) (`arm gcc 14.2.0`،
`-O2`) وعدّ مرات التحميل من `r0`، الذي يحمل `p`:```asm
g:
        ldrh    r2, [r0]     # load *p, once
        ldrsh   r0, [r0]     # load *p, twice
        subs    r0, r2, r0
        bx      lr

تحميل واحد في الكود المصدري، واثنان في الملف الثنائي. أما الثاني فهو تحميل مُبتكَر — قراءة صُنعت بواسطة المترجم ولم تكتبها أنت أبدًا. وهي قانونية في إطار الآلة المُجرَّدة للغة C، التي تفترض أن الذاكرة لا يمكن أن تتغير بين قراءتين. لكن عندما تكون تلك الذاكرة قابلة للكتابة من قبل المهاجم، يتحول الافتراض إلى استغلال: إذ يمكن أن يقع التحميل المُبتكَر بعد فحص أمني، معيدًا بصمت فتح نافذة من نوع التحقق من الوقت إلى الاستخدام (TOCTOU) التي ظن المبرمج أنه أغلقها. فالقيمة التي تحققت منها والقيمة التي تستخدمها لم يعودا مضمونين أن يكونا متماثلين — رغم أنك لم تكتب أبدًا كودًا يعيد قراءتها.

تجاوز سعة المخزن المؤقت من العدم

يُثبت التحدي وجود التحميل المُبتكَر؛ فلنرَ كيف يتحول ذلك إلى تلفٍ في الذاكرة.

في ثغرة من نوع TOCTOU، يتحقق البرنامج من أن القيمة آمنة ثم يستخدمها. ومع ذلك، توجد نافذة للاستغلال إذا استطاع المهاجم تغيير القيمة في جزء صغير من الوقت بين هاتين القراءتين — فالقيمة غير الضارة تجتاز الفحص بينما الخطيرة هي التي تُستخدم:```c if (shared->len <= 20) // CHECK reads shared->len // ** attacker modifies shared->len ** memcpy(out, shared->data, shared->len); // USE reads it again: buffer overflow

الإصلاح الكلاسيكي هو **اللقطة أولاً**: انسخ أي بيانات قد يعبث بها المهاجم
في متغير محلي لا يمكن للمهاجم الوصول إليه، ثم لا تثق إلا
بذلك المتغير المحلي. بمجرد أن يصبح `len` في متغير محلي يكون مجمّدًا — لا يمكن للمهاجم الذي يتسابق مع
الذاكرة المشتركة أن يلمسه بعد الآن — لذا فإن الفحص والنسخ مضمونان
لرؤية نفس القيمة. هكذا يعمل الكود في `receive` أدناه على إصلاح TOCTOU:
يلتقط لقطة من الرسالة، ويتحقق من صحة اللقطة، وينشر النسخة الموثوقة
في `slot` ليتمكن المستهلك من إعادة توجيهها:```c
#include <string.h>

struct message {
    int  len;          /* payload length */
    char data[20];     /* payload        */
};

struct message slot;   /* the most recently validated message */
char out[20];          /* fixed 20-byte destination           */

void receive(struct message *shared) {
    struct message local = *shared;    /* 1. snapshot untrusted input   */
    if (local.len <= 20)               /* 2. validate the snapshot      */
        slot = local;                  /* 3. publish the validated copy */
}

void forward(void) {                   /* the time of use, later        */
    memcpy(out, slot.data, slot.len);  /* slot.len was checked <= 20 ... right? */
}

بالنسبة للمصدر، هذا صحيح. len يُقرأ مرة واحدة بالضبط — في اللقطة — لذا القيمة التي تجتاز فحص <= 20 هي القيمة المنشورة في slot. نافذة TOCTOU مغلقة والكود آمن.

إلا أنه ليس كذلك. تحت x86-64 gcc مع -O2، يقرأ receive إياه من الذاكرة المشتركة الأصلية مرتين: مرة كقيمة عددية لبوابة الفحص، ومرة أخرى كجزء من النسخ الجماعي الذي يُنشر في slot:```nasm receive: cmp DWORD PTR [rdi], 20 ; READ #1: the CHECK reads shared->len directly movdqu xmm0, XMMWORD PTR [rdi] ; READ #2: the bulk copy re-reads it (len is byte 0) mov rax, QWORD PTR [rdi+16] ; (the bulk copy's tail: struct bytes 16-23) jg .L1 ; len > 20? skip the publish mov QWORD PTR slot[rip+16], rax ; (publish that tail) movaps XMMWORD PTR slot[rip], xmm0 ; and publish the TOCTOU-vulnerable snapshot .L1: ret forward: movsx rdx, DWORD PTR slot[rip] ; copy size = slot.len, the unchecked READ #2 value mov esi, OFFSET FLAT:slot+4 ; src = slot.data mov edi, OFFSET FLAT:out ; dst = out[20] jmp memcpy ; copies slot.len bytes into out[20]

يعمل الفحص على READ #1؛ القيمة التي تستقر في `slot.len` هي READ #2. المهاجم الذي يقلب `len` بينهما يمرّر قيمة آمنة إلى فحص `<= 20` بينما تُنشر قيمة ضخمة في `slot` — وبعدها تنسخ `forward` ذلك العدد من البايتات إلى `out[20]`، أي الفيض نفسه الذي كانت اللقطة تهدف إلى منعه، ويعيده المُحسِّن إلى الحياة.

هذا يتحوّل إلى إثبات مفهوم كامل في [`poc/example.c`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/main/poc/example.c)، حيث يستخدم الكود النهج المتعارف عليه لتحصين TOCTOU: بنية `message` غير الموثوقة تُلتقط في `local` بحيث لا يمكن تعديلها، ويتم التحقق من `local.len` الخاص باللقطة مقابل سعة المخزن المؤقت، ولا يُنشر في `slot` إلا النسخة الموثقة؛ ويقوم مستهلك لاحقًا بنسخ عدد من البايتات بقدر `slot.len` من الحمولة إلى مخزن مؤقت ثابت. في الوقت نفسه، ينافس المهاجم على `shared->len`. حمل مبتكر غير متوقع من المترجم يعيد قراءة `shared->len` من أجل النشر الجماعي، لذلك يحمل `slot.len` قيمة المهاجم الضخمة على الرغم من نجاح الفحص — معيدًا إدخال TOCTOU الذي كان المبرمج يحاول الدفاع ضده، ومنشئًا فيض مخزن مؤقت يبدو مستحيلًا — من العدم.

## السبب

> *بحلول الوقت الذي يصل فيه C إلى كود الآلة، يكون قد أعيد تشكيله عبر تخفيض الواجهة الأمامية، وتحسينات IR، وتخصيص السجلات، وتوليد الكود الخلفي — خط أنابيب عميق متعدد المراحل يتخذ قرارات لا يمكنك رؤيتها. لا توجد مرحلة واحدة تُلام. الحمل المبتكر خاصية ناشئة لخط الأنابيب بأكمله، وليس خطأً في أي جزء منه.*

عند هذه النقطة: يمكن للمترجمين *أن يصدروا* حمولات مبتكرة، والنمط نفسه الذي كان يهدف إلى منع الخطأ — الالتقاط، التحقق، الاستخدام — هو ما يعيد إدخاله. الخطوة التالية (لمعرفة ما إذا كنا عرضة فعليًا للخطر) هي توصيف *متى* يحدث ذلك. يتبيّن أن ذلك صعب.
تنزيل الأداة