Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 4 Aug 1997 11:43:08 -0500 (CDT)
From:      "Thomas H. Ptacek"  <tqbf@enteract.com>
To:        marcs@znep.com (Marc Slemko)
Cc:        freebsd-security@FreeBSD.ORG
Subject:   Re: Vulnerability in 4.4BSD rfork() implementation
Message-ID:  <199708041643.LAA24267@enteract.com>
In-Reply-To: <Pine.BSF.3.95.970802214104.12645Y-100000@alive.znep.com> from "Marc Slemko" at Aug 2, 97 09:53:52 pm

next in thread | previous in thread | raw e-mail | index | archive | help
> 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <a.out.h>
#include <nlist.h>
#include <fcntl.h>
#include <kvm.h>
#include <sys/sysent.h>

#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);
}




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199708041643.LAA24267>