
كاتب المقال: 0x64rem
دخول
منذ عام ونصف العام ، كانت لدي فكرة أن أدرك جهاز التدوير الخاص بي كجزء من الأطروحة في الجامعة. بدأت بدراسة مواد حول الرسوم البيانية لتدفق التحكم ، الرسوم البيانية لتدفق البيانات ، التنفيذ الرمزي ، إلخ. بعد ذلك جاء البحث عن الأدوات ، عينة من مكتبات مختلفة (Angr ، Triton ، Pin ، Z3). لم يحدث أي شيء ملموس في النهاية ، حتى ذهبت هذا الصيف إلى برنامج Summer of Hack 2019 الخاص بـ Digital Security ، حيث عرضت علي توسيع Clang Static Analyzer كموضوع للمشروع. يبدو لي أن هذا الموضوع سوف يساعدني في وضع معرفتي النظرية على الرفوف ، والبدء في تنفيذ شيء كبير والحصول على توصيات من الموجهين ذوي الخبرة. بعد ذلك ، سوف أخبرك كيف ذهبت عملية كتابة المكوّن الإضافي ووصف مسار أفكاري خلال شهر التدريب.
محلل ثابت كلانج
للتطوير ، يوفر Clang ثلاثة خيارات واجهة للتفاعل:
- LibClang هي واجهة C عالية المستوى تتيح لك التفاعل مع AST ، ولكن ليس بشكل كامل. خيار جيد إذا كنت تحتاج إلى تفاعل مع لغة أخرى (على سبيل المثال ، تنفيذ الارتباطات ) أو واجهة مستقرة.
- Clang Plugins - مكتبات ديناميكية تسمى في وقت الترجمة. يسمح لك بمعالجة AST بالكامل.
- LibTooling - مكتبة لإنشاء أدوات منفصلة تستند إلى Clang. كما يتيح الوصول الكامل إلى التفاعل مع AST. يمكن تشغيل التعليمات البرمجية الناتجة خارج بيئة بناء المشروع المحدد.
نظرًا لأننا سنقوم بتوسيع قدرات Clang Static Analyzer ، فإننا نختار تنفيذ البرنامج المساعد. يمكنك كتابة رمز البرنامج المساعد في C ++ أو Python.
بالنسبة إلى الأخير ، هناك مجلدات تسمح لك بتحليل التعليمة البرمجية المصدر ، وتكرارها على العقد الخاصة بشجرة بناء الجملة الناتجة ، كما يمكنك الوصول إلى خصائص العقد ويمكن تعيين العقدة على سطر التعليمات البرمجية المصدر. هذه المجموعة مناسبة لمدقق بسيط. انظر مستودع llvm لمزيد من التفاصيل.
مهمتي تتطلب تحليلا مفصلا للرمز ، لذلك تم اختيار C ++ للتنمية. التالي هو مقدمة للأداة.
Clang Staic Analyzer (يشار إليها فيما يلي بـ CSA) هي أداة للتحليل الثابت لرمز C / C ++ / Objective-C استنادًا إلى التنفيذ الرمزي. يمكن استدعاء المحلل من خلال الواجهة الأمامية Clang عن طريق إضافة -cc1 و -تحليل الإشارات إلى أمر build أو من خلال binar-build-scan منفصل. بالإضافة إلى التحليل نفسه ، يتيح CSA إنشاء تقارير html مرئية.

يحتوي CSA على مكتبة ممتازة لتحليل التعليمات البرمجية المصدر باستخدام AST (شجرة تركيب الملخص) ، و CFG (التحكم في تدفق الرسم البياني). من الهياكل يمكنك أن ترى المزيد من التصريحات من المتغيرات ، وأنواعها ، واستخدام عوامل التشغيل الثنائية و الأحادية ، يمكنك الحصول على تعبيرات رمزية ، وما إلى ذلك. سوف يستخدم المكون الإضافي الخاص بي وظائف فئات AST ، وسيتم تبرير هذا الخيار أكثر. فيما يلي قائمة بالفصول التي تم استخدامها في تنفيذ المكون الإضافي ، وسوف تساعد القائمة في الحصول على فهم أساسي لقدرات CSA:
Stmt - وهذا يشمل العمليات الثنائية.
Decl - إعلان المتغيرات.
Expr - يخزن اليسار ، الأجزاء اليمنى من التعبيرات ، ونوعها.
ASTContext - معلومات حول الشجرة ، العقدة الحالية.
مدير المصدر - معلومات حول الكود الفعلي الذي يتوافق مع جزء الشجرة.
RecursiveASTVisitor، ASTMatcher - دروس لاجتياز شجرة.
أكرر أن CSA توفر للمطور فرصة لدراسة هيكل الكود بالتفصيل ، وأن الفئات المذكورة أعلاه ليست سوى جزء صغير من المتوفر. بالتأكيد أوصي بالاطلاع على وثائق إصدار Clang إذا كنت لا تعرف كيفية استخراج أي بيانات ؛ على الأرجح ، لقد كتب شيء مناسب بالفعل.
عدد صحيح تجاوز البحث
لبدء تنفيذ المكوّن الإضافي ، يلزمك اختيار المهمة التي سيحلها. في هذه الحالة ، يوفر موقع الويب الخاص بـ llvm قوائم بالمدققون المحتملين ؛ كما يمكنك تعديل أدوات ضبط المدقق الثابتة أو ألفا الحالية. أثناء مراجعة رمز المدققون المتاحين ، أصبح من الواضح أنه من أجل تطوير أكثر نجاحًا لـ libclang ، من الأفضل أن تكتب المدقق الخاص بك من البداية ، لذلك تم الاختيار من قائمة الأفكار غير المحققة . نتيجة لذلك ، تم اختيار الخيار لإنشاء مدقق لاكتشاف تجاوز سعة صحيح. لدى Clang بالفعل وظيفة لمنع مشكلة عدم الحصانة هذه (يتم الإشارة إلى الإشارات -ftrapv و -fwrapv وما شابه لاستخدامها) ، وهي مدمجة في برنامج التحويل البرمجي ، ويصب هذا العادم في التحذيرات ، ولا يتم النظر إليه غالبًا. لا يزال هناك UBSan ، لكن هذه معقمات ، لا يستخدمها الجميع ، وهذه الطريقة تتعلق بتحديد المشكلات في وقت التشغيل ، ويعمل المكون الإضافي لـ CSA في وقت الترجمة ، ويقوم بتحليل المصادر.
التالي هو مجموعة من المواد على الضعف المحدد. تجاوز عدد صحيح تستخدم ليكون شيئا بسيطا وليس خطيرا. في الواقع ، الضعف هو مسل ويمكن أن يكون لها عواقب مثيرة للإعجاب.
تدفقات عدد صحيح هي نوع من الثغرات الأمنية التي يمكن أن تؤدي إلى بيانات عدد صحيح في الكود تأخذ قيمًا غير متوقعة. تجاوز سعة - إذا أصبح المتغير أكبر مما كان مقصودًا ، تجاوز الحد الأدنى - أقل من نوعه الأصلي. يمكن أن تظهر مثل هذه الأخطاء بسبب المبرمج وبسبب المترجم.
في C ++ ، أثناء عملية مقارنة حسابية ، يتم توجيه قيم الأعداد الصحيحة إلى نفس النوع ، وفي كثير من الأحيان إلى قيمة أكبر من حيث عمق البت. وتحدث هذه الأشباح في كل مكان وبشكل مستمر ، يمكن أن تكون صريحة أو ضمنية. هناك العديد من القواعد التي تحدث بها الأشباح [1]:
- التحويل من علامة موقعة إلى نوع بعلامة موقعة ، لكن بت أكبر: فقط أضف الترتيب العالي.
- تحويل عدد صحيح موقّع إلى عدد صحيح غير موقّع له نفس السعة: يتم تحويل السالب إلى موجب ويأخذ معنى جديد. مثال على خطأ مشابه في DirectFB هو CVE-2014-2977 .
- تحويل عدد صحيح موقّع إلى عدد صحيح غير موقّع ذي سعة بت أكبر: أولاً ، سيتم توسيع سعة البتة ، ثم إذا كان الرقم سالباً ، فسيغير القيمة بشكل غير صحيح. على سبيل المثال: يصبح 0xff (-1) 0xffffffff.
- عدد صحيح غير موقَّع بعلامة على نفس سعة البت: يمكن لعدد أن يغير القيمة ، اعتمادًا على قيمة البتة العالية.
- عدد صحيح غير موقَّع به عدد صحيح ذو علامة سعة أكبر: أولاً ، تزيد سعة الرقم غير الموقَّع ، ثم التحويل إلى رقم موقَّع.
- التحويل إلى أسفل: يتم اقتطاع وحدات البت فقط. هذا يمكن أن يجعل القيم غير الموقعة سالبة وهكذا. مثال على هذا الضعف في PHP .
أي يمكن أن يكون مشغل مشكلة عدم الحصانة هو إدخال مستخدم غير آمن أو حساب غير صحيح أو تحويل نوع غير صحيح بسبب مبرمج أو مترجم أثناء التحسين. يكون خيار القنبلة الزمنية ممكنًا أيضًا ، عندما يكون جزء من التعليمات البرمجية غير ضار بإصدار واحد من المترجم ، ولكن مع إطلاق خوارزمية تحسين جديدة "تنفجر" وتتسبب في سلوك غير متوقع. في التاريخ ، كان هناك بالفعل مثل هذه الحالة مع فئة SafeInt (السخرية للغاية) [5 ، 6.5.2].
تدفقات عدد صحيح تفتح متجهًا واسعًا: من الممكن فرض التنفيذ على اتخاذ مسار مختلف (إذا كان الفائض يؤثر على البيانات الشرطية) ، يؤدي إلى تجاوز سعة المخزن المؤقت. من أجل الوضوح ، يمكنك التعرف على CVEs محددة ، ومعرفة أسبابها وعواقبها. بطبيعة الحال ، من الأفضل البحث عن تجاوز عدد صحيح في المنتجات مفتوحة المصدر ، بحيث لا تقرأ الوصف فحسب ، بل ترى الكود أيضًا.
- CVE-2019-3560 - يمكن أن يؤدي تجاوز عدد صحيح في Fizz (مشروع ينفذ TLS لـ Facebook) إلى استغلال ثغرة أمنية في DoS باستخدام حزمة شبكة ضيقة.
- CVE-2018-14618 - تجاوز سعة المخزن المؤقت في Curl بسبب تجاوز عدد صحيح بسبب طول كلمة المرور.
- CVE-2018-6092 - في أنظمة 32 بت ، سمحت الثغرة الأمنية في WebAssembly لمتصفح Chrome بتنفيذ RCE من خلال صفحة HTML خاصة.
من أجل عدم إعادة اختراع العجلة ، تم الأخذ بعين الاعتبار الكود الخاص باكتشاف تجاوز عدد صحيح في محلل ثابت CppCheck . منهجه هو كما يلي:
- تحديد ما إذا كان التعبير هو عامل ثنائي.
- إذا كانت الإجابة بنعم ، فقم بالتحقق مما إذا كانت كلتا الوسيطتين من النوع الصحيح.
- تحديد حجم الأنواع.
- تحقق من خلال العمليات الحسابية ما إذا كانت القيمة يمكن أن تتجاوز حدودها القصوى أو الدنيا.
لكن في هذه المرحلة لم يوضح الأمر. لقد تبين أن هناك الكثير من القصص المختلفة ، ومن خلال تنظيم المعلومات يصبح أكثر صعوبة. كل شيء في مكانه وضع قائمة CWE . في المجموع ، هناك 9 أنواع من عدد صحيح الفائض المخصصة على الموقع:
- 190 - عدد صحيح oveflow
- 191 - عدد صحيح صحيح
- 192 - خطأ التماسك الصحيح
- 193 - من جانب واحد
- 194 - تمديد إشارة غير متوقع
- 195 - تم التوقيع على خطأ تحويل غير موقع
- 196 - غير موقّع على خطأ في التحويل الموقَّع
- 197 - خطأ اقتطاع رقمي
- 198 - استخدام ترتيب البايتات غير الصحيح
نحن نعتبر سبب كل خيار ونفهم أن الفيض يحدث مع ممثلين صريح / ضمني غير صحيح. ومنذ ذلك الحين يتم عرض أي قوالب في هيكل شجرة بناء الجملة المجردة ، وسوف نستخدم AST للتحليل. في الشكل أدناه (الشكل 3) ، يمكن ملاحظة أن أي عملية تتسبب في ظهور طبقة في الشجرة هي عقدة منفصلة ، وبالتجول في جميع أنحاء الشجرة ، يمكننا التحقق من جميع تحويلات الكتابة وفقًا لجدول مع تحويلات قد تسبب خطأ.

بشكل أكثر تحديدًا ، تبدو الخوارزمية كما يلي: نلتف حول Casts وننظر إلى IntegralCast (تحويلات عدد صحيح). إذا وجدت عقدة مناسبة ، فابحث عن الأحفاد بحثًا عن عملية ثنائية أو Decl (إعلان متغير). في الحالة الأولى ، تحتاج إلى التحقق من الإشارة وعمق البت التي تستخدمها العملية الثنائية. في الحالة الثانية ، قارن فقط نوع الإعلان.
تنفيذ المدقق
دعنا ننكب على التنفيذ. نحتاج إلى هيكل عظمي للمدقق ، والذي يمكن أن يكون مكتبة قائمة بذاتها ، أو يمكن تجميعها كجزء من Clang. في الكود ، سيكون الفرق صغيرًا. إذا كنت تخطط بالفعل لكتابة البرنامج المساعد الخاص بك ، نوصي بأن تقرأ على الفور ملف pdf صغير: "Clang Static Analyzer: A Checker Developer's Guide's Guide" ، الأشياء الأساسية موصوفة جيدًا هناك ، على الرغم من أن شيئًا ما قد لا يكون ذا صلة بعد الآن ، إلا أن المكتبة يتم تحديثها بانتظام ، لكنك انتزاع على الفور.
إذا كنت تريد إضافة المدقق الخاص بك إلى مجموعة clang ، فأنت بحاجة إلى:
اكتب المدقق نفسه بالمحتوى التالي تقريبًا:
namespace { class SuperChecker : public Checker<check::PreStmt<BinaryOperator>> {
بعد ذلك ، في التعليمات البرمجية المصدر لـ Clang ، ستحتاج إلى تغيير الملفات CMakeLists.txt
و Checkers.td
. نعيش هنا حول ${llvm-source-path}/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
وهنا ${llvm-source-path}/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
.
في البداية ، تحتاج فقط إلى إضافة اسم الملف مع الكود ، في الثانية تحتاج إلى إضافة وصف هيكلي:
#Checkers.td def SuperChecker : Checker<"SuperChecker">, HelpText<"test checker">, Documentation<HasDocumentation>;
إذا لم يكن الأمر واضحًا ، فهناك في ملف Checkers.td
أمثلة كافية عن كيفية وما يجب القيام به.
على الأرجح لن ترغب في إعادة بناء Clang ، وسوف تلجأ إلى الخيار مع مجموعة المكتبة (so / dll). ثم في رمز المدقق يجب أن يكون شيء مثل هذا:
namespace { class SuperChecker : public Checker<check::PreStmt<BinaryOperator>> {
بعد ذلك ، قم بتجميع التعليمات البرمجية الخاصة بك ، يمكنك كتابة البرنامج النصي الخاص بك للتجميع ، ولكن إذا كان لديك أي مشاكل مع هذا (كما كان للمؤلف :)) ، فيمكنك استخدام Makefile في التعليمات البرمجية المصدر clang وإنشاء أمر clangStaticAnalyzerCheckers بطريقة غريبة.
بعد ذلك ، اتصل المدقق:
لعبة الداما المدمج في
clang++ -cc1 -analyze -analyzer-checker=core.DivideZero test.cpp
للخارجية
clang++ -cc1 -load ${PATH_TO_CHECKER}/SuperChecker.so -analyze -analyzer-checker=test.Me -analyzer-config test.Me:UsrInp1="foo" test.Me:Inp1="bar" -analyzer-config test.Me:Inp2=123 test.cpp
في هذه المرحلة ، لدينا بالفعل نوع من النتائج (الشكل 4) ، لكن الكود المكتوب يمكنه اكتشاف الفائض المحتمل فقط. وهذا يعني وجود عدد كبير من الإيجابيات الخاطئة.

لإصلاح ذلك ، يمكننا:
- الانتقال حول الرسم البياني ذهابًا وإيابًا والتحقق من القيم المحددة للمتغيرات للحالات التي يكون لدينا فيها تجاوز سعة محتمل.
- أثناء اجتياز AST ، قم على الفور بحفظ قيم محددة للمتغيرات والتحقق منها عند الضرورة.
- استخدام تحليل ملوث.
لإثبات وجود المزيد من الحجج ، تجدر الإشارة إلى أنه عند تحليل Clang ، فإن جميع الملفات المحددة في #include
directive ، ونتيجة لذلك ، يزداد حجم AST الناتج. نتيجة لذلك ، من بين الخيارات المقترحة ، هناك خيار واحد فقط منطقي فيما يتعلق بمهمة محددة:
- أولاً ، يستغرق الكثير من الوقت لإكماله. المشي في شجرة ، والبحث عن كل ما تحتاج إليه وحسابه سيستغرق وقتًا طويلاً ، فقد يصبح من الصعب تحليل مشروع كبير باستخدام مثل هذا الرمز. للتجول في الشجرة في الكود ، سنستخدم فئة
clang::RecursiveASTVisitor
، التي تقوم بإجراء بحث متعمق. تقدير لوقت هذا النهج
، حيث V هي مجموعة القمم ، و E هي مجموعة حواف الرسم البياني. - والثاني - يمكنك بالتأكيد تخزين ، لكننا لا نعرف ما سوف نحتاج وما لن. بالإضافة إلى ذلك ، فإن هياكل الشجرة نفسها ، التي نستخدمها في التحليل ، تتطلب الكثير من الذاكرة ، لذلك فإن إنفاق هذه الموارد على شيء آخر يعد فكرة سيئة.
- الثالثة هي فكرة جيدة ، لهذه الطريقة يمكنك أن تجد ما يكفي من البحوث والأمثلة. ولكن في CSA لا يوجد تلوث جاهز. يوجد مدقق ، تمت إضافته لاحقًا إلى قائمة مدققي ألفا (alpha.security.taint.TaintPropagation) في المصادر ، وهو موصوف في الملف
GenericTaintChecker.cpp
. المدقق جيد ، ولكنه مناسب فقط لوظائف الإدخال / الإخراج غير الآمنة المعروفة من C ، بل "يحدد" فقط المتغيرات التي كانت عبارة عن وسيطات أو نتائج لوظائف خطيرة. بالإضافة إلى الخيارات الموضحة ، يجدر النظر في المتغيرات العامة ، وحقول الفصل ، وما إلى ذلك ، من أجل استعادة نموذج "التوزيع" بشكل صحيح.
تم قضاء الوقت المتبقي للتدريب الداخلي في قراءة GenericTaintChecker.cpp
ومحاولة إعادة GenericTaintChecker.cpp
لتناسب احتياجاتك. لم تنجح هذه العملية بنجاح بنهاية الفصل الدراسي ، لكنها ظلت مهمة للتنقيح بالفعل خارج نطاق التدريب في DSEC. أثناء التطوير ، أصبح من الواضح أيضًا أن تحديد المهام الخطرة مهمة منفصلة ، ولا تأتي دائمًا الأماكن الخطرة في المشروع من بعض الوظائف القياسية ، لذلك تمت إضافة علامة إلى المدقق للإشارة إلى قائمة الوظائف التي ستعتبر "مسمومة" / "ملحوظة" أثناء التحليل الملوث.
بالإضافة إلى ذلك ، تمت إضافة التحقق لتحديد ما إذا كان المتغير حقل بت. بواسطة أدوات CSA القياسية ، يتم تحديد الحجم حسب النوع ، وإذا عملنا مع حقل بت ، فسيكون لحجمه قيمة نوع بت للحقل بأكمله ، وليس عدد البتات المحددة في الإعلان المتغير.
ما هي النتيجة؟
في الوقت الحالي ، تم تطبيق مدقق بسيط يمكنه التحذير فقط من تدفقات عدد صحيح محتمل. فصل معدّل للتحليل الملوث ، والذي لا يزال أمامه الكثير من العمل. بعد ذلك ، تحتاج إلى استخدام SMT لتحديد الفائض. لهذا ، يعد محلل Z3 SMT مناسبًا ، والذي تمت إضافته إلى مجموعة Clang في الإصدار 5.0.0 (حسب ملاحظات الإصدار ). لاستخدام المُحلّل ، من الضروري بناء CLANG_ANALYZER_BUILD_Z3=ON
الخيار CLANG_ANALYZER_BUILD_Z3=ON
، وعندما يتم استدعاء المكون الإضافي لـ CSA مباشرةً ، يتم إرسال -Xanalyzer -analyzer-constraints=z3
الإشارات.
مستودع جيثب للنتائج
المراجع:
Howard M.، Leblanc D.، Viega J. "The Sins 24 of أمان الكمبيوتر"
كيفية كتابة مدقق في 24 ساعة
كلانج محلل ثابت: دليل المطور مدقق
دليل تطوير المدقق CSA
ديتز دبليو وآخرون. فهم عدد صحيح صحيح في C / C ++