CVE-2022-0847复现

CVE-2022-0847复现

/x01 看雪学苑 2023-09-19 18:14

在 Linux 内核的 copy_page_to_iter_pipe 和 push_pipe 函数中,管道缓冲区结构的 “flags “成员缺乏正确的初始化,因此可能包含过期的值。无权限的本地用户可利用此漏洞任意写入文件,从而完成提权。

pipe机制

pipe是内核提供的一种通讯机制,返回两个文件描述符,一个用于发送数据,另一个用于接受数据。实现方式是循环队列它只能用于有血缘关系的进程间通信

pipe结构体如下:

bufs是循环数组,head用来写管道,指向buffer头部,tail用来读管道,指向buffer尾部。

pip_buffer结构体如下:

pipe_write

向管道中写数据时会调用函数pipe_write()

pipe_write()关键功能如下:

1.如果pipe不为空,则说明pipe中还有未被读取的部分,如果未被读取的数据所在的buffer标识为PIPE_BUF_FLAG_CAN_MERGE且要写入的数据加上原来的数据不超过一个页,即可在原来的buffer上续写。

2.不符合上述条件就会新申请一个页面,将新申请的页面插入pipe中,默认初始化flag为PIPE_BUF_FLAG_CAN_MERGE,即默认状态是允许buffer续写的。

零拷贝

Linux系统中一切皆文件,Linux系统的很多活动无外乎读操作写操作,零拷贝就是为了提高读写性能而出现的。

页面高速缓存

我们知道文件一般存放在硬盘(机械硬盘或固态硬盘)中,CPU 并不能直接访问硬盘中的数据,而是需要先将硬盘中的数据读入到内存中,然后才能被 CPU 访问。由于读写硬盘的速度比读写内存要慢很多(DDR4 内存读写速度是机械硬盘500倍,是固态硬盘的200倍),所以为了避免每次读写文件时,都需要对硬盘进行读写操作,Linux 内核使用 页缓存(Page Cache) 机制来对文件中的数据进行缓存。

当用户对文件进行读写时,实际上是对文件的 页缓存 进行读写。所以对文件进行读写操作时,会分以下两种情况进行处理:

◆当从文件中读取数据时,如果要读取的数据所在的页缓存已经存在,那么就直接把页缓存的数据拷贝给用户即可。否则,内核首先会申请一个空闲的内存页(页缓存),然后从文件中读取数据到页缓存,并且把页缓存的数据拷贝给用户。

◆当向文件中写入数据时,如果要写入的数据所在的页缓存已经存在,那么直接把新数据写入到页缓存即可。否则,内核首先会申请一个空闲的内存页(页缓存),然后从文件中读取数据到页缓存,并且把新数据写入到页缓存中。对于被修改的页缓存,内核会定时把这些页缓存刷新到文件中。

将pipe_buffer直接指向页高速缓存作为pipe的buf页使用即可实现以零拷贝的方式在文件描述符之间移动数据。

copy_page_to_iter_pipe

源码:

关键功能如下:

1.找到pipe的写指针head。

2.将head指向的pipe_buffer的page改为准备好的文件缓存页。

这里函数没有初始化pipe_buffer的flag,如果用户调用copy_page_to_iter_pipe之前,head指向pipe_buffer是刚申请来的,那此时的pipe_buffer的flag应该为PIPE_BUF_FLAG_CAN_MERGE,如果再次调用pipe_write函数用户就可以向pipe_buffer指向的文件缓存页中写数据,这就造成了一个任意文件写。

copy_page_to_iter_pipe可以由splice调用。

poc逻辑

调试

直接在docker hub拉个搭建好的环境:
https://hub.docker.com/r/chenaotian/cve-2022-0847

启动qemu的时候先把文件系统里的init脚本改一下,setuidgid 0 改成 setuidgid 1000:

启动qemu之后确认testfile事只读文件:

gdb挂上源码在do_splice打断点,执行./exp testfile 1 nihao
,断下来之后在copy_page_to_iter_pipe打断点,继续运行,断下来之后运行到取buf的位置:

打印查看pipe->bufs[0]的值:

走到函数结尾处,buf[0]的page被替换为文件页高速缓存,并且offset、len、ops也都被替换,再次查看pipe->bufs[0]的值:

可以看到flag没有被替换,仍然为0x10即PIPE_BUF_FLAG_CAN_MERGE。打断点到pipe_write,继续运行,断下来并执行到取buffer处:

可以看到取出了带有页高速缓存的buf并准备写入。继续运行,成功经过判断到达写入函数:

直接继续运行,查看文件内容:

字符串成功写入只读文件,复现成功。

看雪ID:/x01

https://bbs.kanxue.com/user-home-929564.htm

*本文为看雪论坛优秀文章,由 /x01 原创,转载请注明来自看雪社区

#往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复

球分享

球点赞

球在看