|
Vulnerability kerberos rsh, rcp and rlogin Affected Systems running kerberos kth-krb4 Description There has been discovered a security-hole in kerberized rsh, rcp and rlogin. 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 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. To activate the bug, it is required that there are valid tickets for the target user laying around somewhere on your system (usually in /tmp/). The hole allows any user on the system to gain privilegies of any other user including root. Note also that it appears the bsdi version of su uses kerbose tickets if kerbose is configured, but the BSDI kerberosIV implementation does not appear to have the problem (the tf_init() routine which opens the ticket file checks to see that the real uid of the process is either root or owns the ticket file). The hole has been successfully tested on kth-kerberos, but is suspected to exist on any other versions of kerberos. The bug was discovered by Mattias Amnefelt. Solution 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 should be changing 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). 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. 3. missing IP-nummer check in telnetd. This fix will of course be included in an upcoming version RSN. 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; }