Netfilter漏洞提权利用(CVE-2023-35001)

news2025/1/16 14:39:51

前言

Netfilter是一个用于Linux操作系统的网络数据包过滤框架,它提供了一种灵活的方式来管理网络数据包的流动。Netfilter允许系统管理员和开发人员控制数据包在Linux内核中的处理方式,以实现网络安全、网络地址转换(Network Address Translation,NAT)、数据包过滤等功能。

漏洞成因

netfilter中存在这nft_byteorder_eval函数,该函数的作用是将寄存器中的数据以主机序或网络序存储。具体代码如下,若采用的操作是NFT_BYTEORDER_NTOH则是将数据从主机序转化为网络序,而NFT_BYTEORDER_HTON则是从网络序转换为主机序。具体转换多少个字节则是用priv->size指定的,在该操作下可以转换二、四、八字节。该漏洞也是由于在对两字节数据进行大小端序转存时出现了错误所导致的。

可以看到代码【1】中使用了联合体存储了源地址和目的地址,联合体的变量分别是u32u16分别代表的是四字节与两字节的空间大小。然后在代码【2】与【3】处源地址是直接取出u16的变量存储到目的地址的u16变量中。

乍一看似乎很符合常理,因为在处理双字节的时候,联合体中的变量就以u16存储,若处理四字节就转化为u32存储,但是这里存在个问题,在C语言中,联合体的存储空间是以最大空间为标准,换句话说无论联合体取出的变量是u16还是u32,联合体的大小都是占用四个字节的,而不会出现双字节的情况,因此在对sd两个联合体进行遍历时,会以四字节为单位找到下一个位置。但是在计算长度时是以双字节进行计算的,因此就会导致拷贝时发生溢出。

File: linux-5.19\net\netfilter\nft_byteorder.c
26: void nft_byteorder_eval(const struct nft_expr *expr,
27: 			struct nft_regs *regs,
28: 			const struct nft_pktinfo *pkt)
29: {
   	...
33:1union { u32 u32; u16 u16; } *s, *d; //使用联合体存储源地址与目的地址
    ...
39: 	switch (priv->size) {
    ...
72: 	case 2:
73: 		switch (priv->op) {
74: 		case NFT_BYTEORDER_NTOH:
75: 			for (i = 0; i < priv->len / 2; i++)
76:2】d[i].u16 = ntohs((__force __be16)s[i].u16);//将源地址的数据拷贝到目的地址的低16位中
77: 			break;
78: 		case NFT_BYTEORDER_HTON:
79: 			for (i = 0; i < priv->len / 2; i++)
80:3】d[i].u16 = (__force __u16)htons(s[i].u16);
81: 			break;
82: 		}
83: 		break;
84: 	}
85: }

举个例子,我们自定义一个联合体数组dest,分别向下标0、1以及2进行赋值。

union {short a;long b;} dst[10];
int main()
{
	dst[0].a = 0x1122;
	dst[1].a = 0x3344;
	dst[2].a = 0x5566;
	
	return 0;
}

按照设想的情况,在使用双字节变量进行遍历的时候会以双字节为单位进行遍历,但是实际的情况如下图。可以发现即使每次赋值都是对双字节的变量进行赋值,但是再遍历的时候还是按照联合体中最大的存储空间(四字节)进行遍历的。

image-20240226143625469

因此漏洞的成因如下,因此在使用nft_byteorder函数转换双字节的大小端序时溢出。

image-20240226144306379

模块地址泄露

nft_byteorder_eval函数内部,溢出的地址是在寄存器下方。因此可以通过控制寄存器的下标值选择需要泄露的地址。

image-20240226145158671

在此需要观察通过nft_byteorder_eval函数可以溢出的范围,priv->len是可以人为控制的,只要满足reg * 4 + priv->len <= 0x50即可,reg代表寄存器的下标值,由于下标为0-4是属于状态值,因此不能通用,我们的reg的值需要从4开始计算起, 那0x50 - 0x10 = 0x40就是我们priv->len能设置最大的值,(0x40 / 2) * 4 = 0x80,因此(0xaf8 ~ 0xaf8 + 0x80)范围内都是可以访问到的。但是现在存在一个问题,虽然我们可以越界访问,但是每次只能获取四字节中的低两个字节。

...
75: 			for (i = 0; i < priv->len / 2; i++)
76:2】d[i].u16 = ntohs((__force __be16)s[i].u16);//将源地址的数据拷贝到目的地址的低16位中
...

将下列值传参给nft_byteorder_eval函数

/*
	dst:18
	src:8
	priv->op:NFT_BYTEORDER_HTON
	priv->len:24
	priv->size:2
*/
rule_add_byteorder(r, 18, 8, NFT_BYTEORDER_HTON, 24, 2);

泄露的值如下,可以发现高两个字节的值是无法泄露的,因为在nft_byteorder_eval中,每次只拷贝了u16的变量。因此每次泄露只能获取低两字节的值。因此需要寻找其他方法进行地址的泄露。

image-20240226151409616

nf_trace_fill_rule_info函数用于跟踪数据包,并且会将rule->handle的值放进数据包中回传给用户。

想要正常执行nf_trace_fill_rule_info函数需要绕过条件

  • rule不能为空,并且rule->is_last需要为0,即当前rule不是最后一个
  • info->type不能是NFT_TRACETYPE_RETURN以及info->verdict->code不能NFT_CONTINUE
/*函数递归
nft_do_chain
->
nft_trace_packet
->
__nft_trace_packet
->
nft_trace_notify
->
nf_trace_fill_rule_info
*/

File: linux-5.19\net\netfilter\nf_tables_trace.c
126: static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
127: 				   const struct nft_traceinfo *info)
128: {
129: 	if (!info->rule || info->rule->is_last)
130: 		return 0;
131: 
132: 	/* a continue verdict with ->type == RETURN means that this is
133: 	 * an implicit return (end of chain reached).
134: 	 *
135: 	 * Since no rule matched, the ->rule pointer is invalid.
136: 	 */
137: 	if (info->type == NFT_TRACETYPE_RETURN &&
138: 	    info->verdict->code == NFT_CONTINUE)
139: 		return 0;
140: 
141: 	return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
142: 			    cpu_to_be64(info->rule->handle),
143: 			    NFTA_TRACE_PAD);
144: }

因此想要通过nf_trace_fill_rule_info函数获取数据的第一步是伪造rule

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

regs变量的下方存在jumpstack变量

image-20240226153618757

结构体nft_jumpstack的构成如下,由chainrulelast_rule组成,并且该结构体变量在regs下方,并且通过byteorder操作可以访问到jumpstack结构体,那么利用byteorder操作篡改rule

struct nft_jumpstack {
	const struct nft_chain *chain;
	const struct nft_rule_dp *rule;
	const struct nft_rule_dp *last_rule;
};

接下来看一下nft_rule_dp结构体,可以发现is_last是调用nf_trace_fill_rule_info函数的条件,handle是泄露的值。

struct nft_rule_dp {
	u64				is_last:1,
					dlen:12,
					handle:42;	/* for tracing */
	unsigned char			data[]
		__attribute__((aligned(__alignof__(struct nft_expr))));
};

在进入nf_trace_fill_rule_info函数内部前需要经历规则与表达式的遍历。

File: linux-5.19\net\netfilter\nf_tables_core.c
255: 	for (; rule < last_rule; rule = nft_rule_next(rule)) { //遍历rule
256: 		nft_rule_dp_for_each_expr(expr, last, rule) { //遍历expr
257: 			if (expr->ops == &nft_cmp_fast_ops)
258: 				nft_cmp_fast_eval(expr, &regs);
259: 			else if (expr->ops == &nft_cmp16_fast_ops)
260: 				nft_cmp16_fast_eval(expr, &regs);
261: 			else if (expr->ops == &nft_bitwise_fast_ops)
262: 				nft_bitwise_fast_eval(expr, &regs);
263: 			else if (expr->ops != &nft_payload_fast_ops ||
264: 				 !nft_payload_fast_eval(expr, &regs, pkt))
265: 				expr_call_ops_eval(expr, &regs, pkt); //执行expr->ops
266: 
267: 			if (regs.verdict.code != NFT_CONTINUE)
268: 				break;
269: 		}
270: 
271: 		switch (regs.verdict.code) {
272: 		case NFT_BREAK:
273: 			regs.verdict.code = NFT_CONTINUE;
274: 			nft_trace_copy_nftrace(&info);
275: 			continue;
276: 		case NFT_CONTINUE:
277: 			nft_trace_packet(&info, chain, rule,
278: 					 NFT_TRACETYPE_RULE); //跟踪数据包
279: 			continue;
280: 		}
281: 		break;
282: 	

遍历规则的宏定义如下,若是rule->dlen没有进行改写,那么会根据rule->dlen找到下一个rule,但是当前的rule是伪造的,因此会导致在取出expr会报错。倘若将rule->dlen修改为0,则下个rule的位置就是当前rule + 8

由于不定长数组unsigned char data[],在sizeof操作中的值为0,因此sizeof(*rule)的值为8。此时将last_rule改写成rule + 8就可以直接跳出循环。

#define nft_rule_next(rule)		(void *)rule + sizeof(*rule) + rule->dlen

在完场上述流程后,就可以顺利进入nft_trace_packet函数内部,nft_trace_packet函数也比较简单,实际是调用了__nft_trace_packet函数

File: linux-5.19\net\netfilter\nf_tables_core.c
37: static inline void nft_trace_packet(struct nft_traceinfo *info,
38: 				    const struct nft_chain *chain,
39: 				    const struct nft_rule_dp *rule,
40: 				    enum nft_trace_types type)
41: {
42: 	if (static_branch_unlikely(&nft_trace_enabled)) {
43: 		const struct nft_pktinfo *pkt = info->pkt;
44: 
45: 		info->nf_trace = pkt->skb->nf_trace;
46: 		info->rule = rule;
47: 		__nft_trace_packet(info, chain, type);
48: 	}
49: }

可以发现想要进入nft_trace_notify函数需要满足info->traceinfo->trace不为空。

File: linux-5.19\net\netfilter\nf_tables_core.c
24: static noinline void __nft_trace_packet(struct nft_traceinfo *info,
25: 					const struct nft_chain *chain,
26: 					enum nft_trace_types type)
27: {
28: 	if (!info->trace || !info->nf_trace)
29: 		return;
30: 
31: 	info->chain = chain;
32: 	info->type = type;
33: 
34: 	nft_trace_notify(info);
35: }

使用meta表达式可以设置skb->nf_trace,将skb->nf_trace设置为非空就可以进入到nft_trace_notify函数。

File: linux-5.19\net\netfilter\nft_meta.c
...
443: 	case NFT_META_NFTRACE:
444: 		value8 = nft_reg_load8(sreg);
445: 
446: 		skb->nf_trace = !!value8;
447: 		break;
...

nft_trace_notify函数内部,还会判断是否订阅NFNLGRP_NFTRACE。没订阅则无法继续执行。

File: linux-5.19\net\netfilter\nf_tables_trace.c
    ...
176: 	if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE))
177: 		return;
	...

libnml库中使用mnl_socket_setsockopt函数进行netlink的组订阅,由于在使用宏NFNLGRP_NFTRACE编译时会提示找不到该值,因此这里使用实际值代替了。

static int group = 9;
if (mnl_socket_setsockopt(nleak, NETLINK_ADD_MEMBERSHIP, &group,
				  sizeof(int)) < 0) {
		perror("mnl_socket_setsockopt");
		exit(EXIT_FAILURE);
}

接下来就需要具体如何伪造rule,通过byteorder操作可以首先可以将原先的chainrule以及last_rule的地址泄露,但是只能泄露四字节。

image-20240226185754371

由于我们需要找到符合上述条件的rule,并且我们只有rule的最低两个字节,因此搜索范围不大,因此需要在泄露的rule_low附近寻找一个合适的模块地址。在存储泄露的rule之前存储利用immediate以及meta_set操作,我们选择其中一个进行泄露即可。

image-20240226190914862

伪造的方式也比较简单,由于is_lastdlen都需要设置为0,因此我们只需要找到两个字节为0的值,作为伪造的rule即可,伪造的rule如下。

image-20240226191341104

修改后的结果如下

image-20240226193040824

由于handle实际是占用42比特,但是有3个比特被设置为0了,因此实际泄露的值只有39比特,但是由于模块地址的高4个字节都是固定的0xffffffff,因此不影响模块地址的泄露。通过从数据包中提取数据得到handle的值为后,简单移位操作就可以还原。

module = ((leak << 13)  >> 16);

最后泄露模块基地址成功。

image-20240226192449806

总结

总结一下模块基地址的泄露流程

  • 构造基础链
    • 设置NFT_JUMP表达式
    • 通过meta设置为NFT_META_NFTRACE
  • 泄露链
    • byteorder表达式触发漏洞,第一次读chainrule以及last_rule,第二次改写为chainfake rule以及fake last_rule
    • dynset表达式泄露chainrulelast_rule
  • 订阅NFNLGRP_NFTRACE组,接收数据包
  • 后续接着分享如何绕过kaslr以及最终提权的利用。

原版exp使用go语言写的,我使用c语言重写了一版。

完整exp:https://github.com/h0pe-ay/Vulnerability-Reproduction/tree/master/CVE-2023-35001(nftables)(c语言)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1623063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

centos7搭建maven私服nexus

1.nexus Nexus Repository Manager&#xff08;通常简称 Nexus 或 Nexus RM&#xff09;是由Sonatype公司开发的一款开源的、强大的软件仓库管理工具&#xff0c;主要用于企业级的二进制组件&#xff08;如Java库、Node.js模块、Python包等&#xff09;存储、管理和分发。 官方…

定序器导出fbx到max里对位k动作

可以把场景移动到原点去k动作&#xff0c;然后可以恢复到ue4的位置 -- 定义全局变量 global CenterPoint undefined global averageCenter [0,0,0]-- 定义对话框 rollout restoreRollout "定序器>FBX>MAX" (button CreateButton "建立中心点" wid…

使用JS代理 实现大对象的功能拆解

序言 在Android开发中&#xff0c;可以通过webView的addJavascriptInterface方法注入一个对象到网页中。但是随着开发的需求越来越多。这个对象身上的方法也越来越多。这个对象对应的java类&#xff0c;体积越来越大&#xff0c;不利于维护。为了在不影响之前代码的基础上。把…

操作系统安全:Windows与Linux的安全标识符,身份鉴别和访问控制

「作者简介」&#xff1a;2022年北京冬奥会中国代表队&#xff0c;CSDN Top100&#xff0c;学习更多干货&#xff0c;请关注专栏《网络安全自学教程》 操作系统有4个安全目标&#xff0c;也就是说想要保证操作系统的安全&#xff0c;就必须实现这4个需求&#xff1a; 标识系统…

网络协议安全:OSI七层模型分层及作用,数据封装与解封过程,数据传输过程。

「作者简介」&#xff1a;2022年北京冬奥会中国代表队&#xff0c;CSDN Top100&#xff0c;学习更多干货&#xff0c;请关注专栏《网络安全自学教程》 这一章节我们需要知道OSI分哪七层&#xff0c;每层的作用&#xff0c;知道数据在七层模型中是怎样传输的&#xff0c;封包和解…

数据结构练习:链表扩容

大致步骤&#xff1a; 一&#xff1a;创建一个新链表&#xff0c;遍历原链表的同时&#xff0c;将原链表的值复制给新链表 二&#xff1a;将新链表插入到原链表中&#xff08;大致如下&#xff09; 注&#xff1a; 1.头结点是不存有数据的 2.记得malloc后要free 3.*&是…

男士休闲裤比较好的品牌有哪些?高品质休闲男装推荐

穿衣服最重要的并不是要求多好看多时尚&#xff0c;相信绝大部分年纪在23岁以上的男同胞们更希望穿一些简约好搭配的款式&#xff0c;更重要的其实就是要求质量耐穿&#xff0c;以及有足够好的舒适性。 但是现在市面上的品牌实在是太多了&#xff0c;而且质量也参差不齐&#x…

SpringBoot 3.x + Swagger3 踩坑实录

问题描述 维护的SpringBoot版本是3.0版本&#xff0c;翻教程的时候发现很多SpringBoot2.x版本用的都是springfox&#xff0c;但问题是在SpringBoot3.x版本后&#xff0c;逐渐不支持springfox&#xff0c;强行启动会导致异常&#xff0c;现阶段使用的Springdoc进行替换。 参考…

设计模式-六大原则

设计模式的六大原则是软件工程中的基本概念&#xff0c;使得构建可维护、可扩展和可重用的代码。 1.单一职责原则&#xff08;Single Responsibility Principle&#xff09;&#xff1a;一个类或方法应该只有一个引起变化的原因&#xff0c;确保类或模块的功能高度内聚。 案例&…

力扣数据库题库学习(4.22日)

577. 员工奖金 问题链接 思路分析 Employee表与Bonus表通过empId字段可以连接&#xff0c;需求是查出奖金少于1000的员工名和奖金值。 这里奖金少于1000的情况就是没有奖金有奖金但少于1000 这里我给出的解决方案就是使用左连接&#xff0c;将Employee表作为左表&#xff…

c++二叉树的进阶--二叉搜索树

1. 二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值 它的左…

企业为什么要选择通配符SSL证书使用?

企业选择使用通配符SSL证书主要是出于以下几个重要原因&#xff1a; 1. 经济性&#xff1a; - 节省成本&#xff1a;相较于为每一个子域名单独购买并维护单独的SSL证书&#xff0c;通配符证书能够以一张证书覆盖同一主域名下的所有同级子域名&#xff0c;无需为新增或已有的子域…

SpringBoot框架——8.MybatisPlus常见用法(常用注解+内置方法+分页查询)

1.MybatisPlus常用注解&#xff1a; 1.1 当数据库、表名和字段名和实体类完全一致时无需加注解&#xff0c;不一致时&#xff1a; TableName指定库名 TableId指定表名 TableField指定字段名 1.2 自增主键&#xff1a; TableId(typeIdType.AUTO) private Long id; 1.3 实体类中属…

【JAVA】java 中的Stream 常用函数

java 中的Stream 常用函数 一、简介1.1、什么是Stream&#xff1f;1.2、创建Stream1.3、Stream的特性 二、Stream的操作2.1、中间操作&#xff1a;2.2、终端操作&#xff1a;2.3、Stream的并行处理 三、Stream 常用函数四、使用示例4.1、计算集合中偶数的平方和&#xff1a;4.2…

新手也能学会的甘特图制作教程

甘特图是什么&#xff1f; 甘特图(Gantt Chart)是一种以图表形式直观展示项目计划的工具,由20世纪初的管理学家亨利甘特(Henry Gantt)发明并命名。它具有以下几个主要特点: 水平时间轴 甘特图的横轴是一条时间轴,通常按天、周或月来刻度,直观展示了项目从开始到结束的整个时间…

【信息收集】端口扫描masscan负载均衡识别lbd

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、什么是masscan masscan在kali系统上是自带的端口扫描…

回归预测 | Matlab实现BO-RF贝叶斯优化随机森林多变量回归预测

回归预测 | Matlab实现BO-RF贝叶斯优化随机森林多变量回归预测 目录 回归预测 | Matlab实现BO-RF贝叶斯优化随机森林多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现BO-RF贝叶斯优化随机森林多变量回归预测&#xff1b; 2.输入7个特征&#xf…

考研日常记录(upd 24.4.24)

由于实在太无聊了 &#xff0c; 所以记录以下考研备考日常 &#xff0c; 增加一点成就感 &#xff0c; 获得一点前进动力。 文章目录 2024.4.18 周四课程情况&#xff1a;时间规划&#xff1a; 2024.4.19 周五课程情况&#xff1a;时间规划&#xff1a; 2024.4.20 周六2024.4.2…

Java知识总结---并发篇

线程 线程的状态 Java中线程可以有如下6中状态&#xff1a; NEW 新创建 RUNNABLE 可运行 BLOCKED 阻塞 WAITING 等待 TIMED WAITING 计时等待 TERMINATED 终止 线程的创建 &#xff08;1&#xff09;继承Thread类 public class ExtendsThread extends Thread { O…

学习Docker笔记

在23号刚刚学完新版本的docker还想说回去继续学习老版本的springcloud课程里面的docker 结果一看黑马首页新版本课程出了&#xff0c;绷不住了。以下是我学习新版本docker的笔记&#xff0c;记录了我学习过程遇到的各种bug和解决&#xff0c;也参考了黑马老师的笔记&#xff1a…