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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2022-0847 — استغلال إثبات المفهوم وتحليل تفصيلي لثغرة CVE-2022-0847 (Dirty Pipe) لتصعيد الامتيازات في نواة لينكس، بما في ذلك بيئة Docker للاختبار والتصحيح. | Kitploit
أدوات/GitHubGitHub/chenaotian/cve-2022-0847
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عملي
GitHubchenaotian/cve-2022-0847

CVE-2022-0847

استغلال إثبات المفهوم وتحليل تفصيلي لثغرة CVE-2022-0847 (Dirty Pipe) لتصعيد الامتيازات في نواة لينكس، بما في ذلك بيئة Docker للاختبار والتصحيح.

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

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2022-0847 Dirty Pipe تحليل رفع الصلاحيات في نواة لينكس

[toc]

نُشرت هذه المقالة في الأصل في حساب الأمن العام لشركة Huawei، وهذه هي نسخة المدونة (الأكثر اكتمالاً)

رابط النشر الأصلي: https://mp.weixin.qq.com/s/6VhWBOzJ7uu80nzFxe5jpg

ملخص الثغرة

رقم الثغرة: CVE-2022-0847 (الاسم المستعار: Dirty Pipe)

المنتج المتأثر: linux kernel - splice syscall

الإصدارات المتأثرة: تم إدخاله في linux 5.8 بواسطة التصحيح f6dd975583bd ~ تم إصلاحه في 5.16.11 و 5.15.25 و 5.10.102

خطورة الثغرة: يمكن كتابة محتوى لا يتجاوز صفحة واحدة (كافٍ) لأي ملف قابل للقراءة، مما يسمح برفع الصلاحيات محليًا.

إعداد البيئة

Docker لتحليل الثغرة: chenaotian/cve-2022-0847 (إذا لم يكن متاحًا بعد، فأنا لم أقم برفعه بعد)

يوفر:

  • نواة لينكس 5.13 قابلة للتصحيح ومترجمة مع الثغرة
  • qemu, gdb, كود مصدر نواة لينكس 5.13
  • الاستغلال (exp)

بدء التشغيل:

cd ~/cve-2022-0847
gcc exp.c -o exp --static && cp exp ./rootfs && cd rootfs
find . | cpio -o --format=newc > ../rootfs.img
cd ../ 
./boot.sh

التصحيح:

gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b do_splice
b copy_page_to_iter_pipe 
b pipe_write
ignore 3 15
...
p *(struct pipe_inode_info *) pipe
p (struct pipe_buffer)pipe->bufs[0]

مبدأ الثغرة

المبدأ المختصر للثغرة هو أن استدعاء دالة splice يمكنه إرسال ملف إلى pipe بطريقة "النسخ الصفري" (zero-copy). على مستوى الكود، النسخ الصفري يعني استخدام ذاكرة التخزين المؤقت للملف (page cache) مباشرة كصفحة buf في الـ pipe. لكن تم إدخال ثغرة عدم تهيئة متغير هنا، مما يؤدي إلى إمكانية التعامل مع صفحة ذاكرة التخزين المؤقت للملف لاحقًا في قناة الـ pipe على أنها صفحة ذاكرة تخزين مؤقت عادية للـ pipe، وبالتالي يمكن تعديلها عن طريق "الكتابة المتتابعة" (续写). ومع ذلك، في هذه الحالة، لا تعتبر النواة صفحة ذاكرة التخزين المؤقت هذه "صفحة متسخة" (dirty page)، وبالتالي لا يتم تحديثها إلى القرص لفترة قصيرة (حتى إعادة التشغيل التالية أو ما شابه). خلال هذه الفترة، ستستخدم جميع السيناريوهات التي تصل إلى هذا الملف صفحة ذاكرة التخزين المؤقت المعدلة، مما يحقق عملية "كتابة عشوائية في أي ملف قابل للقراءة لفترة قصيرة". يمكن استخدام هذا لرفع الصلاحيات محليًا.

نقطة حدوث الثغرة

وفقًا للتصحيح، تقع نقطة حدوث الثغرة في دالة copy_page_to_iter_pipe، حيث تمت إضافة تهيئة لـ buf->flags. لذا فهذه ثغرة عدم تهيئة متغير.

image-20220308170149137

تظهر نقطة استدعاء copy_page_to_iter_pipe في استدعاء النظام splice. تقوم دالة splice (استدعاء النظام) بنقل محتويات الملف إلى الأنبوب (pipe) بطريقة "النسخ الصفري". مقارنةً بنقل محتويات الملف مباشرةً إلى الأنبوب، فإن الأداء أفضل. سيتم شرح ذلك بالتفصيل أدناه.

مبدأ pipe و pipe_write

أولاً، الاسم المستعار للثغرة هو Dirty Pipe، لذا دعنا نفهم الأنبوب (pipe). pipe هي قناة اتصال توفرها النواة، يتم إنشاؤها بواسطة دالة pipe/pipe2، وتعيد اثنين من واصفات الملفات: أحدهما لإرسال البيانات والآخر لاستقبالها، مثل طرفي الأنبوب. لن ندخل في التفاصيل الدقيقة للاستخدام.

image-20220309124007780

باختصار، في النواة، يبلغ الطول الإجمالي لذاكرة التخزين المؤقت للـ pipe عادةً 65536 بايت، وتُدار على شكل صفحات، بإجمالي 16 صفحة (كل صفحة 4096 بايت). الصفحات ليست متجاورة، بل تُدار عبر مصفوفة، لتشكل قائمة دائرية. يتم الحفاظ على مؤشرين للقائمة: واحد للكتابة (pipe->head) وآخر للقراءة (pipe->tail). سنحلل هنا بشكل أساسي دالة pipe_write:

linux-5.13\fs\pipe.c : 400 : pipe_write

static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
	struct file *filp = iocb->ki_filp;
	struct pipe_inode_info *pipe = filp->private_data;
	unsigned int head;
	ssize_t ret = 0;
	size_t total_len = iov_iter_count(from);
	ssize_t chars;
	bool was_empty = false;
	bool wake_next_writer = false;

	··· ···
    ··· ···
	head = pipe->head;
	was_empty = pipe_empty(head, pipe->tail);
	chars = total_len & (PAGE_SIZE-1);
	if (chars && !was_empty) { 
        //[1] إذا لم تكن ذاكرة التخزين المؤقت للـ pipe فارغة، فحاول الكتابة "بشكل متتابع" من الصفحة الحالية الأخيرة
		unsigned int mask = pipe->ring_size - 1;
		struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
		int offset = buf->offset + buf->len; 

		if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
		    offset + chars <= PAGE_SIZE) { 
            /*[2] المفتاح: إذا كانت علامة PIPE_BUF_FLAG_CAN_MERGE موجودة، فهذا يعني أن الصفحة تسمح بالكتابة المتتابعة.
             * إذا كان طول الكتابة لا يتجاوز الصفحة، فاكتب بشكل متتابع؛ وإلا فابدأ صفحة جديدة. */
			ret = pipe_buf_confirm(pipe, buf);
			···
			ret = copy_page_from_iter(buf->page, offset, chars, from);
			···
			}
			buf->len += ret;
			···
		}
	}

	for (;;) {//[3] إذا تعذرت الكتابة المتتابعة على الصفحة السابقة، فابدأ صفحة جديدة
		··· ···
		head = pipe->head;
		if (!pipe_full(head, pipe->tail, pipe->max_usage)) {
			unsigned int mask = pipe->ring_size - 1;
			struct pipe_buffer *buf = &pipe->bufs[head & mask];
			struct page *page = pipe->tmp_page;
			int copied;

			if (!page) {//[4] طلب صفحة جديدة
				page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
				if (unlikely(!page)) {
					ret = ret ? : -ENOMEM;
					break;
				}
				pipe->tmp_page = page;
			}

			spin_lock_irq(&pipe->rd_wait.lock);

			head = pipe->head;
			··· ···
			pipe->head = head + 1;
			spin_unlock_irq(&pipe->rd_wait.lock);

			/* Insert it into the buffer array */
			buf = &pipe->bufs[head & mask];
			buf->page = page;//[5] وضع الصفحة الجديدة في مصفوفة الصفحات
			buf->ops = &anon_pipe_buf_ops;
			buf->offset = 0;
			buf->len = 0;
			if (is_packetized(filp))
				buf->flags = PIPE_BUF_FLAG_PACKET;
			else
				buf->flags = PIPE_BUF_FLAG_CAN_MERGE;
            	//[6] تعيين العلم، افتراضيًا PIPE_BUF_FLAG_CAN_MERGE
			pipe->tmp_page = NULL;

			copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); 
            //[7] عملية النسخ
			··· ···
			ret += copied;
			buf->offset = 0;
			buf->len = copied;

			··· ···
		}
        ··· ···
    }
	··· ···
	return ret;
}
  1. إذا لم تكن قناة الـ pipe الحالية فارغة (تكون فارغة عندما head==tail)، فهذا يعني وجود بيانات غير مقروءة في القناة. يتم الحصول على المؤشر head، أي الصفحة الأحدث المستخدمة للكتابة، والتحقق من len و offset لتلك الصفحة (للعثور على نهاية البيانات). ثم محاولة الكتابة بشكل متتابع على الصفحة الحالية.
  2. التحقق مما إذا كانت الصفحة الحالية تحمل علامة PIPE_BUF_FLAG_CAN_MERGE، إذا لم تكن موجودة، فلا يُسمح بالكتابة المتتابعة على الصفحة الحالية. أو إذا تجاوز طول البيانات المضافة إلى البيانات السابقة طول الصفحة الواحدة (أي عملية كتابة تتجاوز حدود الصفحة)، فلا يمكن الكتابة بشكل متتابع.
  3. إذا تعذرت الكتابة المتتابعة على الصفحة السابقة، فابدأ صفحة جديدة.
  4. alloc_page تطلب صفحة جديدة.
  5. يتم وضع الصفحة الجديدة في بداية المصفوفة (قد تحل محل صفحة موجودة)، وتهيئة القيم.
  6. buf->flag يتم تهيئتها افتراضيًا إلى PIPE_BUF_FLAG_CAN_MERGE، لأن الحالة الافتراضية تسمح بالكتابة المتتابعة على الصفحة.
  7. يتم نسخ البيانات المكتوبة، وتكرر العملية المذكورة أعلاه حتى الانتهاء.

مفتاح استغلال الثغرة هو علامة PIPE_BUF_FLAG_CAN_MERGE غير المهيأة في splice، والتي تحدد ما إذا كان يمكننا الكتابة بشكل متتابع على صفحة pipe "غير المكتملة".

من splice إلى copy_page_to_iter_pipe

كما ذكر أعلاه، يدير pipe 16 صفحة كذاكرة تخزين مؤقت. طريقة النسخ الصفري في splice هي استبدال صفحة ذاكرة التخزين المؤقت للـ pipe بصفحة ذاكرة التخزين المؤقت للملف مباشرةً (تغيير مؤشر صفحة ذاكرة التخزين المؤقت للـ pipe للإشارة إلى صفحة ذاكرة التخزين المؤقت للملف).

image-20220309124515813

مكدس استدعاءات استدعاء النظام splice إلى دالة الثغرة copy_page_to_iter_pipe عميق جدًا، ولن نحلله بالتفصيل. مكدس الاستدعاءات كالتالي:

تنزيل الأداة