Article 4851 of comp.lang.perl: Xref: feenix.metronet.com comp.lang.perl:4851 Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.ecn.bgu.edu!wupost!math.ohio-state.edu!sdd.hp.com!decwrl!decwrl!olivea!sgigate!sgiblab!rpal.rockwell.com!headwall.Stanford.EDU!nntp.Stanford.EDU!bir7 From: bir7@leland.Stanford.EDU (Ross Biro) Subject: Replacement Fingerd in Perl Message-ID: <1993Aug6.162538.27710@leland.Stanford.EDU> Summary: Please check for security holes Keywords: finger fingerd X chroot security Sender: news@leland.Stanford.EDU (Mr News) Organization: Stanford University, California Date: Fri, 6 Aug 93 16:25:38 GMT Lines: 217 This has probably been done many times before, but I've written a fingerd in perl which allows arbitrary applications to be launched via a finger command from a remote computer. I.E. finger "xarchie -display $DISPLAY"@... will cause xarchie to be executed. Since this is an obvious security problem, I would like other people to examine the script for any security holes I might have missed. I will attempt to summarize any responses I get, so feel free to follow up via email instead of posting. This has been written under a government contract, so it will probably be in the public domain once it is finished. However for now I have put a Berkeley style copyright on it. Ross Biro bir7@leland.stanford.edu Member League for Programming Freedom (LPF) mail lpf@uunet.uu.net to protect your Freedom #!/usr/local/bin/perl #fingerd.pl # # Copyright 1993 RIACS. # Permission to use, copy, modify, and distribute this # software and its documentation for any purpose and without # fee is hereby granted, provided that this copyright # notice appears in all copies. RIACS # makes no representations about the suitability of this # software for any purpose. It is provided "as is" without # express or implied warranty. # # Written by Ross Biro 8/5/93 # # The problem we had was how to easily launch clients on a computer # without opening up all sorts of security holes. # It was decided to use a simple protocol that most computer # would be able to speak without additional software. Hence # fingerd(8) was replaced by this perl script. # # This is to be launched from inetd. So stdin and stdout should # point to the socket. # # Features: # 1) Chroot is executed so applications do not need to be # scrutinized as carefully as they otherwise would. # (The ability to run a subshell should still be checked for.) # # 2) Executables are added to the system by simply including # them in the correct directory. # # Problems: # 1) Must be run as root (More potential damage from security holes). # # 2) Currently Only works with X applications. # # Set $Debug to True if you want to run in debugging mode. $Debug = 1; # The user to run everything as. Should have limited privildges. 'nobody' # is a good name to put here. $User='rosscaptive'; # The log file keeps track of all the connections and can be # configured to get stdout/stderr of the application. # Should be modified to use syslog when $Debug is false. $LogFile='>>/tmp/fingerd.log'; { ($name, $passwd,$uid,$gid,$quota,$comment, $Gcos,$dir,$shell) = getpwnam($User); # abort if we didn't get anything die ("Unable to find user $User") if (!$name || !( $name eq $User)); # Give Up Root privledges, but since UID=0 we can still get # them back later to do the chroot. This is not really # necessary, but I feel like being paranoid. $> = $uid; # Get rid of all group privledges ($(, $) ) = ($gid, $gid); # Name is used again later, so let's undef it to prevent any interference. undef $name; # change to the home directory of the captive user. chdir ($dir) || die ("Unable to change directory to $dir: $!"); # read a line from stdin (the remote end) $_ = <>; # Now we want to kill all the ctrl characters, just to be safe. s/[\000-\037]/ /g; # open the log file. open (LOG, $LogFile) || die ("Unable to open log"); # get the address of the remote user. $addr = getpeername(STDIN); # redirect stdin from /dev/null. We don't need it any more, and # this way any application will simply get eof if it tries to # read its stdin. open (STDIN, '&STDOUT'); } else { open (STDOUT, '>/dev/null'); open (STDERR, '>&STDOUT'); } # set effective uid so that we have root access again. $>=0; #now do the chroot chroot ($dir) || die ("Unable to chroot to $dir"); # I beleive on some systems the chroot doesn't really # take affect until after the next chdir. So we do one now. chdir ('/') || die ('Unable to change dir'); #now give up all root privileges ($<,$>) = ($uid, $uid); # make sure it worked. (Does this really do anything? ) exit if ($< != $uid || $> != $uid); # Now we want complete control over the environment, so # we trash it. undef %ENV; # now set the display if it was passed. This has the # advantage of making all reasonable commands take # the option -display. $ENV{'DISPLAY'} = $1 if (s/-display[\t ]+([^ \t]+)//); # set the path. First to things that they can execute # remotely, and then to things which are used in the # scripts, but should not be executed remotely. $ENV{'PATH'}='/usr/bin:/usr/rbin'; # Now set the shell to be /bin/rsh. In the event that # they get a shell we might as well attempt some # extra damage control by limiting what they can # do as much as we can. $ENV{'SHELL'}='/bin/rsh'; exec ("$_") || die ("Unable to exec\n"); } # This section of the code is attempting to be a fingerd. Currently # it only lets you finger by user id. It would be better if it # let you finger any string without funny symbols in it, but # for now I feel like being paranoid. #now give up all root privileges ($<,$>) = ($uid, $uid); # make sure it worked. exit if ($< != $uid || $> != $uid); # clear the IFS, It would only be set if inetd was trying something # funny, but we will do it anyway. $ENV{'IFS'}=' ' if ($ENV{'IFS'}); # make sure that we have a valid uid. ($name, $passwd,$uid,$gid,$quota,$comment, $Gcos,$dir,$shell) = getpwnam($command); # check for strange characters. There shouldn't be any because # it's a valid uid. But let's be a little paranoid. Are there # other things that should be searched for? exit if ( $command =~ /[\[\]@^;*!\$&]/); exec ("finger $command") if (($name eq $command)); die ("Unable to exec finger."); } .