[664] in linux-security and linux-alert archive
Re: [linux-security] sliplogin hole explanation
daemon@ATHENA.MIT.EDU (Yury Shevchuk)
Sat Apr 6 14:12:16 1996
Date: Sat, 6 Apr 96 13:52 +0400
To: linux-security@tarsier.cv.nrao.edu
Cc: util-linux@math.uio.no
In-Reply-To: from Yury Shevchuk
From: sizif@botik.ru (Yury Shevchuk)
>In message <m0u2pHl-000HRgC@monad.swb.de> Olaf Kirch writes:
>>We all know that you can pass most environment variables to a login
>>shell when started through telnetd. Assuming you have the password for a
>>sliplogin account on a Linux box, you can pass the ENV variable in this
>>fashion.
>>
>>The attack goes something like this:
>>
>>ENV='`/evil/command`' telnet
>>telnet> environ export ENV
>>telnet> open targethost
>>
>>You then log into your regular slip account, which executes sliplogin as
>>your login shell. Sliplogin, in turn, runs the /etc/slip.login shell
>>script using bash. At startup, bash evaluates *and expands* ENV to
>>obtain the name of a startup file to use instead of .bashrc, and
>>faithfully executes /evil/command.
>
>This is more than only a sliplogin hole! The same attack is
>applicable to every account with shell script as a login shell. Of
>course, you won't right away get the root shell, as in sliplogin's
>case, just a shell with the account's uid, but...
I think I have found a way to close this and related holes. The
solution is to patch /bin/login to destroy environment in spite of the
-p option passed from telnetd, if the login shell is not in /etc/shells.
Grounds: if your shell is in /etc/shells, then your password gives you
full shell access to the system, and environment tricks won't buy you
more. The tricks are only useful when your login shell is not a
normal shell, but rather a script or a program which gives you
specific restricted access to the system, and you want to fool it to
execute arbitrary commands on the system for you.
So by clobbering environment when login shell is not in /etc/shells we
preserve functionality for normal accounts, and remove the danger
for restricted accounts.
Note that this approach requires that your /etc/shells contents agreed
with the definition of /etc/shells: only general shells should be
listed there. Sometimes people violate this rule: for example, a
typical advise for getting ftp-only accounts work with wu-ftpd is "set
the login shell to /bin/false and add /bin/false to /etc/shells, or
wu-ftpd won't let you in". This suggestion is a kludge; the proper
fix is to change wu-ftpd to bypass /etc/shells check for chrooted
guest accounts.
-- Yury Shevchuk
--------------------------------------------------------------------------
This is a patch to util-linux-2.5/login-utils/login.c which closes the
security hole that exploits interpretation of IFS, ENV, etc. by
scripts and custom programs used as login shells, for example
/sbin/sliplogin or /bin/false.
--- login.c 1996/04/06 07:25:58 1.1
+++ login.c 1996/04/06 08:35:45 1.2
@@ -156,10 +156,11 @@
void badlogin P_((char *name));
char *stypeof P_((char *ttyid));
void checktty P_((char *user, char *tty, struct passwd *pwd));
void getstr P_((char *buf, int cnt, char *err));
void sleepexit P_((int eval));
+int general_shell P_((char *shell));
#undef P_
#ifdef KERBEROS
#include <kerberos/krb.h>
#include <sys/termios.h>
@@ -674,12 +675,16 @@
if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
termenv = "dumb";
}
- /* destroy environment unless user has requested preservation */
- if (!pflag)
+ /* destroy environment unless user has requested preservation.
+ Also destroy environment when login shell is not a normal
+ shell, to disable all tricks that use IFS, ENV, etc. to get
+ full shell access out of shell scripts used as specialized
+ login shells. */
+ if (!pflag || !general_shell(pwd->pw_shell))
{
environ = (char**)malloc(sizeof(char*));
memset(environ, 0, sizeof(char*));
}
@@ -994,5 +999,26 @@
{
sleep((unsigned int)5);
exit(eval);
}
+/* return 1 if `shell' is a general shell (i.e. found in /etc/shells);
+ 0 otherwise. */
+
+int
+general_shell(shell)
+ char *shell;
+{
+ char *p;
+ int ret = 0;
+
+ setusershell ();
+ while((p = getusershell ())) {
+ if (strcmp (p, shell) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+ endusershell ();
+
+ return ret;
+}
--------------------------------------------------------------------------