Re: Serious security flaw in rpc.mountd on several operating systems.

Luke Mewburn (lm@CS.RMIT.EDU.AU)
Thu, 28 Aug 1997 23:55:19 +1000

Theo de Raadt writes:
> > > I'm not sure exactly what systems this vulnerability affects, but clearly
> > > it is a serious problem.
> >
> > Since then, It has been confirmed that this hole is present on at least
> > some distributions/versions of Linux, Ultrix, NetBSD, OpenBSD, SunOS,
> > Solaris, and probably many many more.
>
> This was solved well before 2.1 shipped. The problem did exist in
> 2.0, but that's about a year old now, and has been replaced with 2.1.
>
> Here's the log entry:
>
> ----
> symbolic names:
> OPENBSD_2_1: 1.16.0.2
> OPENBSD_2_0: 1.11.0.2
> ...
> revision 1.12
> date: 1996/12/05 23:14:27; author: millert; state: Exp; lines: +14 -9
> Stop info gathering attack pointed out by Alan Cox <alan@cymru.net>
> Only return ENOENT if the dir trying to be mounted is really exported
> to the client. Return EACCESS if not exported.
> ----
>
> Now, if I remember, Alan had posted the information about this to
> BUGTRAQ, thus prompting us to fix it (there is a small chance that the
> problem report actually came to us via David Holland, though).

Theo,
I've examined your fix to this, and I believe that it may rely
upon a variable being set to a valid file system ID (fsb.f_fsid)
which won't be set to a known value if the directory requested doesn't
exist. In the random chance that fsb.f_fsid is set to a valid fsid
(because of the unknown state of uninitialised automatic variables)
then an unprivileged client will receive ENOENT instead of EACCES.

In general, as mountd doesn't know which export list to apply to a
non-existant directory, it's not possible for it to determine whether
the client is permitted or not. Therefore, it's not really feasible
to differentiate between ENOENT for "valid" clients and EACCES for
"invalid" clients.

Returning EACCES for non-existant directories as well as for lack of
sufficient permissions results in far simpler code, as opposed to a
more complicated solution, which would return EACCES except if the
requested non-existant directory would be part of an export list
that the client does have permissions for.

A pseudocode outline of the relevant code section in OpenBSD follows:
(see ftp://ftp3.usa.openbsd.org/pub/OpenBSD/src/sbin/mountd/mountd.c,
mntsvc() function)

-->
struct stat stb;
struct statfs fsb;
struct exportlist *ep;
long bad = 0;

if (stat(dirpath, &stb) < 0 ||
(!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
statfs(dirpath, &fsb) < 0) {
bad = ENOENT;
}
ep = ex_search(&fsb.f_fsid);
/* lm: note that if the earlier stat() fails,
(and therefore bad=ENOENT), fsb will have
unknown contents because the call to statfs()
won't be made. Usually, however, this search
will fail, but there is a chance that the
random contents of the uninitialised fsb
will result in a +ve match */
if (ep && (chk_host(ep->ex_defdir, ...) || (....))) {
if (bad) {
/* lm: it's possible to get here because of
the conditions described above */
svc_sendreply(..., &bad, ); /* ENOENT */
return;
}
/* ...
* mount ok
* ...
*/
} else
bad = EACCES;
if (bad) {
svc_sendreply(..., &bad, ); /* bad = EACCES */
return;
}
<--

Sure, this is probably being a bit too paranoid, but depending on the
value of an uninitialised variable probably isn't the best solution...

I hope that I haven't misinterpreted your code and caused undue alarm.

Luke.

PS: bugtraq is a wonderful service - thanks from another (usually)
silent reader.

--
Luke Mewburn <lm@cs.rmit.edu.au>
UNIX Technical Support, Department of Computer Science, RMIT.
Developer, NetBSD Foundation <lukem@netbsd.org>