Date: Sat, 13 May 89 16:24:51 MDT Subject: #42 - Unix Security Mailing List ---------------------------------------------------------------------- Editors Corner. Sigh. I have kept up hope that around every corner will be less work to do, and I would have more free time to spend with this list. Such has not been the case, and I can see that around the next few corners there will definitely be *more* work, so I feel I should give up the editorship of this mailing list. Strange as it sounds, it's hard to find an hour a week to put everything together. Between teaching, research, consulting, writing, and non-work related things my life is just too full, and getting more so. I have really enjoyed working with the list, and wish I had more time (past, present, and future). So... are there any volunteers to take over the list? (Not that it really only takes an hour a week, but that's probably a good minimum.) This will be the last issue I send out. I will post a message in news.sysadmin when someone has come forth, so that the event will be widely distributed (unlike some issues of the list!). Perhaps the list should be pared down to a smaller size of "really important people" with the net and the other list taking over. At any rate, there's the archives to place somewhere, etc. I believe news.sysadmin is the most appropriate place to carry on this discussion, and I will post a copy of this message there as well. I'll take part in the discussion time permitting, but this should be considered my official farewell... ---------------------------------------------------------------------- From: zardoz!felix!netsys.COM!len (Len Rose) Subject: Re: uucico & uuclean permissions Date: Sat, 31 Dec 88 1:42:54 EST Erik sez: > Because SCO put the lock files in /usr/spool/uucp, I was forced to > remove all of my useful communications programs, because users would > need to be effective uid==uucp to write lock files.... > Erik Murrey > MPX Data Systems, Inc. Why didn't you just protect them from execution by other users.. Am I missing something obvious? -- len@netsys.com {ames,att,rutgers}!netsys!len ---------------------------------------------------------------------- From: zardoz!ucsd!nprdc!stanonik (Ron Stanonik) Date: 7 January 1989 0750-PST (Saturday) Subject: Re: Contents of this mailing list Okay, let's take a topic that should be restricted. Berkeley recently sent out a fix for a security hole in passwd/chfn, mainly reducing the size of some buffers. Anyone know what hole was being exploited and how? Reducing buffer sizes suggests a collecting buffer somewhere, maybe the buf in replace, was being exceeded clobbering some nearby variables in "useful" way? Or maybe the 4kb limit in ndbm was being exceeded, again "usefully"? Anyone care to shed some light? We've made a number of changes to passwd to tailor the gecos field for our own uses. Without an understanding of the security hole recently fixed, maybe we've still got it even after reducing buffer sizes, because we have some additional gecoS:fields? Thanks, Ron Stanonik stanonik@nprdc.arpa ucsd!nprdc!stanonik ---------------------------------------------------------------------- Date: Wed, 4 Jan 89 12:39:42 PST From: neil@zardoz.uucp (Neil Gorsuch) Subject: mkdir discussion in news.admin usenet news This was posted in usenet news group news.admin: Newsgroups: news.admin Subject: mkdir() and security hole *****FIX**** Summary: how *TO* run a /bin/mkdir Keywords: mkdir hole fix Message-ID: <9466@merch.TANDY.COM> Date: 15 Dec 88 08:08:48 GMT Sender: doug@merch.TANDY.COM Reply-To: doug@merch.TANDY.COM (Doug Davis) Organization: lawnet Lines: 431 Before you flame, this got posted to news.admin because thats where the original message about this showed up. besides this is small. Attached is a version of /bin/mkdir that should eliminate the problem with the race condition that someone can take advantage of to cause a major security hole. Machines that have the mkdir() function call, most anything based on 4.2 BSD or later, will not need this program. Everyone else that I know of should want this. For security reasons I have elected not to describe the problem with /bin/mkdir fully, just suffice it to say that I have tested it on 11 differen't architectures and the "bug" existed on all of them. If your /bin/mkdir program is setuid root, you too probably have this bug as well. This mkdir first makes a directory to play in, which is owned by root and is mode 000. It is made in the same directory in which the user is requesting his directory. In this "secure" directory, to which the user allegedly has no access, the mknod(), chown(), and links for `.' and `..' are performed. The new directory is then linked into place. Finally, the "secure" directory is removed. Yes, there is a bit more overhead, but a much more secure program is worth it. As usual, I will accept mail, suggestions, comments, etc will be appreciated. Flames will be ignored. If anyone can poke security holes in this code I would really like to hear about it. BTW: I know the calls to rand() are not really needed. They just make it more fun for someone trying to defeat the code. Doug Davis -- Lawnet 1030 Pleasent Valley Lane. Arlington Texas 76015 817-467-3740 { sys1.tandy.com, motown!sys1, uiucuxc!sys1, killer!texbell } letni!doug "Talk about holes in UNIX, geeze thats nothing compaired with the security problems in the ship control programs of StarFleet." #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'Makefile' <<'END_OF_FILE' X# What kind of strrchr do we have? X# 'strrchr' for most newer unix's, 'rindex' for earlier editions X# STTRCHR = -DSTRRCHR=rindex STRRCHR = -DSTRRCHR=strrchr X# do you have a rand() function call? X# If you don't have one, comment out the line below RAND = -DRAND X X# for debugging, X# -DDEBUG X DEFINES = $(STRRCHR) $(DEBUG) $(RAND) SHELL = /bin/sh CC = /bin/cc CFLAGS = -O $(DEFINES) LDFLAGS = -n -s X all: mkdir X mkdir.o: mkdir.c X $(CC) $(CFLAGS) mkdir.c -c X mkdir: mkdir.o X $(CC) $(LDFLAGS) mkdir.o -o mkdir END_OF_FILE if test 510 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'mkdir.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mkdir.c'\" else echo shar: Extracting \"'mkdir.c'\" \(7645 characters\) sed "s/^X//" >'mkdir.c' <<'END_OF_FILE' X/* X * Secure mkdir program, solves that nasty race problem... X * X * 13 December 1988 Doug Davis doug@lenti.lawnet.com X * and John Elliot IV iv@trsvax.tandy.com X * X * X * Theory of operation: X * This mkdir first makes a directory to play in, which is X * owned by root and is mode 000. It is made in the same X * directory in which the user is requesting his directory. X * In this "secure" directory, to which the user allegedly X * has no access, the mknod(), chown(), and links for `.' X * and `..' are performed. The new directory is then linked X * into place. Finally, the "secure" directory is removed. X * X * This code copyright 1988 by Doug Davis (doug@letni.lawnet.com) X * You are free to modify, hack, fold, spindle, duplicate, pass-along X * give-away, publish, transmit, or mutlate this code in any maner, X * provided that you give credit where credit is due and don't pretend X * that you wrote it. X * X * If you do my lawyers (and I have a lot of lawyers) will teach you a lesson X * or two in copyright law that you will never ever forget. X */ X X#define MAXPATHLEN 128 /* maximum reasonanble path length */ X X#include X#include X#include X#ifdef DEBUG X# include X#else /*DEBUG*/ X# define NULL ((char *) 0) X#endif /*DEBUG*/ X X#define MKNODE 1 X#define LINK 2 X char *Malloc_Failed = "malloc() failed."; char *Doesnt_Exist = " does not exist."; char *Cannot_Access = "cannot access "; char *Already_Exist = " already exists."; char *Secure_Failed = "makedir secure parent failed "; char *Couldnt_Link = "Couldn't link to "; char *Mkdir_Failed = "makedir failed "; char *Chown_Failed = "chown() failed "; X extern char *STRRCHR(); extern char *malloc(); X extern int errno; extern int getpid(); X extern unsigned short getgid(); extern unsigned short getuid(); X X#ifdef RAND extern int rand(); X#else /*RAND*/ extern int getppid(); X#endif /*RAND*/ X extern long time(); X char *Progname; X main(argc, argv) int argc; char *argv[]; X{ X Progname = argv[0]; X errno = 0; X X if (argc < 2) { X print("Usage: "); X print(Progname); X print(" directory_name [ ... directory_name ]\n"); X exit(0); X } X X /* Catch those nasty signals that could cause us X * to mess up the filesystem */ X swat_sigs(); X X while (--argc) X md(*++argv); /* make each directory */ X X exit(errno); X} X X md(s) char *s; X{ X char *basename, *parent, *fullname; X char securename[MAXPATHLEN], securedir[MAXPATHLEN]; X long snum; X unsigned short myuserid, mygroupid; X struct stat sanity; X X /* find out who I really am */ X myuserid = getuid(); X mygroupid = getgid(); X X /* set up the pseudo-RANDom number generation system */ X#ifndef RAND X srand(getpid()); X#endif /*RAND*/ X X /* see if we are explicit or indirect */ X basename = STRRCHR(s, '/'); X if (basename == (char *) NULL) { X fullname = malloc(strlen(s)+1); X if (fullname == (char *) NULL) X error(Malloc_Failed, NULL, errno); X parent = malloc(2); X if (parent == (char *) NULL) X error(Malloc_Failed, NULL, errno); X parent[0] = '.'; X parent[1] = '\0'; X strcpy(fullname, s); X basename = s; X } else { X fullname = malloc(strlen(s)+1); X if (fullname == (char *) NULL) X error(Malloc_Failed, NULL, errno); X strcpy(fullname, s); X *basename = '\0'; X basename++; X parent = malloc(strlen(s) + 3); X if (parent == (char *) NULL) X error(Malloc_Failed, NULL, errno); X strcpy(parent, s); X strcat(parent, "/."); X } X X /* Generate the secure names ... */ X do { X /* round and round we go where we stop depends on X * the non-existance of securedir */ X snum = time((int *) 0); X#ifdef RAND X sprintf(securedir, "%s/%ld", parent, snum - (long)rand()); X sprintf(securename, "%s/%ld", securedir, snum + (long)rand()); X#else /*RAND*/ X sprintf(securedir, "%s/%ld", parent, snum - (long)getppid()); X sprintf(securename, "%s/%ld", securedir, snum + (long)getppid()); X snum += (long)getpid(); X#endif /*RAND*/ X } while (stat(securedir, &sanity) == 0); X X#ifdef DEBUG X /* spill the beans .. */ X printf("parent == %s\n", parent); X printf("basename == %s\n", basename); X printf("fullname == %s\n", fullname); X printf("securedir == %s\n", securedir); X printf("securename == %s\n", securename); X fflush(stdout); X#endif /*DEBUG*/ X X /* lets see if our parent directory is around... */ X if ((stat(parent, &sanity)) != 0) X error(parent, Doesnt_Exist, 0); X X /* find out if we can write here */ X if (canIwrite(&sanity, myuserid, mygroupid) != 0) X error(Cannot_Access, parent, 0); X X /* find out if we are going to stomp on something.. */ X if ((stat(fullname, &sanity)) == 0) X error(fullname, Already_Exist, 0); X X /* make secure parent directory (note the mode of 0) */ X if (makedir(parent, securedir, 0) > 0) X error(Secure_Failed, securedir, errno); X X /* now make our directory underneath it */ X if (makedir(parent, securename, 0777) > 0) X error(Mkdir_Failed, securedir, errno); X X /* do that eerie little chown() thats the "root" of all our problems */ X if (chown(securename, myuserid, mygroupid) != 0) X error(Chown_Failed, securename, errno); X X /* do a quick sanity check, just to annoy someone trying, unsccessfully X * I might add, to trick mkdir into chowning something it shouldn't.. */ X if ((stat(fullname, &sanity)) == 0) { X /* what happend? this wasn't here a couple of functions ago.. */ X unlink(securename); X rmdir(securedir); X error(fullname, Already_Exist, 0); X } X X /* okay, put it where it belongs */ X if ((link(securename, fullname)) < 0) X error(Couldnt_Link, fullname, errno); X X /* remove all our rubbish, and tidy everything up.. */ X unlink(securename); X rmdir(securedir); X if (parent != (char *) NULL) X free(parent); X if (fullname != (char *) NULL) X free(fullname); X return(0); X} X makedir(parent, dir, mode) char *parent, *dir; int mode; X{ X char dotdot[MAXPATHLEN]; X X#ifdef DEBUG X printf("mkdir(%s, %s)\n", parent, dir); X fflush(stdout); X#endif /*DEBUG*/ X X /* put the node together */ X if ((mknod(dir, S_IFDIR | mode, 0)) < 0) X return (MKNODE); X X /* make dot */ X strcpy(dotdot, dir); X strcat(dotdot, "/."); X if ((link(dir, dotdot)) < 0) X return (LINK); X X /* make dotdot */ X strcat(dotdot, "."); X if ((link(parent, dotdot)) < 0) X return (LINK); X X return (0); X} X rmdir(dir) char *dir; X{ X char dots[MAXPATHLEN]; X X#ifdef DEBUG X printf("rmdir(%s)\n", dir); X fflush(stdout); X#endif /*DEBUG*/ X X strcpy(dots, dir); X strcat(dots, "/."); X X /* unlink(".") */ X if (unlink(dots) < 0) X return (LINK); X X /* unlink("..") */ X strcat(dots, "."); X if (unlink(dots) < 0) X return (LINK); X X /* unlink the directory itself */ X if (unlink(dir) < 0) X return (LINK); X X return (0); X} X print(s) char *s; X{ X write(2, s, strlen(s)); X} X error(s1, s2, err) char *s1, *s2; int err; X{ X write(2, Progname, strlen(Progname)); X write(2, ": ", 2); X write(2, s1, strlen(s1)); X errno = err; X if (s2 != NULL) X write(2, s2, strlen(s2)); X if (err != 0) X perror(" "); X else X write(2, "\n", 1); X exit(errno); X} swat_sigs() X{ X register int i; X X for (i=SIGHUP; i<=NSIG ; i++) X signal(i, SIG_IGN); /* bye-bye */ X} canIwrite(stbuff, uid, gid) register struct stat *stbuff; register unsigned short uid, gid; X{ X /* we let root get away with anything... */ X if (uid == 0) X return(0); X X /* can I write in it as an OWNER ? */ X if (uid == stbuff->st_uid && stbuff->st_mode & 0200) X return(0); X X /* okay, so how about as a GROUP ? */ X if (gid == stbuff->st_gid && stbuff->st_mode & 0020) X return(0); X X /* alright, how about an OTHER ? */ X if (stbuff->st_mode & 0002) X return(0); X X /* okay, so I can't write here.. */ X return(-1); X} X#ifdef DEBUG unlink(s) char *s; X{ X printf("Unlink(%s)\n", s); X fflush(stdout); X} X#endif /*DEBUG*/ END_OF_FILE if test 7645 -ne `wc -c <'mkdir.c'`; then echo shar: \"'mkdir.c'\" unpacked with wrong size! fi chmod +x 'mkdir.c' # end of 'mkdir.c' fi echo shar: End of shell archive. exit 0 ---------------------------------------------------------------------- Date: Thu, 5 Jan 89 00:38:23 PST From: neil@zardoz.uucp (Neil Gorsuch) Subject: callbacks: use a different line Posted in usenet news group comp.sys.sun by Dan Franklin: As more people are trying to beef up security by having the system call them back to log in, it's probably worth a reminder: don't use the same telephone line (number) to call in and out. That would render the callback mechanism completely useless. The reason is that there is no reliable indication from the phone company to your modem that a caller has actually hung up. A penetrator can merely call in, request a login, cut off the modem carrier and stay on the line, simulating a dial tone if your modem checks for it (but many don't even do that). The modem can *try* to hang up, but with many phone systems the caller can keep the line open, at least for a little while. If the caller does get hung up, a quick redial can often reestablish the connection before the modem starts dialing. Even if the callback software checks for a ring indication and aborts the procedure any time it gets one, there is still a timing window you can get through if you're persistent. Even using a different line is not a defense, if the number can be discovered. The penetrator can just call it ahead of time. You must use a separate, unrelated (and unlisted) set of phone numbers. It's best if the numbers have a different exchange prefix, to make finding them really difficult. Disclaimer: I'm not a security expert, and this information is several years old. But phone systems don't change all that quickly, so I suspect it's all still true. ---------------------------------------------------------------------- Date: Thu, 5 Jan 89 01:20:16 PST From: neil@zardoz.uucp (Neil Gorsuch) Subject: V1.74 (security problem in passwd) Of interest to people with BSD source, posted to another mailing list and to usenet news group comp.bugs.4bsd.ucb-fixes: ------------------------------------------------------------------------ The CERT center received the following information from Keith Bostic at the Computer Systems Research Group at UC-Berkeley shortly after noon EST today (12/21). This note has also been posted to comp.bugs.4bsd.ucb-fixes. cert@sei.cmu.edu ------------------ Subject: security problem in passwd Index: bin/passwd.c 4.3BSD Description: There's a security problem associated with the passwd(1) program in all known Berkeley systems. This problem is also in most Berkeley derived systems, see your vendor for more information. Fix: Apply the following patch to the file src/bin/passwd.c and recompile/reinstall it. *** passwd.c.orig Wed Dec 21 08:57:41 1988 --- passwd.c Wed Dec 21 09:00:25 1988 *************** *** 332,337 **** --- 332,339 ---- return (crypt(pwbuf, saltc)); } + #define STRSIZE 100 + char * getloginshell(pwd, u, arg) struct passwd *pwd; *************** *** 338,344 **** int u; char *arg; { ! static char newshell[BUFSIZ]; char *cp, *valid, *getusershell(); if (pwd->pw_shell == 0 || *pwd->pw_shell == '\0') --- 340,346 ---- int u; char *arg; { ! static char newshell[STRSIZE]; char *cp, *valid, *getusershell(); if (pwd->pw_shell == 0 || *pwd->pw_shell == '\0') *************** *** 415,423 **** getfingerinfo(pwd) struct passwd *pwd; { ! char in_str[BUFSIZ]; struct default_values *defaults, *get_defaults(); ! static char answer[4*BUFSIZ]; answer[0] = '\0'; defaults = get_defaults(pwd->pw_gecos); --- 417,425 ---- getfingerinfo(pwd) struct passwd *pwd; { ! char in_str[STRSIZE]; struct default_values *defaults, *get_defaults(); ! static char answer[4*STRSIZE]; answer[0] = '\0'; defaults = get_defaults(pwd->pw_gecos); *************** *** 429,435 **** */ do { printf("\nName [%s]: ", defaults->name); ! (void) fgets(in_str, BUFSIZ, stdin); if (special_case(in_str, defaults->name)) break; } while (illegal_input(in_str)); --- 431,437 ---- */ do { printf("\nName [%s]: ", defaults->name); ! (void) fgets(in_str, STRSIZE, stdin); if (special_case(in_str, defaults->name)) break; } while (illegal_input(in_str)); *************** *** 440,446 **** do { printf("Room number (Exs: 597E or 197C) [%s]: ", defaults->office_num); ! (void) fgets(in_str, BUFSIZ, stdin); if (special_case(in_str, defaults->office_num)) break; } while (illegal_input(in_str) || illegal_building(in_str)); --- 442,448 ---- do { printf("Room number (Exs: 597E or 197C) [%s]: ", defaults->office_num); ! (void) fgets(in_str, STRSIZE, stdin); if (special_case(in_str, defaults->office_num)) break; } while (illegal_input(in_str) || illegal_building(in_str)); *************** *** 452,458 **** do { printf("Office Phone (Ex: 6426000) [%s]: ", defaults->office_phone); ! (void) fgets(in_str, BUFSIZ, stdin); if (special_case(in_str, defaults->office_phone)) break; remove_hyphens(in_str); --- 454,460 ---- do { printf("Office Phone (Ex: 6426000) [%s]: ", defaults->office_phone); ! (void) fgets(in_str, STRSIZE, stdin); if (special_case(in_str, defaults->office_phone)) break; remove_hyphens(in_str); *************** *** 464,470 **** */ do { printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone); ! (void) fgets(in_str, BUFSIZ, stdin); if (special_case(in_str, defaults->home_phone)) break; remove_hyphens(in_str); --- 466,472 ---- */ do { printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone); ! (void) fgets(in_str, STRSIZE, stdin); if (special_case(in_str, defaults->home_phone)) break; remove_hyphens(in_str); *************** *** 501,507 **** if (input_str[length-1] != '\n') { /* the newline and the '\0' eat up two characters */ printf("Maximum number of characters allowed is %d\n", ! BUFSIZ-2); /* flush the rest of the input line */ while (getchar() != '\n') /* void */; --- 503,509 ---- if (input_str[length-1] != '\n') { /* the newline and the '\0' eat up two characters */ printf("Maximum number of characters allowed is %d\n", ! STRSIZE-2); /* flush the rest of the input line */ while (getchar() != '\n') /* void */; ---------------------------------------------------------------------- Date: Thu, 5 Jan 89 20:43:44 PST From: zardoz!ucsd!brian (Brian Kantor) Subject: Stallman et al on the Internet Worm Date: 20 Dec 88 14:53 From: minow%thundr.DEC@decwrl.dec.com Subject: Stallman, Minsky and Drescher on the Internet Worm The following letter appeared in the Business section of the Boston Globe, 20 Dec 1988. [It does not represent the position of Digital Equipment Corporation (or my position, either). Martin Minow] Recent computer virus threatens American justice system, too The recent computer network virus was a prank designed to be harmless. A minor programming error made it replicate so much that it clogged Internet, a research network, with messages. Now some people want to punish this accident as deliberate sabotage. Yes, people should not clog networks. But the "worm" had parts designed to avoid clogging; one had an error. Research is error prone: punishing errors is futile if limited to errors in pranks. More rational is to keep critical computers off research networks, as the military does. Yes, another worm might be designed to destroy files. Some people are angry at these potential future crimes; so angry that they clamor to punish someone as an example, whether his own deeds deserve it or not. This clamor threatens the American tradition of justice for each individual -- something even more valuable than a working Internet. Richard Stallman Free Software Foundation, Cambridge. Henry Minsky and Gary Drescher MIT Artificial Intelligence Laboratory, Cambridge. ---------------------------------------------------------------------- Date: Fri, 6 Jan 89 10:59:10 EST From: ames!iuvax!bsu-cs!dhesi (Rahul Dhesi) Subject: Contents of this mailing list I am slightly concerned about the contents of this mailing list. I had originally supposed that the purpose of this mailing list was the posting of security-related messages that were not appropriate for posting in an open newsgroup. I was added to the list a few weeks ago, I find that almost nothing that I have received from this list so far (since I was added to the list a few weeks ago) has deserved to be restricted to a private mailing list. I highly recommend that all people sending a message to this list ask themselves if the message really should be restricted to this list. If not, it should probably be posted to an appropriate Usenet newsgroup and not sent to this list. Rahul Dhesi {iuvax,pur-ee}!bsu-cs!dhesi ---------------------------------------------------------------------- From: "Matt Crawford" Subject: Re: Contents of this mailing list Date: Fri, 06 Jan 89 14:02:24 CST I agree with Rahul entirely. Matt Crawford ---------------------------------------------------------------------- The UNIX Security Mailing List Ignore the headers on this list and mail to: ...!ncar!isis!security (mail for the list). ...!ncar!isis!sec-request (administrativia).