From owner-freebsd-security Mon Aug 4 09:43:20 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.5/8.8.5) id JAA09605 for security-outgoing; Mon, 4 Aug 1997 09:43:20 -0700 (PDT) Received: from enteract.com (enteract.com [206.54.252.1]) by hub.freebsd.org (8.8.5/8.8.5) with ESMTP id JAA09600 for ; Mon, 4 Aug 1997 09:43:17 -0700 (PDT) Received: (from tqbf@localhost) by enteract.com (8.8.5/8.7.6) id LAA24267; Mon, 4 Aug 1997 11:43:09 -0500 (CDT) From: "Thomas H. Ptacek" Message-Id: <199708041643.LAA24267@enteract.com> Subject: Re: Vulnerability in 4.4BSD rfork() implementation To: marcs@znep.com (Marc Slemko) Date: Mon, 4 Aug 1997 11:43:08 -0500 (CDT) Cc: freebsd-security@FreeBSD.ORG Reply-To: tqbf@enteract.com In-Reply-To: from "Marc Slemko" at Aug 2, 97 09:53:52 pm X-Mailer: ELM [version 2.4 PL24 ME8a] Content-Type: text Sender: owner-freebsd-security@FreeBSD.ORG X-Loop: FreeBSD.org Precedence: bulk > root access. With the skeleton code posted in this advisory, it is even > easier. Sorry about that. > Secondly, FreeBSD 2.2 (probably any version of 2.2-current starting > around 1996/02/23) and 3.0 are both vulnerable. 2.1 and earlier are not. Sorry about that. > Third, I would recommend the use of the loadable module included in the > advisory to close the hole temporarily until there is a FreeBSD advisory You've mentioned the only real problem with this: if the module is unloaded or the system reboots and it isn't replaced, you've lost root. If you want to finalize this change on your system, replace the function pointer "rfork" in kern/init_sysent.c with "nosys" and recompile. Also, many of my systems don't run with LKM support; however, my module doesn't actually "load" anything, it just takes advantage of the bootstrap function to get at kernel memory. If you want to chop rfork() out of your system without using an LKM, I've included a short C program here to do that through /dev/kmem; you can use it instead if you feel like living dangerously. To compile, do "cc -g -o disable_rfork disable_rfork.c -lkvm". -- cut here -- #include #include #include #include #include #include #include #include #include #include #include #include #define PATH_KVM "/dev/kmem" #define RFORK_SYSCALL 251 #define SYSCALL_TABLE_NAME "_sysent" #define BAD_SYSTEM_CALL_HANDLER "_nosys" #define SYSENT_VECTOR "_aout_sysvec" int main() { int sycall; int fd, i; kvm_t *kt; struct nlist name[4]; struct sysent *sp; struct sysentvec *sv; sycall = RFORK_SYSCALL; fd = open(PATH_KVM, O_RDWR); if(fd < 0) { perror("kmem open failure"); exit(errno); } kt = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm open"); if(!kt) exit(errno); name[0].n_un.n_name = SYSCALL_TABLE_NAME; name[1].n_un.n_name = BAD_SYSTEM_CALL_HANDLER; name[2].n_un.n_name = SYSENT_VECTOR; name[3].n_un.n_name = NULL; if((i = kvm_nlist(kt, name)) < 0) { fprintf(stderr, "unable to locate data (%d): %s\n", i, strerror(errno)); exit(errno); } if(lseek(fd, name[2].n_value, SEEK_SET) < 0) { perror("lseek"); exit(errno); } sv = malloc(sizeof(*sv)); if(!sv) { fprintf(stderr, "malloc failure\n"); exit(-1); } if(read(fd, (u_char *) sv, sizeof(struct sysentvec)) < sizeof(struct sysentvec)) { perror("syscall vector read"); exit(errno); } if(sycall > (sv->sv_size - 1) || sv < 0) { fprintf(stderr, "System call out of bounds! (%d)\n", sv->sv_size); exit(-1); } if(lseek(fd, name[0].n_value + (sycall * sizeof(struct sysent)), SEEK_SET) < 0) { perror("seek"); exit(errno); } sp = malloc(sizeof(struct sysent)); if(!sp) { fprintf(stderr, "malloc failure\n"); exit(errno); } if(read(fd, (u_char *) sp, sizeof(*sp)) < sizeof(*sp)) { perror("read sysent"); exit(errno); } if(lseek(fd, name[0].n_value + (sycall * sizeof(struct sysent)), SEEK_SET) < 0) { perror("seek"); exit(errno); } sp->sy_call = (int (*)()) name[1].n_value; if((i = write(fd, (u_char *) sp, sizeof(*sp))) < sizeof(*sp)) { perror("write"); if(i) { fprintf(stderr, "Some data written; now" " would be a good time to" " shut down gracefully\n"); exit(-1); } } exit(0); }