From owner-freebsd-questions Fri Nov 1 19:03:36 1996 Return-Path: owner-questions Received: (from root@localhost) by freefall.freebsd.org (8.7.5/8.7.3) id TAA09548 for questions-outgoing; Fri, 1 Nov 1996 19:03:36 -0800 (PST) Received: from scanner.worldgate.com (scanner.worldgate.com [198.161.84.3]) by freefall.freebsd.org (8.7.5/8.7.3) with ESMTP id TAA09522 for ; Fri, 1 Nov 1996 19:03:26 -0800 (PST) Received: from znep.com (uucp@localhost) by scanner.worldgate.com (8.7.5/8.7.3) with UUCP id UAA17569; Fri, 1 Nov 1996 20:02:58 -0700 (MST) Received: from localhost (marcs@localhost) by alive.ampr.ab.ca (8.7.5/8.7.3) with SMTP id UAA24232; Fri, 1 Nov 1996 20:02:06 -0700 (MST) Date: Fri, 1 Nov 1996 20:02:05 -0700 (MST) From: Marc Slemko X-Sender: marcs@alive.ampr.ab.ca To: "S(pork)" cc: freebsd-questions@FreeBSD.org Subject: Re: lpr hole In-Reply-To: Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: owner-questions@FreeBSD.org X-Loop: FreeBSD.org Precedence: bulk freebsd-security is the appropriate list for such things; the problem has been discussed there. There is also a freebsd-security-notifications list that is a moderated low volume announcements list, but it doesn't get as much traffic as it should. As usual, to subscribe send mail to majordomo@freebsd.org with 'subscribe listname'. There is a fix for the lpr exploit, and other exploits in lpr. It has been added to the -current branch (latest development stuff; don't use unless you know what you are doing) and -stable (few changes from 2.1.5; important fixes like this make it into -stable). If you are concerned about things like this, and have room for a full source tree, it is probably worth keeping up to date with -stable with either CTM, sup or CVSup. I am including the lpr patch (against 2.1.5) below (it doesn't just fix the problem the script exploits, but also several others), but be warned that there are other holes that are just as bad, most of them fixed in -stable. On Wed, 30 Oct 1996, S(pork) wrote: > Hi, > > I recently found an exploit for lpr that will allow root access by anyone > with an account on the system. As far as I know, this affects all FBSD. > A temp fix is to chmod -s it, but I wonder if anyone has a patch for this. > The exploit itself has been around for a while, but it seems to be > resurfacing (as they always do) and coming into vogue... From what I > gather it's some sort of race/overflow thing that makes lpr make you a > nice little root owned SUID shell. I also have a few other little things > I've found; is there any sort of security related list/archive for FBSD? > CERT is so ridiculously behind on these things it's not even funny. > > Curious about security, > > Charles > Index: lpr/lpc/cmds.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lpc/cmds.c,v retrieving revision 1.2 retrieving revision 1.2.4.1 diff -c -r1.2 -r1.2.4.1 *** cmds.c 1995/05/30 03:47:58 1.2 --- cmds.c 1996/11/01 04:56:57 1.2.4.1 *************** *** 276,282 **** d1 = (struct dirent **)a; d2 = (struct dirent **)b; ! if (c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)) return(c1); c1 = (*d1)->d_name[0]; c2 = (*d2)->d_name[0]; --- 276,282 ---- d1 = (struct dirent **)a; d2 = (struct dirent **)b; ! if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3))) return(c1); c1 = (*d1)->d_name[0]; c2 = (*d2)->d_name[0]; *************** *** 304,310 **** SD = _PATH_DEFSPOOL; printf("%s:\n", printer); ! for (lp = line, cp = SD; *lp++ = *cp++; ) ; lp[-1] = '/'; --- 304,310 ---- SD = _PATH_DEFSPOOL; printf("%s:\n", printer); ! for (lp = line, cp = SD; (*lp++ = *cp++); ) ; lp[-1] = '/'; *************** *** 591,597 **** cp1 = buf; while (--argc >= 0) { cp2 = *argv++; ! while (*cp1++ = *cp2++) ; cp1[-1] = ' '; } --- 591,597 ---- cp1 = buf; while (--argc >= 0) { cp2 = *argv++; ! while ((*cp1++ = *cp2++)) ; cp1[-1] = ' '; } *************** *** 814,820 **** fd = open(line, O_RDONLY); if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { (void) close(fd); /* unlocks as well */ ! printf("\tno daemon present\n"); return; } (void) close(fd); --- 814,820 ---- fd = open(line, O_RDONLY); if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { (void) close(fd); /* unlocks as well */ ! printf("\tprinter idle\n"); return; } (void) close(fd); Index: lpr/lpc/lpc.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lpc/lpc.c,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.1 diff -c -r1.1.1.1 -r1.1.1.1.6.1 *** lpc.c 1994/05/26 05:21:55 1.1.1.1 --- lpc.c 1996/11/01 04:57:02 1.1.1.1.6.1 *************** *** 171,177 **** longest = 0; nmatches = 0; found = 0; ! for (c = cmdtab; p = c->c_name; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return(c); --- 171,177 ---- longest = 0; nmatches = 0; found = 0; ! for (c = cmdtab; (p = c->c_name); c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return(c); Index: lpr/lpd/lpd.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lpd/lpd.c,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.1 diff -c -r1.1.1.1 -r1.1.1.1.6.1 *** lpd.c 1994/05/26 05:21:53 1.1.1.1 --- lpd.c 1996/11/01 04:57:09 1.1.1.1.6.1 *************** *** 176,181 **** --- 176,182 ---- } #define mask(s) (1 << ((s) - 1)) omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); + (void) umask(07); signal(SIGHUP, mcleanup); signal(SIGINT, mcleanup); signal(SIGQUIT, mcleanup); *************** *** 190,195 **** --- 191,197 ---- syslog(LOG_ERR, "ubind: %m"); exit(1); } + (void) umask(0); sigsetmask(omask); FD_ZERO(&defreadfds); FD_SET(funix, &defreadfds); *************** *** 242,247 **** --- 244,253 ---- domain = AF_INET, fromlen = sizeof(frominet); s = accept(finet, (struct sockaddr *)&frominet, &fromlen); + if (frominet.sin_port == htons(20)) { + close(s); + continue; + } } if (s < 0) { if (errno != EINTR) *************** *** 459,467 **** register FILE *hostf; int first = 1; extern char *inet_ntoa(); f->sin_port = ntohs(f->sin_port); ! if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) fatal("Malformed from address"); /* Need real hostname for temporary filenames */ --- 465,475 ---- register FILE *hostf; int first = 1; extern char *inet_ntoa(); + int good = 0; f->sin_port = ntohs(f->sin_port); ! if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED || ! f->sin_port == htons(20)) fatal("Malformed from address"); /* Need real hostname for temporary filenames */ *************** *** 471,480 **** fatal("Host name for your address (%s) unknown", inet_ntoa(f->sin_addr)); ! (void) strncpy(fromb, hp->h_name, sizeof(fromb)); from[sizeof(fromb) - 1] = '\0'; from = fromb; hostf = fopen(_PATH_HOSTSEQUIV, "r"); again: if (hostf) { --- 479,502 ---- fatal("Host name for your address (%s) unknown", inet_ntoa(f->sin_addr)); ! (void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1); from[sizeof(fromb) - 1] = '\0'; from = fromb; + /* Check for spoof, ala rlogind */ + hp = gethostbyname(fromb); + if (!hp) + fatal("hostname for your address (%s) unknown", + inet_ntoa(f->sin_addr)); + for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) { + if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr, + sizeof(f->sin_addr))) + good = 1; + } + if (good == 0) + fatal("address for your hostname (%s) not matched", + inet_ntoa(f->sin_addr)); + hostf = fopen(_PATH_HOSTSEQUIV, "r"); again: if (hostf) { *************** *** 493,507 **** fatal("Your host does not have line printer access"); /*NOTREACHED*/ } - - - - - - - - - - - - --- 515,517 ---- Index: lpr/lpd/printjob.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lpd/printjob.c,v retrieving revision 1.4.4.2 retrieving revision 1.4.4.3 diff -c -r1.4.4.2 -r1.4.4.3 *** printjob.c 1995/10/06 10:30:44 1.4.4.2 --- printjob.c 1996/11/01 04:57:12 1.4.4.3 *************** *** 271,276 **** --- 271,278 ---- if (TR != NULL) /* output trailer */ (void) write(ofd, TR, strlen(TR)); } + (void) close(ofd); + (void) wait(NULL); (void) unlink(tempfile); exit(0); } Index: lpr/lpd/recvjob.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lpd/recvjob.c,v retrieving revision 1.2 retrieving revision 1.2.4.1 diff -c -r1.2 -r1.2.4.1 *** recvjob.c 1995/05/30 03:48:01 1.2 --- recvjob.c 1996/11/01 04:57:17 1.2.4.1 *************** *** 170,176 **** * returns */ strcpy(cp + 6, from); ! strcpy(tfname, cp); tfname[0] = 't'; if (!chksize(size)) { (void) write(1, "\2", 1); --- 170,177 ---- * returns */ strcpy(cp + 6, from); ! strncpy(tfname, cp, sizeof tfname-1); ! tfname[sizeof tfname-1] = '\0'; tfname[0] = 't'; if (!chksize(size)) { (void) write(1, "\2", 1); *************** *** 197,203 **** (void) write(1, "\2", 1); continue; } ! (void) strcpy(dfname, cp); if (index(dfname, '/')) frecverr("readjob: %s: illegal path name", dfname); --- 198,205 ---- (void) write(1, "\2", 1); continue; } ! (void) strncpy(dfname, cp, sizeof dfname-1); ! dfname[sizeof dfname-1] = '\0'; if (index(dfname, '/')) frecverr("readjob: %s: illegal path name", dfname); Index: lpr/lpr/lpr.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lpr/lpr.c,v retrieving revision 1.2.4.2 retrieving revision 1.2.4.4 diff -c -r1.2.4.2 -r1.2.4.4 *** lpr.c 1995/10/09 08:39:17 1.2.4.2 --- lpr.c 1996/11/01 04:57:36 1.2.4.4 *************** *** 111,117 **** static int test __P((char *)); static int checkwriteperm __P((char*, char *)); ! void main(argc, argv) int argc; char *argv[]; --- 111,117 ---- static int test __P((char *)); static int checkwriteperm __P((char*, char *)); ! int main(argc, argv) int argc; char *argv[]; *************** *** 288,294 **** /* * Check to make sure queuing is enabled if userid is not root. */ ! (void) sprintf(buf, "%s/%s", SD, LO); if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010)) fatal2("Printer queue is disabled"); /* --- 288,294 ---- /* * Check to make sure queuing is enabled if userid is not root. */ ! (void) snprintf(buf, sizeof(buf), "%s/%s", SD, LO); if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010)) fatal2("Printer queue is disabled"); /* *************** *** 331,337 **** continue; /* file unreasonable */ if (sflag && (cp = linked(arg)) != NULL) { ! (void) sprintf(buf, "%d %d", statb.st_dev, statb.st_ino); card('S', buf); if (format == 'p') card('T', title ? title : arg); --- 331,338 ---- continue; /* file unreasonable */ if (sflag && (cp = linked(arg)) != NULL) { ! (void) snprintf(buf, sizeof(buf), "%d %d", statb.st_dev, ! statb.st_ino); card('S', buf); if (format == 'p') card('T', title ? title : arg); *************** *** 349,360 **** printf("%s: %s: not linked, copying instead\n", name, arg); if ((i = open(arg, O_RDONLY)) < 0) { printf("%s: cannot open %s\n", name, arg); ! continue; } - copy(i, arg); - (void) close(i); - if (f && unlink(arg) < 0) - printf("%s: %s: not removed\n", name, arg); } if (nact) { --- 350,361 ---- printf("%s: %s: not linked, copying instead\n", name, arg); if ((i = open(arg, O_RDONLY)) < 0) { printf("%s: cannot open %s\n", name, arg); ! } else { ! copy(i, arg); ! (void) close(i); ! if (f && unlink(arg) < 0) ! printf("%s: %s: not removed\n", name, arg); } } if (nact) { *************** *** 444,450 **** static char buf[BUFSIZ]; if (*file != '/') { ! if (getwd(buf) == NULL) return(NULL); while (file[0] == '.') { switch (file[1]) { --- 445,451 ---- static char buf[BUFSIZ]; if (*file != '/') { ! if (getcwd(buf,sizeof(buf)) == NULL) return(NULL); while (file[0] == '.') { switch (file[1]) { *************** *** 481,487 **** register int len = 2; *p1++ = c; ! while ((c = *p2++) != '\0') { *p1++ = (c == '\n') ? ' ' : c; len++; } --- 482,488 ---- register int len = 2; *p1++ = c; ! while ((c = *p2++) != '\0' && len < sizeof(buf)) { *p1++ = (c == '\n') ? ' ' : c; len++; } *************** *** 691,697 **** char buf[BUFSIZ]; char *lmktemp(); ! (void) sprintf(buf, "%s/.seq", SD); if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) { printf("%s: cannot create %s\n", name, buf); exit(1); --- 692,698 ---- char buf[BUFSIZ]; char *lmktemp(); ! (void) snprintf(buf, sizeof(buf), "%s/.seq", SD); if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) { printf("%s: cannot create %s\n", name, buf); exit(1); *************** *** 715,721 **** inchar = strlen(SD) + 3; n = (n + 1) % 1000; (void) lseek(fd, (off_t)0, 0); ! sprintf(buf, "%03d\n", n); (void) write(fd, buf, strlen(buf)); (void) close(fd); /* unlocks as well */ } --- 716,722 ---- inchar = strlen(SD) + 3; n = (n + 1) % 1000; (void) lseek(fd, (off_t)0, 0); ! snprintf(buf, sizeof(buf), "%03d\n", n); (void) write(fd, buf, strlen(buf)); (void) close(fd); /* unlocks as well */ } *************** *** 732,738 **** if ((s = malloc(len)) == NULL) fatal2("out of memory"); ! (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host); return(s); } --- 733,739 ---- if ((s = malloc(len)) == NULL) fatal2("out of memory"); ! (void) snprintf(s, len, "%s/%sA%03d%s", SD, id, num, host); return(s); } Index: lpr/lptest/lptest.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/lptest/lptest.c,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.1 diff -c -r1.1.1.1 -r1.1.1.1.6.1 *** lptest.c 1994/05/26 05:21:56 1.1.1.1 --- lptest.c 1996/11/01 04:57:50 1.1.1.1.6.1 *************** *** 48,54 **** /* * lptest -- line printer test program (and other devices). */ ! void main(argc, argv) int argc; char **argv; --- 48,54 ---- /* * lptest -- line printer test program (and other devices). */ ! int main(argc, argv) int argc; char **argv; Index: lpr/pac/pac.c =================================================================== RCS file: /usr/cvs/src/usr.sbin/lpr/pac/pac.c,v retrieving revision 1.2.4.1 retrieving revision 1.2.4.2 diff -c -r1.2.4.1 -r1.2.4.2 *** pac.c 1995/08/26 11:50:53 1.2.4.1 --- pac.c 1996/11/01 04:57:52 1.2.4.2 *************** *** 98,104 **** static int qucmp __P((const void *, const void *)); static void rewrite __P((void)); ! void main(argc, argv) int argc; char **argv; --- 98,104 ---- static int qucmp __P((const void *, const void *)); static void rewrite __P((void)); ! int main(argc, argv) int argc; char **argv;