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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2012-0056 — رفع امتيازات لينكس | Kitploit
أدوات/GitHubGitHub/pythonone/cve-2012-0056
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالشيل كودالتعلم والتعليماستغلال الملفات الثنائية
GitHubpythonone/cve-2012-0056

CVE-2012-0056

رفع امتيازات لينكس

عرض المستودع
1613منذ 10 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

تصعيد الامتيازات المحلي في لينكس عبر SUID /proc/pid/mem Write

Mempodipper

تقديم Mempodipper، وهو استغلال لـ CVE-2012-0056. /proc/pid/mem هي واجهة لقراءة وكتابة ذاكرة العملية مباشرة عن طريق التحرك باستخدام نفس العناوين مثل مساحة الذاكرة الافتراضية للعملية. في الإصدار 2.6.39، اعتُبرت الحماية ضد الوصول غير المصرح به إلى /proc/pid/mem كافية، ولذلك تمت إزالة #ifdef السابق الذي كان يمنع دعم الكتابة إلى ذاكرة عملية عشوائية. يمكن لأي شخص لديه الصلاحيات الصحيحة الكتابة إلى ذاكرة العملية. وتبين، بالطبع، أن التحقق من الصلاحيات كان ضعيفًا. هذا يعني أن جميع نوى لينكس >=2.6.39 معرضة للخطر، حتى إصلاحها قبل بضعة أيام. لنأخذ كود النواة القديم خطوة بخطوة ونعرف ما المشكلة فيه.

عند فتح /proc/pid/mem، يُستدعى كود النواة هذا:

static int mem_open(struct inode* inode, struct file* file)
{
	file->private_data = (void*)((long)current->self_exec_id);
	/* OK to pass negative loff_t, we can catch out-of-range */
	file->f_mode |= FMODE_UNSIGNED_OFFSET;
	return 0;
}

لا توجد قيود على الفتح؛ يمكن لأي شخص فتح fd /proc/pid/mem لأي عملية (خاضعة للقيود المعتادة لـ VFS). كل ما يفعله هو تسجيل self_exec_id للعملية الأصلية التي فُتح بها وتخزينه للتحقق منه لاحقًا أثناء القراءات والكتابات.

أما الكتابات (والقراءات)، فلها قيود تحقق من الصلاحيات. لننظر إلى دالة الكتابة:

static ssize_t mem_write(struct file * file, const char __user *buf,
			 size_t count, loff_t *ppos)
{

/* كود غير مهم تمت إزالته من أجل المشاركة في المدونة */	

	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
	
/* كود غير مهم تمت إزالته من أجل المشاركة في المدونة */

	mm = check_mem_permission(task);
	copied = PTR_ERR(mm);
	if (IS_ERR(mm))
		goto out_free;

/* كود غير مهم تمت إزالته من أجل المشاركة في المدونة */	

	if (file->private_data != (void *)((long)current->self_exec_id))
		goto out_mm;

/* كود غير مهم تمت إزالته من أجل المشاركة في المدونة
 * (تتابع الدالة هنا لكتابة المخزن المؤقت في الذاكرة)
 */	

إذن هناك فحصان مهمان لمنع الكتابات غير المصرح بها: check_mem_permission و self_exec_id. لنقم بالأول أولاً والثاني ثانياً.

كود check_mem_permission ببساطة يستدعي __check_mem_permission، وإليك كودها:

static struct mm_struct *__check_mem_permission(struct task_struct *task)
{
	struct mm_struct *mm;

	mm = get_task_mm(task);
	if (!mm)
		return ERR_PTR(-EINVAL);

	/*
	 * يمكن للمهمة دائمًا النظر إلى نفسها، في حال اختارت
	 * استخدام استدعاءات النظام بدلاً من تعليمات التحميل.
	 */
	if (task == current)
		return mm;

	/*
	 * إذا كانت العملية الحالية تتعقب (ptrace) بنشاط، ويمكنها أيضًا
	 * أن تُرفق حديثًا باستخدام ptrace الآن، فاسمح بذلك.
	 */
	if (task_is_stopped_or_traced(task)) {
		int match;
		rcu_read_lock();
		match = (ptrace_parent(task) == current);
		rcu_read_unlock();
		if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH))
			return mm;
	}

	/*
	 * لا أحد آخر مسموح له.
	 */
	mmput(mm);
	return ERR_PTR(-EPERM);
}

هناك طريقتان لتفويض الكتابة في الذاكرة. إما task == current، بمعنى أن العملية التي يُكتب إليها هي نفسها التي تكتب، أو أن current (العملية الكاتبة) لديها صلاحيات ptrace غامضة للتعامل مع task (العملية التي يُكتب إليها). ربما تعتقد أنه يمكنك خداع كود ptrace؟ هذا مغري. لكني لا أعرف. بدلاً من ذلك، دعنا نكتشف كيف يمكننا جعل عملية ما تكتب ذاكرة عشوائية إلى نفسها، بحيث task == current.

الآن بطبيعة الحال، نريد الكتابة في ذاكرة عمليات suid، لأننا بهذا نحصل على صلاحية الجذر. ألقِ نظرة على هذا:

$ su "yeeeee haw I am a cowboy"
Unknown id: yeeeee haw I am a cowboy

سيخرج su أي نص تريده إلى stderr، مسبوقًا بـ "Unknown id:". لذا، يمكننا فتح fd إلى /proc/self/mem، واستخدام lseek إلى المكان المناسب في الذاكرة للكتابة (سنتناول ذلك لاحقًا)، ثم استخدام dup2 لربط stderr بـ fd الخاص بـ mem، ثم exec إلى su $shellcode لكتابة مشغل شل (shell spawner) إلى ذاكرة العملية، ثم نحصل على الجذر. حقًا؟ ليس بهذه السهولة.

هنا يأتي القيد الآخر. بعد اجتياز اختبار task == current، يتحقق من أن self_exec_id الحالي يطابق self_exec_id الذي فُتح به fd. ما هو self_exec_id بحق الأرض؟ إنه مذكور فقط في بضع أماكن في النواة. والأكثر أهمية هو داخل exec:

void setup_new_exec(struct linux_binprm * bprm)
{
/* تم تقليم كميات هائلة من الكود لأغراض هذه المشاركة في المدونة */

	/* يؤدي تنفيذ exec إلى تغيير نطاقنا. لم نعد جزءًا من مجموعة الخيوط */

	current->self_exec_id++;
			
	flush_signal_handlers(current, 0);
	flush_old_files(current->files);
}
EXPORT_SYMBOL(setup_new_exec);

يتم زيادة self_exec_id في كل مرة تقوم فيها عملية exec. لذا في هذه الحالة، يعمل بحيث لا يمكنك فتح fd في عملية غير suid، ثم استخدام dup2، ثم exec إلى عملية suid... وهذا بالضبط ما كنا نحاول فعله أعلاه. طريقة ذكية لردع هجومنا، أليس كذلك؟

إليك كيفية التغلب على ذلك. نقوم بتفرع (fork) عملية طفل، وداخل تلك العملية الطفل، نقوم بـ exec إلى عملية جديدة. الطفل المفرع في البداية لديه self_exec_id مساوٍ لوالده. عندما نقوم بـ exec إلى عملية جديدة، يزداد self_exec_id بمقدار واحد. وفي نفس الوقت، يكون الوالد مشغولاً بـ exec إلى عملية su التي تكتب الشل كود، لذا يزداد self_exec_id الخاص به إلى نفس القيمة. إذن ما نفعله هو: نجعل هذا الطفل يتفرع ويقوم بـ exec إلى عملية جديدة، وداخل تلك العملية الجديدة، نقوم بفتح fd إلى /proc/parent-pid/mem باستخدام pid العملية الوالدة، وليس عمليتنا الخاصة (كما كان الحال سابقًا). يمكننا فتح fd بهذه الطريقة لأنه لا يوجد فحص صلاحيات لمجرد الفتح. عندما يُفتح، يكون self_exec_id الخاص به قد ازداد بالفعل إلى القيمة الصحيحة التي سيكون عليها self_exec_id للوالد عندما نقوم بـ exec إلى su. وأخيرًا، نمرر fd المفتوح من العملية الطفل إلى العملية الوالدة (باستخدام بعض سحر مقابس مجال يونكس المظلم جدًا)، ثم نقوم بعمليات dup2، ونقوم بـ exec إلى su مع شل كود.

هناك اعتراض واحد متبقي. أين نكتب؟ يجب علينا استخدام lseek إلى موقع الذاكرة المناسب قبل الكتابة، و ASLR يعشو عناوين مساحات العمليات مما يجعل من المستحيل معرفة أين نكتب. هل يجب أن نقضي وقتًا في العمل على المزيد من الذكاء لمعرفة كيفية قراءة ذاكرة العملية، ثم القيام بالبحث؟ لا. تحقق من هذا:

$ readelf -h /bin/su | grep Type
   Type:                              EXEC (Executable file) 

هذا يعني أن su لا يحتوي على قسم .text قابل للنقل (وإلا لكان أخرج "DYN" بدلاً من "EXEC"). اتضح أن su على الغالبية العظمى من التوزيعات لم يُجمَّع مع PIE، مما يعطل ASLR لقسم .text من الملف الثنائي! لذلك اخترنا su بحكمة. الإزاحات في الذاكرة ستكون دائمًا نفسها. لذا للعثور على المكان المناسب للكتابة، دعنا نلقي نظرة على التجميع المحيط برسالة الخطأ "Unknown id: blabla".

يستخرج سلسلة الخطأ هنا:

تنزيل الأداة