Date: Wed, 12 May 1999 14:32:42 +0400
From: Stas Kisel <stas@SONET.CRIMEA.UA.>
To: [email protected]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 [email protected]
Simferopol State University Web-designer [email protected]