Here's a generic wrapper I've written that you can use as an interim
solution for wrapping /usr/bin/ps and /usr/ucb/ps. (/usr/ucb/ps looks
to be similarly vulnerable.) The code is fairly well documented IMHO,
and should be adaptable enough to wrap just about any program.
This wrapper also filters environment variables, so if you have binaries
which blindly trust certain variables (NLSPATH is a common one in Solaris),
you can filter out those variables. (You could also fairly trivially
add in default values for some variables if you needed to, such as for
NLSPATH.)
Finally, this wrapper will log exploit attempts to syslog if you configure
that option. The log facility, log priority, and log ident are all
configurable with #defines. I've currently set the code to LOG_ALERT
on LOG_LOCAL0, with ident "wrapper". To prevent problems with syslog,
the wrapper even limits the number of characters it writes per log
message. (Note: This limit is on the number of characters per message,
not including the identifier, PID, etc.)
I make no guarantee or warranty about this code; it looks good/works fine
for me. :-) If you have problems configuring this wrapper for a
particular program, first read all the comments in the source, and then
email me if you still can't figure it out. :-)
Incidentally, it's safe to leave ps lying around without the suid-bit;
it'll happily list the calling user's own processes, and those processes
alone. That's one of the wonderful advantages of a /proc based ps. :-)
Enjoy!
--Joe
--- wrapper.c
/*****************************************************************/
/* Generic wrapper to prevent exploitation of suid/sgid programs */
/* J. Zbiciak, 5/19/97 */
/*****************************************************************/
#include <stdio.h>
#include <syslog.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
static char rcsid[]="$Id: wrapper.c,v 1.1 1997/05/19 22:48:03 jzbiciak Exp $";
/**************************************************************************/
/* To install, move wrapped executable to a different file name. (I like */
/* just appending an underscore '_' to the filename.) Then, remove the */
/* offending permission bit. Finally, place this program in the wrapped */
/* program's place with the appropriate permissions. Enjoy! */
/**************************************************************************/
/* Tunable values per program being wrapped */
#define WRAPPED "/usr/bin/ps" /* Set to full path of wrapped executable */
#define REALBIN WRAPPED"_" /* Usually can be left untouched. */
#define MAX_ARG (32) /* Maximum argv parameter length. */
#define SYSLOG 1 /* Enable/disable SYSLOGging */
#define FACILITY LOG_LOCAL0 /* Facility to syslog() to */
#define PRIORITY LOG_ALERT /* Priority level for syslog() */
#define LOGIDENT "wrapper" /* How to identify myself to syslog() */
typedef struct tEnvInfo
{
char * env; /* Environment var name with trailing '=' */
int name_len; /* Length of name (including '=') */
int max_len; /* Max length of value assignable to var */
} TEnvInfo;
/* aside: trailing '=' is necessary to prevent problems with variables */
/* whose names prefix each other. */
TEnvInfo allowed_env [] = /* Environ. vars we allow program to see */
{
{ "COLUMNS=", 8, 4 },
{ "LC_CTYPE=", 9, 64 },
{ "LC_MESSAGES=", 11, 64 },
{ "LC_TIME=", 8, 64 },
{ "LOGNAME=", 8, 16 },
{ "TERM=", 5, 16 },
{ "USER=", 5, 16 },
};
#define NUM_ALLOWED_ENV (sizeof(allowed_env)/sizeof(TEnvInfo))
/* Internal use only -- shouldn't need to adjust, usually */
#define MSG_LEN (192) /* Maximum output message length. */
#define MAX_LOG (64) /* Maximum length per call to syslog() */
#ifndef SYSLOG
#error Define "SYSLOG" to be either 1 or 0 explicitly
#endif
/* No user serviceable parts inside (End of configurable options) */
/* Log a message to syslog, and abort */
void log(char * s)
{
#if SYSLOG
char buf[MAX_LOG];
int l;
l=strlen(s);
/* Open up syslog; use "Local0" facility */
openlog(LOGIDENT "[" WRAPPED "]",LOG_PID,FACILITY);
do {
strncpy(buf,s,MAX_LOG-1);
buf[MAX_LOG-1]=0;
syslog (PRIORITY,buf);
l-=64;
if (l>0) s+=MAX_LOG-1;
} while (l>0);
closelog();
#endif
exit(1);
}
/* The main event */
int main(int argc, char * argv[], char *envp[])
{
int i,j,k;
char buf[MSG_LEN];
/* Check all of argv. Log and exit if any args have length > MAX_ARG */
for (i=0;i<argc && argv[i]!=0;i++)
{
if (strlen(argv[i])>MAX_ARG)
{
printf("Error: Aborting!\n"
" Excessive commandline argument length: '%s'\n", argv[i]);
/* Safe since uid/gid etc. are max 5 chars apiece */
sprintf(buf,
"Attempted overrun (argv): "
"uid=%.5d gid=%.5d euid=%.5d egid=%.5d\n",
(int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid());
log(buf);
exit(1); /* safety net */
}
}
/* Check all of envp. Throw out any environment variables which
aren't in "allowed_env[]". If any variables permitted by
"allowed_env[]" are too long, log and exit. */
for (i=j=0; envp[i]!=0; i++)
{
for (k=0;k<NUM_ALLOWED_ENV;k++)
{
if (strncmp(envp[i],
allowed_env[k].env,
allowed_env[k].name_len)==0)
break;
}
if (k!=NUM_ALLOWED_ENV)
{
if (strlen(envp[i]) >
allowed_env[k].max_len+allowed_env[k].name_len)
{
printf("Error: Aborting!\n"
" Excessive environment variable length: '%s'\n",
envp[i]);
/* Safe because we have control over allowed_env[] */
sprintf(buf,
"Attempted overrun (env var '%s'): "
"uid=%.5d gid=%.5d euid=%.5d egid=%.5d\n",
allowed_env[k].env,
(int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid());
log(buf);
exit(1); /* safety net */
}
envp[j++]=envp[i];
}
if (j>NUM_ALLOWED_ENV)
{
log("Internal error to wrapper -- too many allowed env vars");
exit(1); /* safety net */
}
}
envp[j]=0;
/* If we make it this far, we're good to go. */
argv[0]=WRAPPED;
execve(REALBIN, argv, envp);
/* Safe, because errno number is very few chars */
sprintf(buf, "execve failed! errno=%.5d\n",errno);
perror("execve() failed");
log(buf);
exit(1); /* safety net */
}
--- EOF
-- +--------------Joseph Zbiciak--------------+ |- - - - jzbiciak@daldd.sc.ti.com - - - - -| | - - http://ee1.bradley.edu/~im14u2c/ - - | Not your average "Joe." |- - - - Texas Instruments, Dallas - - - -| +-------#include <std_disclaimer.h>--------+