Date: 11/11/97 10:16:00 PM From: Mattias Amnefelt Subject: BoS: Re: Major security-hole in kerberos rsh, rcp and rlogin. To: (""@LOCAL) 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 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; }