#include "proto-ntlmssp.h" #include "masscan-app.h" #include "proto-banout.h" #include "util-safefunc.h" #include "util-malloc.h" #include #include /* +--------+--------+--------+--------+ | 'N' | 'T' | 'L' | 'M' | +- -+- -+- -+- -+ | 'S' | 'S' | 'P' | '\0' | +--------+--------+--------+--------+ | MessageType | +--------+--------+--------+--------+ | TargetNameLen | TargetNameMaxLen| TagetName fields set to zero if +--------+--------+--------+--------+ NTLMSSP_REQUEST_TARGET flag not set | TargetNameOffset | +--------+--------+--------+--------+ | NegotiateFlags | +--------+--------+--------+--------+ | | +- ServerChallenge -+ | | +--------+--------+--------+--------+ | | +- Reserved -+ | | +--------+--------+--------+--------+ | TargetInfoLen | TargetInfoMaxLen| TagetInfo fields set to zero if +--------+--------+--------+--------+ NTLMSSP_NEGOTIATE_TARGET_INFO flag not set | TargetInfoOffset | +--------+--------+--------+--------+ |MajorVer|MinorVer| ProductBuild | +--------+--------+--------+--------+ | Reserved |NTLMver | +--------+--------+--------+--------+ | | +- -+- -+- -+- -+ . . . . . . . . . . . . . . . . . . . +- -+- -+- -+- -+ | | | | | +--------+--------+--------+--------+ Signature (8 bytes): "An 8-byte character array that MUST contain the ASCII string ('N', 'T', 'L', 'M', 'S', 'S', 'P', '\0')." MessageType (4 bytes): "A 32-bit unsigned integer that indicates the message type. This field MUST be set to 0x00000002." TargetNameLen (2 bytes): "A 16-bit unsigned integer that defines the size, in bytes, of TargetName in Payload." Zero if NTLMSSP_REQUEST_TARGET not set. TargetNameMaxLen (2 bytes): "A 16-bit unsigned integer that SHOULD be set to the value of TargetNameLen and MUST be ignored on receipt." Zero if NTLMSSP_REQUEST_TARGET not set. TargetNameBufferOffset (4 bytes): "A 32-bit unsigned integer that defines the offset, in bytes, from the beginning of the CHALLENGE_MESSAGE to TargetName in Payload. If TargetName is a Unicode string, the values of TargetNameBufferOffset and TargetNameLen MUST be multiples of 2." VERSION FIELDS: These fields are valid only if "NTLMSSP_NEGOTIATE_VERSION" flag is set. MajorVer [ProductMajorVersion] (1 byte): "An 8-bit unsigned integer that SHOULD contain the major version number of the operating system in use." MinorVer [ProductMinorVersion] (1 byte): "An 8-bit unsigned integer that SHOULD<34> contain the minor version number of the operating system in use." ProductBuild (2 bytes): "A 16-bit unsigned integer that contains the build number of the operating system in use. This field SHOULD be set to a 16-bit quantity that identifies the operating system build number." NTLMRevisionCurrent (1 byte): "An 8-bit unsigned integer that contains a value indicating the current revision of the NTLMSSP in use. This field SHOULD contain the following value:" "NTLMSSP_REVISION_W2K3 (0x0F): Version 15 of the NTLMSSP is in use." */ static void append_unicode_string(struct BannerOutput *banout, unsigned proto, const char *name, const unsigned char *value, size_t value_length) { unsigned j; banout_append_char(banout, proto, ' '); banout_append(banout, PROTO_SMB, name, AUTO_LEN); banout_append_char(banout, proto, '='); for (j=0; j x->length - x->offset) length = x->length - x->offset; /* See if we have a fragment, in which case we need to allocate a buffer * to contain it */ if (x->offset == 0 && x->length > length) { x->buf = MALLOC(x->length); memcpy(x->buf, px, length); x->offset = (unsigned)length; return; } else if (x->offset) { memcpy(x->buf + x->offset, px, length); x->offset += (unsigned)length; if (x->offset < x->length) return; /* now reset the input to point to our buffer instead */ px = x->buf; length = x->length; } if (length < 56) goto end; /* Verify the signature. There are other protocols that we could possibly * detect at this point and do something else useful with, but for right now, * we are just doing NTLM */ if (memcmp("NTLMSSP", px, 8) != 0) goto end; /* Verify this is a "challenge" packet, which has all the interesting * fields. */ message_type = px[8] | px[9]<<8 | px[10]<<16 | px[11]<<24; if (message_type != 2) goto end; /* Grab the Domain field. This is a pointer in these 8 bytes here * that points into the payload section of the chunk */ name_length = px[12] | px[13]<<8; name_offset = px[16] | px[17]<<8 | px[18]<<16 | px[19]<<24; if (name_length && name_length + name_offset < length) { append_unicode_string(banout, PROTO_SMB, "domain", px+name_offset, name_length); } /* Grab flags */ //flags = px[20] | px[21]<<8 | px[22]<<16 | px[23]<<24; /* Info field */ info_length = px[40] | px[41]<<8; info_offset = px[44] | px[45]<<8 | px[46]<<16 | px[47]<<24; /* Version field */ { char buf[64]; snprintf(buf, sizeof(buf), " version=%u.%u.%u ntlm-ver=%u", px[48], px[49], px[50] | px[51]<<8, px[55] ); banout_append(banout, PROTO_SMB, buf, AUTO_LEN); } /* Parse all the fields */ for (i=info_offset; i+4 info_offset + info_length - i) len = info_offset + info_length - i; if (len > length - i) len = length - i; switch (type) { case 0x00: /* MsvAvEOL */ i = info_offset + info_length; continue; case 1: /* MsvAvNbComputerName */ append_unicode_string(banout, PROTO_SMB, "name", px+i, len); break; case 2: /* MsvAvNbDomainName */ append_unicode_string(banout, PROTO_SMB, "domain", px+i, len); break; case 3: /* MsvAvDnsComputerName */ append_unicode_string(banout, PROTO_SMB, "name-dns", px+i, len); break; case 4: /* MsvAvDnsDomainName */ append_unicode_string(banout, PROTO_SMB, "domain-dns", px+i, len); break; case 5: /* MsvAvDnsTreeName */ append_unicode_string(banout, PROTO_SMB, "forest", px+i, len); break; case 6: /* MsvAvFlags */ break; case 7: /* MsvAvTimestamp */ break; case 8: /* MsvAvSingleHost */ break; case 9: /* MsvAvTargetName */ append_unicode_string(banout, PROTO_SMB, "target", px+i, len); break; case 10: /* MsvChannelBindings */ break; default: break; } i += (unsigned)len; } /* Grab the other fields. This*/ end: /* * Free the buffer if needed */ if (x->buf) { free(x->buf); x->buf = 0; } } void ntlmssp_cleanup(struct NtlmsspDecode *x) { if (x->buf) { free(x->buf); x->buf = 0; } } void ntlmssp_decode_init(struct NtlmsspDecode *x, size_t length) { memset(x, 0, sizeof(*x)); /* [security] Double-check this input, since it's ultimately driven by user-input. * The code that leads to here should already have double-checked this, but I'm * doing it again just in case. This is larger than any input that should be * seen in the real world that a hacker isn't messing with. */ if (length > 65536) length = 65536; x->length = (unsigned)length; x->offset = 0; x->buf = NULL; }