Re: Major security-hole in kerberos rsh, rcp and rlogin.

Mattias Amnefelt (mattiasa@stacken.kth.se)
Thu, 06 Nov 1997 11:25:03 +0100

The security hole in kerberos:

Affects:
kth-krb4

Background:
Every user on a kerberized system has a ticket-file. Only the owner should
be able to read this file. The name of the ticketfile is stored in the
environment-variable KRBTKFILE.

The hole:
The versions of rsh, rcp and rlogin in the kth-krb4 package are setuid to work
with bsd-style rshd and rlogind. When they attempt to read the ticketfile,
there is no check if the user starting the program has read access of the file.
Thus, a user can use any other user on the system's ticketfile by simply
changing an environment variable.

Quick Workaround:
Disable the suid-bits on rcp, rsh and rlogin. This will disable the program's
capabilities to fallback to the non-kerberised protocols if the a user fails
to authenticate himself.

Permanent fix:
Change the uid of the program to the user's uid as early as possible
(patches from the development team are included, plus two other
security patches for kth-kerberos which might be useful to the bugtraq
community).

_- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _
| Mattias Amnefelt | This is a Unix system, I know this. |
| email: mattiasa@stacken.kth.se | - Lex, Jurassic Park |
| phone: +46-(0)70-6970872 | |
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ -

------- Start of forwarded mail -------

>From assar@sics.se Tue Nov 4 22:08:18 1997
Date: 03 Nov 1997 18:35:40 +0100
From: Assar Westerlund <assar@sics.se>
To: krb4@sics.se, krb4-announce@sics.se
Subject: security patches for 0.9.6

The enclosed patch to 0.9.6 fixes three security problems:

1. the tgetent buffer overflow. This is fixed by simply not calling
tgetent.

2. vulnerability of setuid rsh, rlogin, and rcp. (mentioned in a
confusion post on bugtraq).

3. missing IP-nummer check in telnetd.

NOTE: we recommend against running rsh/rlogin/rcp setuid.

This fix will of course be included in an upcoming version RSN.

Assar, Bjorn, and Johan

Index: appl/bsd/rcp.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rcp.c,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -w -r1.43 -r1.44
--- rcp.c 1997/05/13 09:41:26 1.43
+++ rcp.c 1997/11/03 11:18:02 1.44
@@ -49,6 +49,9 @@
static uid_t userid;
static int pflag, iamremote, iamrecursive, targetshouldbedirectory;

+static int argc_copy;
+static char **argv_copy;
+
#define CMDNEEDS 64
static char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */

@@ -403,8 +406,9 @@
kerberos(char **host, char *bp, char *locuser, char *user)
{
int sock = -1, err;
-again:
+
if (use_kerberos) {
+ setuid(getuid());
rem = KSUCCESS;
errno = 0;
if (dest_realm == NULL)
@@ -439,13 +443,11 @@
rem = sock;
#endif
if (rem < 0) {
- use_kerberos = 0;
- port = get_shell_port(use_kerberos, 0);
if (errno == ECONNREFUSED)
oldw("remote host doesn't support Kerberos");
else if (errno == ENOENT)
oldw("can't provide Kerberos authentication data");
- goto again;
+ execv(_PATH_RCP, argv_copy);
}
} else {
if (doencrypt)
@@ -906,8 +908,28 @@
{
int ch, fflag, tflag;
char *targ;
+ int i;

set_progname(argv[0]);
+
+ /*
+ * Prepare for execing ourselves.
+ */
+
+ argc_copy = argc + 1;
+ argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy));
+ if (argv_copy == NULL)
+ err(1, "malloc");
+ argv_copy[0] = argv[0];
+ argv_copy[1] = "-K";
+ for(i = 1; i < argc; ++i) {
+ argv_copy[i + 1] = strdup(argv[i]);
+ if (argv_copy[i + 1] == NULL)
+ errx(1, "strdup: out of memory");
+ }
+ argv_copy[argc + 1] = NULL;
+
+
fflag = tflag = 0;
while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
switch(ch) { /* User-visible flags. */
@@ -951,8 +973,10 @@
* kshell service, pass 0 for no encryption */
port = get_shell_port(use_kerberos, 0);

+ userid = getuid();
+
#ifndef __CYGWIN32__
- if ((pwd = k_getpwuid(userid = getuid())) == NULL)
+ if ((pwd = k_getpwuid(userid)) == NULL)
errx(1, "unknown user %d", (int)userid);
#endif

Index: appl/bsd/rlogin.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rlogin.c,v
retrieving revision 1.61
retrieving revision 1.62
diff -u -w -r1.61 -r1.62
--- rlogin.c 1997/05/25 01:14:47 1.61
+++ rlogin.c 1997/11/03 11:18:09 1.62
@@ -594,14 +594,12 @@
usage();
}
optind += argoff;
- argc -= optind;
- argv += optind;

/* if haven't gotten a host yet, do so */
- if (!host && !(host = *argv++))
+ if (!host && !(host = argv[optind++]))
usage();

- if (*argv)
+ if (argv[optind])
usage();

if (!(pw = k_getpwuid(uid = getuid())))
@@ -609,7 +607,6 @@
if (!user)
user = pw->pw_name;

-
if (user_port)
sv_port = user_port;
else
@@ -636,17 +633,8 @@

get_window_size(0, &winsize);

- try_connect:
if (use_kerberos) {
- struct hostent *hp;
-
- /* Fully qualify hostname (needed for krb_realmofhost). */
- hp = gethostbyname(host);
- if (hp != NULL && !(host = strdup(hp->h_name))) {
- errno = ENOMEM;
- err(1, NULL);
- }
-
+ setuid(getuid());
rem = KSUCCESS;
errno = 0;
if (dest_realm == NULL)
@@ -659,15 +647,22 @@
rem = krcmd(&host, sv_port, user, term, 0,
dest_realm);
if (rem < 0) {
- use_kerberos = 0;
- if (user_port == 0)
- sv_port = get_login_port(use_kerberos,
- doencrypt);
+ int i;
+ char **newargv;
+
if (errno == ECONNREFUSED)
warning("remote host doesn't support Kerberos");
if (errno == ENOENT)
warning("can't provide Kerberos auth data");
- goto try_connect;
+ newargv = malloc((argc + 2) * sizeof(*newargv));
+ if (newargv == NULL)
+ err(1, "malloc");
+ newargv[0] = argv[0];
+ newargv[1] = "-K";
+ for(i = 1; i < argc; ++i)
+ newargv[i + 1] = argv[i];
+ newargv[argc + 1] = NULL;
+ execv(_PATH_RLOGIN, newargv);
}
} else {
if (doencrypt)
Index: appl/bsd/rsh.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rsh.c,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -w -r1.36 -r1.37
--- rsh.c 1997/06/26 13:48:35 1.36
+++ rsh.c 1997/11/03 11:18:14 1.37
@@ -247,9 +247,6 @@
err(1, "can't exec %s", _PATH_RLOGIN);
}

- argc -= optind;
- argv += optind;
-
#ifndef __CYGWIN32__
if (!(pw = k_getpwuid(uid = getuid())))
errx(1, "unknown user id.");
@@ -266,12 +263,12 @@
if (doencrypt)
nfork = 0;

- args = copyargs(argv);
+ args = copyargs(argv+optind);

sv_port=get_shell_port(use_kerberos, doencrypt);

-try_connect:
if (use_kerberos) {
+ setuid(getuid());
rem = KSUCCESS;
errno = 0;
if (dest_realm == NULL)
@@ -284,13 +281,22 @@
rem = krcmd(&host, sv_port, user, args, &rfd2,
dest_realm);
if (rem < 0) {
+ int i;
+ char **newargv;
+
if (errno == ECONNREFUSED)
warning("remote host doesn't support Kerberos");
if (errno == ENOENT)
warning("can't provide Kerberos auth data");
- use_kerberos = 0;
- sv_port=get_shell_port(use_kerberos, doencrypt);
- goto try_connect;
+ newargv = malloc((argc + 2) * sizeof(*newargv));
+ if (newargv == NULL)
+ err(1, "malloc");
+ newargv[0] = argv[0];
+ newargv[1] = "-K";
+ for(i = 1; i < argc; ++i)
+ newargv[i + 1] = argv[i];
+ newargv[argc + 1] = NULL;
+ execv(_PATH_RSH, newargv);
}
} else {
if (doencrypt)
Index: appl/bsd/pathnames.h
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/pathnames.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -w -r1.23 -r1.24
--- pathnames.h 1996/11/17 06:36:42 1.23
+++ pathnames.h 1997/11/03 11:17:19 1.24
@@ -65,6 +65,9 @@
#undef _PATH_RSH /* Redifine rsh */
#define _PATH_RSH BINDIR "/rsh"

+#undef _PATH_RCP /* Redifine rcp */
+#define _PATH_RCP BINDIR "/rcp"
+
#undef _PATH_LOGIN
#define _PATH_LOGIN BINDIR "/login"

@@ -186,6 +189,8 @@
#define _PATH_RLOGIN "/usr/athena/bin/rlogin"
#undef _PATH_RSH
#define _PATH_RSH "/usr/athena/bin/rsh"
+#undef _PATH_RCP
+#define _PATH_RCP "/usr/athena/bin/rcp"
#undef _PATH_LOGIN
#define _PATH_LOGIN "/usr/athena/bin/login"
#endif

Index: appl/telnet/libtelnet/kerberos.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/appl/telnet/libtelnet/kerberos.c,v
retrieving revision 1.34
retrieving revision 1.36
diff -u -w -r1.34 -r1.36
--- kerberos.c 1997/10/21 21:15:24 1.34
+++ kerberos.c 1997/11/03 06:12:14 1.36
@@ -265,9 +267,11 @@
void
kerberos4_is(Authenticator *ap, unsigned char *data, int cnt)
{
+ struct sockaddr_in addr;
char realm[REALM_SZ];
char instance[INST_SZ];
int r;
+ int addr_len;

if (cnt-- < 1)
return;
@@ -288,8 +292,17 @@
printf("\r\n");
}
k_getsockinst(0, instance, sizeof(instance));
- if (r = krb_rd_req(&auth, KRB_SERVICE_NAME,
- instance, 0, &adat, "")) {
+ addr_len = sizeof(addr);
+ if(getpeername(0, (struct sockaddr *)&addr, &addr_len) < 0) {
+ if(auth_debug_mode)
+ printf("getpeername failed\r\n");
+ Data(ap, KRB_REJECT, "getpeername failed", -1);
+ auth_finished(ap, AUTH_REJECT);
+ return;
+ }
+ r = krb_rd_req(&auth, KRB_SERVICE_NAME,
+ instance, addr.sin_addr.s_addr, &adat, "");
+ if (r) {
if (auth_debug_mode)
printf("Kerberos failed him as %s\r\n", name);
Data(ap, KRB_REJECT, (void *)krb_get_err_text(r), -1);
Index: appl/telnet/telnetd/telnetd.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/appl/telnet/telnetd/telnetd.c,v
retrieving revision 1.47
retrieving revision 1.48
diff -u -w -r1.47 -r1.48
--- telnetd.c 1997/10/29 01:26:58 1.47
+++ telnetd.c 1997/11/03 06:08:26 1.48
@@ -647,21 +647,7 @@
int
terminaltypeok(char *s)
{
- char buf[1024];
-
- if (terminaltype == NULL)
- return(1);
-
- /*
- * tgetent() will return 1 if the type is known, and
- * 0 if it is not known. If it returns -1, it couldn't
- * open the database. But if we can't open the database,
- * it won't help to say we failed, because we won't be
- * able to verify anything else. So, we treat -1 like 1.
- */
- if (tgetent(buf, s) == 0)
- return(0);
- return(1);
+ return 1;
}