Subject: Security Mailing List - # 33 ------- Topics: Admin and new people on the list Anti-spoofing Home Directory Checker - Security/Sanity Aid Is 4.3's "lock" a potential problem? Trying to foil the spoofers A few fun security holes Security Mailing List, # 27 Re: public key encryptions broken pwchkr.c ---------------------------------------------------------------------------- Editor's corner. Newcomers to the list since last issue: Steve Grandi (grandi@noao) Jean F. Huens (jean@kulcs) James E. Conley (jec@iuvax) Robert C. White, Jr. (qetzal!rcw@scicom) Thomas Hutton (hutton@scubed) Dennis Page (denny@dsndata) Brian Reid (reid@decwrl) ---------------------------------------------------------------------------- Date: Tue, 24 Mar 87 22:11:59 PST From: hplabs!ucbvax!ucscc.UCSC.EDU!ucscc!haynes (99700000) Subject: Anti-spoofing I'd like to start a discussion on ways of foiling the spoofers - those program that users leave running on terminals that imitate the login sequence and collect the login name and password of the victim who happens to encounter it unwittingly. This can be quite a problem at a university where there are public terminal rooms. I can think of two general schemes. (1) one-time or pseudo-one-time passwords, so that the password collected by the spoofer doesn't work more than once. (2) Some sort of authentication by which the computer assures the user that he is talking to the real login program, and not to a spoofer. Are there other possibilities? Ruling out (1) as impractical in the university setting, we are left with (2). What can the system do that can't be counterfeited by the spoofer? The only things I have thought of involve the system saying something to the user between login name entry and prompting for password. I thought briefly of having the character echo turned off being a privileged function, so the spoofer can't do it and the user's password would be echoed in the screen if the user is talking to the spoofer. But making echo turnoff priviliged would no doubt break a great many useful programs, so that doesn't seem a reasonable possibility. Then we might have the system say something to the user that is unique to that user and that the spoofer doesn't know how to produce. For instance, the login program could encrypt the login name using a password unknown to the spoofer and print the result of the encryption as part of the prompt for password. The user would have to compare the cryptic response with a copy saved from a previous login session (or he could memorize it). While the cryptic response could be read by anyone looking over the user's shoulder, the spoofer would not likely have a list of login names and correct responses to use to counterfeit the system. And not knowing the encryption key the spoofer couldn't create the cryptic response from the login name afresh. My current thinking is that the user might create a file of a known name (say, .secret) and write into it some brief word or phrase of his own choosing. The login program, being privileged, can read that file and display the contents ahead of, or instead of, the password prompt. The spoofer program can't read the file, unless the user is so foolish as to make it publicly readable. Again, the spoofer is not likely to have a list of known login names and secrets. Anybody have a better idea? Jim Haynes ...ucbvax!ucscc!haynes haynes@ucbarpa.berkeley.edu haynes@ucscc.bitnet ---------------------------------------------------------------------------- Date: Wed, 25 Mar 87 15:12:28 EST From: John Owens Subject: Home Directory Checker - Security/Sanity Aid This was posted to comp.unix.wizards and net.sources, but I also wanted to send it here, both for those that don't read or get news, and so that it will be in the archives for posterity.... On the UNIX Security list, quite a while back, mention was made of problems that could occur when home directories of users are writable. (Installing |"some command" in ~uucp/.forward remotely and things like that.) This prompted me to write the enclosed program, both to check for this, and to help protect users against themselves. The program looks at all the home directories listed in /etc/passwd, and prints a message if they don't exist, are not directories, or their mode is not in the "table" of "OK" modes. I'm using stat() instead of lstat(), so symbolic links are perfectly acceptable, as long as they point to directories.... This program should run on any version of UNIX that I can think of; if it doesn't, please let me know. The list of good modes is, of course, subjective. I initially used the first set, then added the second set based on the output of the first run. I didn't add all the mismatched modes I found; just the ones that were fairly normal and that I didn't want to hear about.... The program is surprisingly (to me) fast. It took under a second on our decently loaded VAX-11/785 running 4.3BSD with 501 passwd entries! This program is placed in the public domain - you have only your conscience to stop you from saying "hey, look at this neat program I wrote".... Enjoy! John Owens Old Dominion University - Norfolk, Virginia, USA john@ODU.EDU old arpa: john%odu.edu@RELAY.CS.NET +1 804 440 3915 old uucp: {seismo,harvard,sun,hoptoad}!xanth!john #include #include #include /* These are "OK" modes for home directories to have. */ /* End with 0 (so 0 can't be "OK", but who cares). */ int table[] = { /* always good modes: */ 0700, 0750, 0770, 0755, 0775, /* some people here like to do wierd things */ 0711, 0751, 0771, 0 }; main(argc,argv) char **argv; { register int mode; register int *p; struct passwd *pp; static struct stat statb; if (argc != 1) { printf("Usage: %s\n",argv[0]); exit(1); } while ((pp = getpwent()) != (struct passwd *)0) { if (stat(pp->pw_dir,&statb) < 0) { perror(pp->pw_dir); continue; } if ((statb.st_mode & S_IFMT) != S_IFDIR) { printf( "User %s's home directory %s is not a directory! (mode 0%o)\n", pp->pw_name,pp->pw_dir,statb.st_mode); continue; } mode = statb.st_mode & ~S_IFMT; for (p=table;*p;p++) if (mode == *p) goto ok; /* note that 3.3 will print 4 if needed */ printf("User %s's home directory %s is mode 0%3.3o!\n", pp->pw_name,pp->pw_dir,mode); ok: ; } exit(0); } ---------------------------------------------------------------------------- Date: Sat, 28 Mar 87 16:47:32 CST From: seismo!uwvax!astroatc!brucec (Bruce Cantrall) Subject: Is 4.3's "lock" a potential problem? Hello, I was wondering if someone could lock up his/her terminal and then just type in possible root passwords until they find it. I am sure this must of come up when lock was first implemented. Thank You, Bruce Cantrall Astronautics Corp of America |{seismo,harvard}!uwvax!astroatc!brucec |{ihnp4,decvax}!nicmad!astroatc!brucec ---------------------------------------------------------------------------- Date: Wed, 25 Mar 87 16:42:59 PST From: ihnp4!ucbvax!ucscc.UCSC.EDU!ucscc!haynes (99700000) Subject: Trying to foil the spoofers I'd like to start a discussion on ways of foiling the spoofers - those program that users leave running on terminals that imitate the login sequence and collect the login name and password of the victim who happens to encounter it unwittingly. This can be quite a problem at a university where there are public terminal rooms. I can think of two general schemes. (1) one-time or pseudo-one-time passwords, so that the password collected by the spoofer doesn't work more than once. (2) Some sort of authentication by which the computer assures the user that he is talking to the real login program, and not to a spoofer. Are there other possibilities? Ruling out (1) as impractical in the university setting, we are left with (2). What can the system do that can't be counterfeited by the spoofer? The only things I have thought of involve the system saying something to the user between login name entry and prompting for password. I thought briefly of having the character echo turned off being a privileged function, so the spoofer can't do it and the user's password would be echoed in the screen if the user is talking to the spoofer. But making echo turnoff priviliged would no doubt break a great many useful programs, so that doesn't seem a reasonable possibility. Then we might have the system say something to the user that is unique to that user and that the spoofer doesn't know how to produce. For instance, the login program could encrypt the login name using a password unknown to the spoofer and print the result of the encryption as part of the prompt for password. The user would have to compare the cryptic response with a copy saved from a previous login session (or he could memorize it). While the cryptic response could be read by anyone looking over the user's shoulder, the spoofer would not likely have a list of login names and correct responses to use to counterfeit the system. And not knowing the encryption key the spoofer couldn't create the cryptic response from the login name afresh. My current thinking is that the user might create a file of a known name (say, .secret) and write into it some brief word or phrase of his own choosing. The login program, being privileged, can read that file and display the contents ahead of, or instead of, the password prompt. The spoofer program can't read the file, unless the user is so foolish as to make it publicly readable. Again, the spoofer is not likely to have a list of known login names and secrets. Anybody have a better idea? Jim Haynes ...ucbvax!ucscc!haynes haynes@ucbarpa.berkeley.edu haynes@ucscc.bitnet ---------------------------------------------------------------------------- Date: Sun, 29 Mar 87 22:55:57 PST From: hoptoad.UUCP!gnu@cgl.ucsf.edu (John Gilmore) Subject: A few fun security holes In conversation over cognac this evening a few fun holes came up: * AT&T 3B Unix systems reportedly include a "trouble" entry in the passwd file, with no password. The wonderful :-) menu-driven tools for administering the system will not give it a password. If you have such a system, you can fix it by editing /etc/passwd by hand. This is similar to the well-known login: field password: service entry on VMS. * These systems are also distributed with /etc read-write by the world, making it easy for anyone to replace /etc/passwd for example. * On some systems, to find out the names of files in a directory where you don't have read permission, you can often use "du", which will happily report the names of files that it can't access. * A good way to coredump the shell on many systems, including my Sun (release 3.0) is to run the following command: $ << `echo xxx` After the first line of input, it coredumps. Copyright 1987 John Gilmore; you can redistribute only if your recipients can. (This is an effort to bend Stargate to work with Usenet, not against it.) {sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu gnu@ingres.berkeley.edu ---------------------------------------------------------------------------- Date: Fri, 3 Apr 87 19:44:56 EST From: seismo!EDDIE.MIT.EDU!garp!henry (Henry Mensch) Posted-Date: Fri, 3 Apr 87 19:44:56 EST In-Reply-To: seismo!isis!aburt@EDDIE.MIT.EDU's message of Wed, 1 Apr 87 08:34:53 mst Subject: Security Mailing List, # 27 I've received 27, 28, and 29 three or four times now. When will this stop? # # Henry Mensch / / E40-358C MIT, Cambridge, MA # {ames,cca,rochester,mit-eddie}!garp!henry ---------------------------------------------------------------------------- Reply-To: James M Galvin Subject: Re: public key encryptions broken Date: Fri, 03 Apr 87 23:54:05 -0500 From: James M Galvin I don't remember off hand how to get mail back to Lyle, nor am I sure this discussion is appropriate for security, but here goes. > Date: Wed Jun 25 16:01:32 MDT 1986 > From: Lyle McElhaney > Subject: public key encryptions broken > Evi Nemeth, a professor at Colorado University, co-hosted a tutorial at > the Atlanta USENIX conference concerning the practical aspects of network > maintenence. She mentioned in an aside that she had broken the common > public-key method used to distribute keys for various encryption schemes. > It might be of interest to the list that the public-key method based on a > key size of 127 (128?) bits has indeed been broken, at the cost of several > weekends of time on several (10?) Denelcor HEP 1000 machines (this isn't > advertising; the company went defunct last year) about two years ago. Well, I am sorry I missed USENIX, but which public-key method are we talking about here? There are basically three: those based on the knapsack problem, those based on computing logarithms in GF(p) fields and those based on factoring an integer which is the product of two large primes. The first is my vote for "common", in that it has been around the longest (not by much though) and studied the most (at least there is the most written about it). The first has long since been broken, with various weaknesses depending on the "trapdoor" selected for the knapsack. The second has some versions with weaknesses but how weak a given instance is is not known. The third is still our best hope for a public key system, with no breaks that I know of. It is in fact a commercially available product. Jim "will I ever finish my disseration that needs a public key system" Galvin ---------------------------------------------------------------------------- Date: Tue, 7 Apr 87 11:33:34 mst From: pyramid!ptsfa!jmc Subject: pwchkr.c I've extensively modified the password breaker program that was posted to the security mailing list a while ago. Besides reformatting it for my aesthetic taste (:-), I added 'double login' checking (foofoo where logid is foo), leading and trailing special characters (common in V.2+ where passwd wants some special characters) and 'big' dictionary (I have source to UNIX spell dictionary). It can also try to break a single user's password. Be warned, you need a Cray if you turn everything on and have the UNIX spell dictionary and a large password file. I also modified the non-root escape (now commented out) to say "memory fault" rather than give a cute message - why tell them what is going on - let them disassemble the binary if they really want to know! PS: This works on V.2, I don't know about 4.2, 4.3. ============================================================= /* * pwchkr - find logins with crackable passwords * * WARNING: if all options enabled it can run for WEEKS on a large /etc/password * Revision 1.1 85/09/10 16:00:56 root * jmc 5/10/87 - cb(1), hacked and added 'x' option special chars checking * jmc 5/13/87 - added single user and LARGE dictionary checking */ #include #include #include #ifndef lint static char *rcsid = "$Header: pwchkr.c,v 2.0 87/03/10 16:00:56 jmc Exp $"; #endif int verbose = 0, singles = 0, backwards = 0, dictfile = 0, checkgecos = 0, checkcase = 0, chknulls = 0, printit = 0, users = 0, chkwords = 0, checkfchars = 0; char *fchars = "0123456789,<.>/?;:[{]}`~=+-_)(*&^%$#@! "; char *strchr(), *reverse(); long atol(); FILE *fopen(); char *fgets(); static FILE *df = NULL, /* extended LARGE dictionary */ *pwf = NULL, /* passwd file */ *wlf = NULL; /* word list file */ char *logid = NULL, *Wordlist = NULL; main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind, opterr; register int c; /* don't give the cracker an even break */ /* if (getuid()) { printf("memory fault\n"); exit(1); } */ while ((c = getopt(argc, argv, "bcd:f:gl:npsuvw:x")) != EOF) switch(c) { case 'b': /* forwards and backwards */ backwards++; break; case 'c': /* check cases */ checkcase++; break; case 'd': /* large dictionary files */ dictfile++; if ((df = fopen(optarg,"r")) == NULL) { perror(optarg); exit(1); } break; case 'f': /* use alternate file */ if (setpwent(optarg)) { perror(optarg); exit(1); } printf("Using %s instead of /etc/passwd\n", optarg); break; case 'g': /* use gecos */ checkgecos++; break; case 'l': /* use one login id */ logid = optarg; break; case 'n': /* null passwords */ chknulls++; break; case 'p': /* print out password when found */ printit++; break; case 's': /* single letter passwords */ singles++; break; case 'u': /* print out users as testing */ users++; break; case 'v': /* motormouth */ verbose++; break; case 'w': /* use file of likely passwords */ Wordlist = optarg; break; case 'x': /* extended check of 'funny' chars */ checkfchars++; break; case '?': putopt(); exit(0); } chkpw(); endpwent(); exit(0); } #define ARB_CONST 25 #define GUESSLEN 100 chkpw() { int c; register char *cp, *cp2; register struct passwd *pwd; struct passwd *getpwent(); char guess[GUESSLEN]; char *wordarray[ARB_CONST]; char *malloc(), **wordptr, **endptr; int done = 0; if (Wordlist) { if ((wlf = fopen(Wordlist,"r")) == NULL) { perror(Wordlist); exit(1); } wordptr = wordarray; /* * note that endptr points to space OUTSIDE of wordarray */ endptr = wordarray + (sizeof(wordarray)/sizeof(char *)); while (fscanf(wlf,"%[^\n]\n",guess) != EOF) { if (wordptr == endptr) { fprintf(stderr,"ARB_CONST %d too small.\n", ARB_CONST); exit(1); } if ((*wordptr = malloc(1+strlen(guess))) == NULL) { fprintf(stderr,"malloc: wordlist too big\n"); exit(1); } strcpy(*wordptr, guess); wordptr++; } *wordptr = NULL; } while ((pwd = getpwent()) != 0 ) { if ((logid != NULL) && strcmp(logid, pwd->pw_name)) continue; if (strlen(pwd->pw_passwd) != 13) /* no encrypted passwd */ { if (verbose || users) printf("\tnot encrypted %s \"%s\"\n", pwd->pw_name, pwd->pw_gecos); continue; } else if (verbose || users) printf("%s \"%s\"\n", pwd->pw_name, pwd->pw_gecos); if (pwd->pw_passwd == '\0') { if (chknulls) printf("Problem: null passwd:\t%s\tshell: %s\n", pwd->pw_name, pwd->pw_shell); continue; } /* try name and variants */ strcpy(guess, pwd->pw_name); if (uandltry(pwd, guess)) /* try user's login name */ continue; strcat(guess, pwd->pw_name); if (uandltry(pwd, guess)) /* try login name repeated */ continue; c = strlen(pwd->pw_name); /* reusing variable 'c' */ strcpy(&guess[c+1], pwd->pw_name); /* leave a one char hole */ cp = fchars; /* reusing variable 'cp' */ while (*cp != NULL) { guess[c] = *cp++; if (uandltry(pwd, guess)) /* name?name try */ continue; } if (checkgecos) /* try gecos field */ { strcpy(guess, pwd->pw_gecos); cp = guess; if (*cp == '-') /* special gecos field */ cp++; if ((cp2 = strchr(cp, ';')) != NULL) *cp2 = '\0'; for (;;) { if ((cp2 = strchr(cp, ' ')) == NULL) { if (uandltry(pwd, cp)) done++; break; } *cp2 = '\0'; if (uandltry(pwd, cp)) { done++; break; } cp = ++cp2; } } if (!done && dictfile) /* try dictionary file */ { rewind(df); cp = guess; while ((c = getc(df)) != EOF) { if (c == '\n') { if ((cp - guess) > 8) guess[8] = '\0'; else *cp = '\0'; if(uandltry(pwd, guess)) { done++; break; } cp = guess; continue; } *cp++ = c; } } if (!done && Wordlist) /* try wordlist */ { wordptr = wordarray; while (endptr != wordptr) { if (*wordptr == NULL) break; if (uandltry(pwd, *wordptr++)) { done++; break; } } } if (!done && singles) /* try single letters and digits */ { guess[1] = '\0'; for (guess[0]='a'; guess[0] <= 'z'; guess[0]++) if (ftry(pwd, guess)) break; for (guess[0]='A'; guess[0] <= 'Z'; guess[0]++) if (ftry(pwd, guess)) break; for (guess[0]='0'; guess[0] <= '9'; guess[0]++) if (ftry(pwd, guess)) break; } } } /* * Stands for "upper and lower" try. Calls funny char append try, * with the supplied version of the password, and with * an upper and lowercase version of the password. If the user doesn't * want to try upper and lower case then we just return after the one * check. */ uandltry (pwd,guess) char *guess; struct passwd *pwd; { register char *cp; char tbuf[100]; int alllower, allupper; alllower = allupper = 1; if (ftry(pwd, guess) || (backwards && ftry(pwd, reverse(guess)))) return (1); if (!checkcase) return(0); strcpy (tbuf, guess); cp = tbuf-1; while (*++cp) { if (isupper(*cp)) alllower = 0; if (islower(*cp)) allupper = 0; } if (!allupper) { for (cp=tbuf; *cp != '\0'; cp++) if (islower (*cp)) *cp += 'A' - 'a'; if (ftry(pwd, tbuf) || (backwards && ftry(pwd, reverse(tbuf)))) return(1); } if (!alllower) { for ( cp = tbuf; *cp != '\0'; cp++) if (isupper (*cp)) *cp += 'a' - 'A'; if (ftry(pwd, tbuf) || (backwards && ftry(pwd, reverse(tbuf)))) return(1); } return(0); } /* append funny chars if selected */ ftry(pwd,guess) register struct passwd *pwd; char *guess; { char *fptr; char tguess[100]; char t2guess[100]; short guesslen; if (try(pwd, guess)) return(1); if (!checkfchars) return(0); fptr = fchars; guesslen = strlen(guess); /* first trailing char */ strcpy(tguess, guess); /* some parameters fixed len - defensive prog */ tguess[guesslen+1] = NULL; /* put in real end of string */ strcpy(t2guess+1, guess); /* leading char guess */ while (*fptr != NULL) { tguess[guesslen] = *fptr; if (try(pwd, tguess)) return(1); t2guess[0] = *fptr++; /* now leading char */ if (try(pwd, t2guess)) return(1); } } try(pwd,guess) register struct passwd *pwd; char *guess; { register char *cp; char *crypt (); if (verbose) printf("Trying \"%s\" on %s\n", guess, pwd->pw_name); if (! guess || ! *guess) return(0); cp = crypt(guess, pwd->pw_passwd); if (strcmp(cp, pwd->pw_passwd)) return (0); if (printit) printf("Problem: Guessed:\t%s\tshell: %s passwd: %s\n", pwd->pw_name, pwd->pw_shell, guess); else printf("Problem: Guessed:\t%s\tshell: %s\n", pwd->pw_name, pwd->pw_shell); fflush(stdout); return(1); } putopt() { fprintf(stderr,"b:\t\tcheck forwards and backwards\n"); fprintf(stderr,"c:\t\tcheck all-upper and all-lower case\n"); fprintf(stderr,"d: file:\tcheck large dictionary file\n"); fprintf(stderr,"f: file:\tuse alternate to /etc/passwd\n"); fprintf(stderr,"g:\t\tuse Full name portion of the gecos field\n"); fprintf(stderr,"l: name:\tcheck one login id\n"); fprintf(stderr,"n:\t\tcomplain about null passwords\n"); fprintf(stderr,"p:\t\tprint the password when guessed\n"); fprintf(stderr,"s:\t\tcheck the single letters a-z, A-Z, 0-9\n"); fprintf(stderr,"u:\t\toutput username currently being checked\n"); fprintf(stderr,"w: file:\tuse indicated small file\n"); fprintf(stderr,"v:\t\tverbose -- list all guesses on stdout\n"); fprintf(stderr,"x:\t\textended check - lead/trail chars\n"); } /* ============== end of PW guessing program ===========================*/ /* * reverse a string */ char *reverse(str) char *str; { register char *ptr; register int len; char *malloc(); if ((ptr = malloc((len = strlen(str))+1)) == NULL) return(NULL); ptr += len; *ptr = '\0'; while (*str && (*--ptr = *str++)) ; return(ptr); } /* =================== pw functions ============== */ #define MAXUID 0x7fff /* added by tonyb 12/29/83 */ /* altered to a reasonable number - mae 8/20/84 */ /* * Add a parameter to "setpwent" so I can override the file name. */ setpwent(file) char *file; { if ((pwf = fopen(file,"r")) == NULL) return(1); return(0); } endpwent() { fclose(pwf); pwf = NULL; } char * pwskip(p) register char *p; { while(*p && *p != ':' && *p != '\n') ++p; if(*p == '\n') *p = '\0'; else if(*p) *p++ = '\0'; return(p); } char PASSWD[] = "/etc/passwd"; char EMPTY[] = ""; static char line[BUFSIZ+1]; static struct passwd passwd; struct passwd * getpwent() { register char *p; long x; if(pwf == NULL) if (setpwent(PASSWD)) { perror(PASSWD); return(NULL); } p = fgets(line, BUFSIZ, pwf); if(p == NULL) return(0); passwd.pw_name = p; p = pwskip(p); passwd.pw_passwd = p; p = pwskip(p); x = atol(p); passwd.pw_uid = (x < 0 || x > MAXUID)? (MAXUID+1): x; p = pwskip(p); x = atol(p); passwd.pw_gid = (x < 0 || x > MAXUID)? (MAXUID+1): x; passwd.pw_comment = EMPTY; p = pwskip(p); passwd.pw_gecos = p; p = pwskip(p); passwd.pw_dir = p; p = pwskip(p); passwd.pw_shell = p; (void) pwskip(p); p = passwd.pw_passwd; return(&passwd); } ---------------------------------------------------------------------------- The UNIX Security Mailing List Ignore the headers on this list and mail to: ...!hao!isis!security (mail for the list). ...!hao!isis!sec-request (administrativia).