COMMAND

    profil(2)

SYSTEMS AFFECTED

    NetBSD prior to 1.4.1, Solaris, FreeBSD

PROBLEM

    Following is  based on  NetBSD Security  Advisory.   NetBSD uses a
    profil(2) system call that dates  back to "version 6" unix.   This
    system call arranges for the kernel to sample the PC and increment
    an element of an array on every profile clock tick.  The  security
    issue stems from the fact that profiling is not turned off when  a
    process  execve(2)'s  another  program  image.   As  the  size and
    location of this array as well  as the scale factor are under  the
    program's  control,  it  is  possible  to arrange for an arbitrary
    16-bit program virtual address  to be incremented on  each profile
    clock tick.

    Although unlikely, it is  theoretically possible that an  attacker
    with  local  access  and  knowledge  of  the  addresses  used   by
    privileged programs could  construct an exploit.   It may be  that
    there are  no candidate  addresses that,  when incremented, result
    in a security failure. However, as this can turn -1 into 0, and  0
    into  1,  and  as   security-related  system  calls  and   library
    functions often return either -1  or 0, this mechanism could  turn
    system  call  returns  of  success  into  failure  or failure into
    success  if  a  program  stores  system  call  results into memory
    locations.  Discovery of problem and kernel patch by Ross Harvey.

    Following program  will check  to see  if a  given system  has the
    profil(2)  bug  described  in  NetBSD  Security  Advisory.   If it
    prints `Counting!'  then you've  got it...   At least  one  system
    (Solaris) appears to fix the  security issue but doesn't turn  off
    profiling unless the new image is  owned by a different user.   To
    check for this, you need to do something like:

	% cc profiltest.c
	% su
	# cp a.out prog.setuid
	# chown (something) prog.setuid
	# (possibly make it setuid)
	# exit
	% ./a.out

    If the program  doesn't find prog.setuid,  it just exec's  itself;
    this  gets  the  same   result  on  most  systems.    (So:  %   cc
    profiltest.c; ./a.out).

    #include <sys/types.h>
    #include <stdio.h>
    #include <unistd.h>

    volatile unsigned short twobins[2];

    int
    main(int ac, char **av)
    {

	    if (ac == 1) {
		    /* can't check the return value; on some systems it's void */
		    profil((char *)twobins, sizeof twobins, (u_long)&main, 2);
		    /* try a different image for uid/setuid tests */
		    execl("prog.setuid", "tryroot", "-", 0);
		    /* otherwise, just chain to ourself */
		    execl(av[0], av[0], "-", 0);
		    fprintf(stderr, "problems\n");
		    exit(1);
	    }
	    for(;;) {
		    if (twobins[0] | twobins[1]) {
			    printf("Counting!\n");
			    twobins[0] = twobins[1] = 0;
		    }
	    }
    }

    Solaris _is_ vulnerable too.  It appears that most or all versions
    of Solaris  _are_ vulnerable  after all.   Chris Thompson  of  the
    Cambridge University Computing Service first noticed this and  has
    notified Sun.

SOLUTION

    Upgrade to  NetBSD 1.4.1,  NetBSD-current, or  apply the following
    patch:

    Index: kern_exec.c
    ===================================================================
    RCS file: /cvsroot/syssrc/sys/kern/kern_exec.c,v
    retrieving revision 1.101
    diff -u -w -u -r1.101 kern_exec.c
    --- kern_exec.c     1999/04/27 05:28:44     1.101
    +++ kern_exec.c     1999/08/06 07:19:24
    @@ -415,6 +415,7 @@
			    goto exec_abort;
	    }

    +   stopprofclock(p);       /* stop profiling */
	    fdcloseexec(p);             /* handle close on exec */
	    execsigs(p);                /* reset catched signals */
	    p->p_ctxlink = NULL;        /* reset ucontext link */

    The  code  in  FreeBSD  is  somewhat  different  (it  is  actually
    amazingly close,  given that  the code  was written  twice, by two
    different parties); patch (not tested) below:

    Index: kern_exec.c
    ===================================================================
    RCS file: /home/ncvs/src/sys/kern/kern_exec.c,v
    retrieving revision 1.99
    diff -u -r1.99 kern_exec.c
    --- kern_exec.c	1999/04/27 11:15:55	1.99
    +++ kern_exec.c	1999/08/11 13:29:28
    @@ -229,6 +229,9 @@
     		p->p_fd = tmp;
     	}

    +	/* stop profiling */
    +	stopprofclock(p);
    +
 	    /* close files on exec */
      	fdcloseexec(p);