Subject: Re: [secure@FREEBSD.LUBLIN.PL: FreeBSD (and other BSDs?) local root explot]
To: Manuel Bouyer <bouyer@antioche.lip6.fr>
From: Todd C. Miller <Todd.Miller@courtesan.com>
List: tech-security
Date: 08/27/1999 08:22:07
  by redmail.netbsd.org with SMTP; 27 Aug 1999 14:22:41 -0000
	by xerxes.cs.colorado.edu (8.9.3/8.9.3) with ESMTP id IAA05497;
	Fri, 27 Aug 1999 08:22:07 -0600 (MDT)
Message-Id: <199908271422.IAA05497@xerxes.cs.colorado.edu>
To: Manuel Bouyer <bouyer@antioche.lip6.fr>
cc: tech-security@netbsd.org
Subject: Re: [secure@FREEBSD.LUBLIN.PL: FreeBSD (and other BSDs?) local root explot] 
In-reply-to: Your message of "Fri, 27 Aug 1999 12:31:16 +0200."
             <19990827123116.A345@antioche.lip6.fr> 
References: <19990827115805.A4542@antioche.lip6.fr> <19990827123116.A345@antioche.lip6.fr> 
Date: Fri, 27 Aug 1999 08:22:07 -0600
From: "Todd C. Miller" <Todd.Miller@courtesan.com>

But isn't the real issue simply that core dumps are following a
symlink?  I tried this on OpenBSD-current and didn't have any luck
getting the exploit to work (maybe I didn't try hard enough).

I suspect that the find core dump is actually caused by a bug in
fts.c that was posted in May to bugtraq.  My version of this patch
in OpenBSD follows.  I'd be interested in knowing if the SEGV goes
away with a find linked with a patched fts.c.

Links to the bugtraq discussion:

http://www.securityfocus.com/templates/archive.pike?list=1&date=1999-05-8&msg=199905121032.OAA12043@sonet.crimea.ua
http://www.securityfocus.com/templates/archive.pike?list=1&date=1999-05-8&msg=Pine.LNX.4.05.9905140429230.22741-100000@demerol.darkridge.com
http://www.securityfocus.com/templates/archive.pike?list=1&date=1999-05-8&msg=199951417143.18628.nwmail@venglin.gadaczka.dhs.org
http://www.securityfocus.com/templates/archive.pike?list=1&date=1999-05-8&msg=199905141037.OAA14127@sonet.crimea.ua

 - todd

----------------------------
revision 1.19
date: 1999/05/17 02:32:31;  author: millert;  state: Exp;  lines: +29 -17
1) Only do pointer adjusting if realloc() changed our pointer
2) Only adjust pointers based on ftp_path, not fts_name.
3) Adjust the entries in the file list, as well as the trees, if
   needed.
Loosely based on a patch from Stas Kisel <stas@SONET.CRIMEA.UA>
----------------------------

Index: fts.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/fts.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- fts.c	1998/08/15 08:10:15	1.18
+++ fts.c	1999/05/17 02:32:31	1.19
@@ -57,7 +57,7 @@
 static void	 fts_lfree __P((FTSENT *));
 static void	 fts_load __P((FTS *, FTSENT *));
 static size_t	 fts_maxarglen __P((char * const *));
-static void	 fts_padjust __P((FTS *, void *));
+static void	 fts_padjust __P((FTS *, FTSENT *));
 static int	 fts_palloc __P((FTS *, size_t));
 static FTSENT	*fts_sort __P((FTS *, FTSENT *, int));
 static u_short	 fts_stat __P((FTS *, FTSENT *, int));
@@ -428,7 +428,7 @@
 		return (sp->fts_cur = NULL);
 	}
 
-	/* Nul terminate the pathname. */
+	/* NUL terminate the pathname. */
 	sp->fts_path[p->fts_pathlen] = '\0';
 
 	/*
@@ -581,9 +581,9 @@
 	register int nitems;
 	FTSENT *cur, *tail;
 	DIR *dirp;
-	void *adjaddr;
+	void *oldaddr;
 	int cderrno, descend, len, level, maxlen, nlinks, oflag, saved_errno,
-	    nostat;
+	    nostat, doadjust;
 	char *cp;
 
 	/* Set current node pointer. */
@@ -679,7 +679,7 @@
 	level = cur->fts_level + 1;
 
 	/* Read the directory, attaching each entry to the `link' pointer. */
-	adjaddr = NULL;
+	doadjust = 0;
 	for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
 		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
 			continue;
@@ -687,6 +687,7 @@
 		if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL)
 			goto mem1;
 		if (dp->d_namlen > maxlen) {
+			oldaddr = sp->fts_path;
 			if (fts_palloc(sp, (size_t)dp->d_namlen)) {
 				/*
 				 * No more memory for path or structures.  Save
@@ -703,7 +704,9 @@
 				SET(FTS_STOP);
 				return (NULL);
 			}
-			adjaddr = sp->fts_path;
+			/* Did realloc() change the pointer? */
+			if (oldaddr != sp->fts_path)
+				doadjust = 1;
 			maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
 		}
 
@@ -762,11 +765,11 @@
 		(void)closedir(dirp);
 
 	/*
-	 * If had to realloc the path, adjust the addresses for the rest
-	 * of the tree.
+	 * If realloc() changed the address of the path, adjust the
+	 * addresses for the rest of the tree and the dir list.
 	 */
-	if (adjaddr)
-		fts_padjust(sp, adjaddr);
+	if (doadjust)
+		fts_padjust(sp, head);
 
 	/*
 	 * If not changing directories, reset the path back to original
@@ -950,7 +953,7 @@
 	if ((p = malloc(len)) == NULL)
 		return (NULL);
 
-	/* Copy the name and guarantee NULL termination. */
+	/* Copy the name and guarantee NUL termination. */
 	memmove(p->fts_name, name, namelen);
 	p->fts_name[namelen] = '\0';
 
@@ -1009,15 +1012,18 @@
  * already returned.
  */
 static void
-fts_padjust(sp, addr)
+fts_padjust(sp, head)
 	FTS *sp;
-	void *addr;
+	FTSENT *head;
 {
 	FTSENT *p;
+	void *addr = sp->fts_path;
 
 #define	ADJUST(p) {							\
-	(p)->fts_accpath =						\
-	    (char *)addr + ((p)->fts_accpath - (p)->fts_path);		\
+	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. */
@@ -1028,6 +1034,12 @@
 	for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
 		ADJUST(p);
 		p = p->fts_link ? p->fts_link : p->fts_parent;
+	}
+
+	/* Adjust entries in the dir list as needed */
+	for (p = head; p; p = p->fts_link) {
+		if (p->fts_path != addr)
+			ADJUST(p);
 	}
 }