Date: Wed, 12 May 1999 14:32:42 +0400 From: Stas Kisel <stas@SONET.CRIMEA.UA> To: BUGTRAQ@netspace.org Subject: fts, du, find Hi. I use FreeBSD-2.2.8 and FreeBSD-2.2.7 and I know that these versions are no longer supported, but: 1. There are many people still using 2.2 2. This bug probably applies to FreeBSD-3.1 and ever to OpenBSD and other. Approximately a month ago I've found a very strange behaviour of 'du' with long direstory structures. I left this alone due to lack of time, but some days ago I saw an article on bugtraq concerning similar behaviour of 'find'. There is a one bug in libc causing this behaviour. I have a patch, but I did not tested it much ;) 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. I have no exploit and probably will no have a free time (I think 3 days is more than enough) for doing it, but I beleive it is 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. 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 $ */ I've only tested this patch on one machine during one day, so it is probably buggy. If you'll apply this patch, please drop me a line if there was any side effect and I'll do a followup in the bugtraq, say, on the Friday. ------------------ patch ---------------------------------------- --- /usr/src/lib/libc/gen/fts.c.orig Tue May 11 13:37:49 1999 +++ /usr/src/lib/libc/gen/fts.c Wed May 12 13:16:08 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,18 @@ { FTSENT *p; -#define ADJUST(p) { \ +#define ADJUST2(p) { \ (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; } } ------------------ endpatch ---------------------------------------- -- Stas Kisel Open Tavrical College Sysadmin stas@sonet.crimea.ua Simferopol State University Web-designer stas@ccssu.crimea.ua ------------------------------------------------------------------------------------------ Date: Fri, 14 May 1999 04:33:34 -0400 From: Jordan Ritter <jpr5@DARKRIDGE.COM> To: BUGTRAQ@netspace.org Subject: Re: fts, du, find On Wed, 12 May 1999, Stas Kisel wrote: > 2. This bug probably applies to FreeBSD-3.1 and ever to OpenBSD and other. I found this back a few months ago when working on the wu-ftp stuff.. OpenBSD definitely has the same problem. last thing I remember thinking was that it was dying because realloc() was failing (as the fts stuff realloc()'s memory as the path grows) .. Jordan Ritter Network Security Engineer Netect/Bindview Corp Boston, MA "Quis custodiet ipsos custodes?" ------------------------------------------------------------------------------------------ Date: Fri, 14 May 1999 14:37:03 +0400 From: Stas Kisel <stas@SONET.CRIMEA.UA> To: BUGTRAQ@netspace.org Subject: Re: fts...(improved patch) > From: Jordan Ritter <jpr5@darkridge.com> > OpenBSD definitely has the same problem. last thing I remember thinking > was that it was dying because realloc() was failing (as the fts stuff > realloc()'s memory as the path grows) .. fts realloc (pathlen+~1000b) of memory only, so realloc succeds. The bug is in the adjusting pointers after realloc(). Next day after sending patch I've found another circumstanses that triggered similar bug in fts. This time some pointers were adjusted which did not belong to realloc()-ed memory chunk. Improved patch is below. Sorry for inconvenience. Probably there are some similar bugs in fts code or patch. Please let me know if you'll see any. \bye Stas ----------------------------- patch ---------------------------------- --- /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; } } ----------------------------- /patch ---------------------------------- ------------------------------------------------------------------------------------------ Date: Fri, 14 May 1999 19:14:02 +0200 From: Przemyslaw Frasunek <venglin@GADACZKA.DHS.ORG> Reply-To: venglin@lagoon.freebsd.org.pl To: BUGTRAQ@netspace.org Subject: Re: fts, du, find > 2. This bug probably applies to FreeBSD-3.1 and ever to OpenBSD and other. Yes, I've tested it on 3.1-STABLE. > I have no exploit and probably will no have a free time (I think > 3 days is more than enough) for doing it, but I beleive it is > 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). I think, that it will be hard to write an exploit. I've tested it on my 2.2.8-RELEASE at home. 'Find' segfaults, when it tries to do: (void)puts(entry->fts_path); because of junk pointer to structure 'entry'. IMHO it _always_ points to 0x200291d6, so it tries to execute (IMHO) _always_ the same commands: 0x200291d6 <puts+34>: repnz scasb %es:(%edi),%al 0x200291d7 <puts+35>: scasb %es:(%edi),%al 0x200291d8 <puts+36>: movl %ecx,%eax 0x200291d9 <puts+37>: enter $0xd0f7,$0x89 0x200291da <puts+38>: notl %eax 0x200291db <puts+39>: rorb 0x488de455(%ecx) 0x200291dc <puts+40>: movl %edx,0xffffffe4(%ebp) 0x200291dd <puts+41>: pushl %ebp 0x200291de <puts+42>: inb $0x8d,%al 0x200291df <puts+43>: leal 0xffffffff(%eax),%ecx 0x200291e0 <puts+44>: decl %eax 0x200291e1 <puts+45>: decl 0x938de84d(%ecx) 0x200291e2 <puts+46>: movl %ecx,0xffffffe8(%ebp) 0x200291e3 <puts+47>: decl %ebp 0x200291e4 <puts+48>: call 0xc1532576 <end+2705991902> and here it segfaults. -- * Fido: 2:480/124 ** WWW: lagoon.freebsd.org.pl/~venglin ** GSM:48-601-383657 * * Inet: venglin@lagoon.freebsd.org.pl ** PGP:D48684904685DF43EA93AFA13BE170BF *