From b72e8ab66fb3a3689697d62947b6c81c1083dc08 Mon Sep 17 00:00:00 2001 From: Robert David Graham <robert_david_graham@yahoo.com> Date: Sun, 10 Jun 2018 22:31:03 -0400 Subject: [PATCH] SMBv2 --- src/proto-banner1.h | 73 +- src/proto-banout.c | 63 +- src/proto-banout.h | 10 + src/proto-ftp.c | 6 +- src/proto-imap4.c | 7 +- src/proto-interactive.c | 9 +- src/proto-interactive.h | 11 +- src/proto-ntlmssp.c | 268 +++ src/proto-ntlmssp.h | 25 + src/proto-pop3.c | 8 +- src/proto-sctp.c | 2 +- src/proto-smb.c | 1883 ++++++++++++++++++---- src/proto-smtp.c | 9 +- src/proto-snmp.c | 4 +- src/proto-spnego.h | 28 + src/proto-ssl.c | 3 +- src/proto-tcp.c | 33 +- src/proto-vnc.c | 19 +- src/proto-x509.c | 325 +++- src/proto-x509.h | 3 + xcode4/masscan.xcodeproj/project.pbxproj | 8 + 21 files changed, 2426 insertions(+), 371 deletions(-) create mode 100644 src/proto-ntlmssp.c create mode 100644 src/proto-ntlmssp.h create mode 100644 src/proto-spnego.h diff --git a/src/proto-banner1.h b/src/proto-banner1.h index a8b3dbe..73c9710 100644 --- a/src/proto-banner1.h +++ b/src/proto-banner1.h @@ -6,6 +6,7 @@ #include "masscan-app.h" #include "proto-banout.h" #include "proto-x509.h" +#include "proto-spnego.h" struct InteractiveData; struct Banner1; @@ -139,39 +140,70 @@ struct Smb72_Negotiate { uint16_t DialectIndex; uint16_t SecurityMode; uint64_t SystemTime; + uint32_t SessionKey; uint32_t Capabilities; uint16_t ServerTimeZone; uint8_t ChallengeLength; uint8_t ChallengeOffset; }; +struct Smb73_Setup { + uint16_t BlobLength; + uint16_t BlobOffset; +}; + struct SMBSTUFF { + unsigned nbt_state; unsigned char nbt_type; unsigned char nbt_flags; - unsigned length; + unsigned is_printed_ver:1; + unsigned is_printed_guid:1; + unsigned is_printed_time:1; + unsigned nbt_length; unsigned nbt_err; - struct { - unsigned char command; - unsigned status; - unsigned char flags1; - unsigned short flags2; - unsigned pid; - unsigned char signature[8]; - unsigned short tid; - unsigned short uid; - unsigned short mid; - unsigned short param_length; - unsigned short param_offset; - unsigned short byte_count; - unsigned short byte_offset; - unsigned short byte_state; - unsigned short unicode_char; - } smb1; + union { + struct { + unsigned char command; + unsigned status; + unsigned char flags1; + unsigned short flags2; + unsigned pid; + unsigned char signature[8]; + unsigned short tid; + unsigned short uid; + unsigned short mid; + unsigned short param_length; + unsigned short param_offset; + unsigned short byte_count; + unsigned short byte_offset; + unsigned short byte_state; + unsigned short unicode_char; + } smb1; + struct { + unsigned seqno; + unsigned short header_length; + unsigned short offset; + unsigned short state; + unsigned short opcode; + unsigned short struct_length; + unsigned is_dynamic:1; + unsigned char flags; + unsigned ntstatus; + unsigned number; + unsigned short blob_offset; + unsigned short blob_length; + } smb2; + } hdr; union { struct Smb72_Negotiate negotiate; - } parms1; - + struct Smb73_Setup setup; + struct { + uint64_t current_time; + uint64_t boot_time; + } negotiate2; + } parms; + struct SpnegoDecode spnego; }; struct ProtocolState { @@ -217,6 +249,7 @@ struct ProtocolParserStream { const unsigned char *px, size_t length, struct BannerOutput *banout, struct InteractiveData *more); + void (*cleanup)(struct ProtocolState *stream_state); }; diff --git a/src/proto-banout.c b/src/proto-banout.c index e24553e..60555c7 100644 --- a/src/proto-banout.c +++ b/src/proto-banout.c @@ -177,6 +177,68 @@ banout_append_char(struct BannerOutput *banout, unsigned proto, int c) banout_append(banout, proto, &cc, 1); } +/*************************************************************************** + ***************************************************************************/ +void +banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits) +{ + if (digits == 0) { + for (digits=16; digits>0; digits--) + if (number>>((digits-1)*4) & 0xF) + break; + } + + for (;digits>0; digits--) { + char c = "0123456789abcdef"[(number>>(unsigned long long)((digits-1)*4)) & 0xF]; + banout_append_char(banout, proto, c); + } +} + +/*************************************************************************** + * Output either a normal character, or the hex form of a UTF-8 string + ***************************************************************************/ +void +banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c) +{ + if (c & ~0xFFFF) { + unsigned c2; + c2 = 0xF0 | ((c>>18)&0x03); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + c2 = 0x80 | ((c>>12)&0x3F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + c2 = 0x80 | ((c>> 6)&0x3F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + c2 = 0x80 | ((c>> 0)&0x3F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + } else if (c & ~0x7FF) { + unsigned c2; + c2 = 0xE0 | ((c>>12)&0x0F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + c2 = 0x80 | ((c>> 6)&0x3F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + c2 = 0x80 | ((c>> 0)&0x3F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + } else if (c & ~0x7f) { + unsigned c2; + c2 = 0xc0 | ((c>> 6)&0x1F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + c2 = 0x80 | ((c>> 0)&0x3F); + banout_append(banout, proto, "\\x", 2); + banout_append_hexint(banout, proto, c2, 2); + } else + banout_append_char(banout, proto, c); +} + + + /*************************************************************************** ***************************************************************************/ static struct BannerOutput * @@ -270,7 +332,6 @@ banout_append(struct BannerOutput *banout, unsigned proto, memcpy(p->banner + p->length, px, length); p->length = (unsigned)(p->length + length); - } /***************************************************************************** diff --git a/src/proto-banout.h b/src/proto-banout.h index 55b639c..ef76c60 100644 --- a/src/proto-banout.h +++ b/src/proto-banout.h @@ -65,6 +65,16 @@ banout_append(struct BannerOutput *banout, unsigned proto, const void *px, size_ void banout_append_char(struct BannerOutput *banout, unsigned proto, int c); +/** + * Append an integer, with hex digits, with the specified number of + * digits + */ +void +banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits); + +void +banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c); + /** * Select a specific string (of the specified protocol). */ diff --git a/src/proto-ftp.c b/src/proto-ftp.c index 137ea66..65e5b08 100644 --- a/src/proto-ftp.c +++ b/src/proto-ftp.c @@ -68,8 +68,7 @@ ftp_parse( const struct Banner1 *banner1, continue; else if (px[i] == '\n') { if (ftp->is_last) { - more->payload = "AUTH TLS\r\n"; - more->length = 10; + tcp_transmit(more, "AUTH TLS\r\n", 10, 0); state = 100; banout_append_char(banout, PROTO_FTP, px[i]); } else { @@ -98,8 +97,7 @@ ftp_parse( const struct Banner1 *banner1, pstate->port = (unsigned short)port; state = 0; - more->payload = banner_ssl.hello; - more->length = (unsigned)banner_ssl.hello_length; + tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0); } else { state = STATE_DONE; diff --git a/src/proto-imap4.c b/src/proto-imap4.c index 3d1fb9b..9bce94e 100644 --- a/src/proto-imap4.c +++ b/src/proto-imap4.c @@ -81,7 +81,7 @@ imap4_parse( const struct Banner1 *banner1, case 5: banout_append_char(banout, PROTO_IMAP4, px[i]); if (px[i] == '\n') { - tcp_transmit(more, "a001 CAPABILITY\r\n", 17); + tcp_transmit(more, "a001 CAPABILITY\r\n", 17, 0); state = 100; } break; @@ -136,7 +136,7 @@ imap4_parse( const struct Banner1 *banner1, case 105: banout_append_char(banout, PROTO_IMAP4, px[i]); if (px[i] == '\n') { - tcp_transmit(more, "a002 STARTTLS\r\n", 15); + tcp_transmit(more, "a002 STARTTLS\r\n", 15, 0); state = 300; } break; @@ -158,8 +158,7 @@ imap4_parse( const struct Banner1 *banner1, pstate->port = (unsigned short)port; state = 0; - more->payload = banner_ssl.hello; - more->length = (unsigned)banner_ssl.hello_length; + tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0); break; } break; diff --git a/src/proto-interactive.c b/src/proto-interactive.c index 2c57fa4..f075103 100644 --- a/src/proto-interactive.c +++ b/src/proto-interactive.c @@ -1,8 +1,11 @@ #include "proto-interactive.h" void -tcp_transmit(struct InteractiveData *more, const void *payload, size_t length) +tcp_transmit(struct InteractiveData *more, const void *payload, size_t length, unsigned flags) { - more->payload = payload; - more->length = (unsigned)length; + more->m_payload = payload; + more->m_length = (unsigned)length; + + if (flags & TCPTRAN_DYNAMIC) + more->is_payload_dynamic = 1; } diff --git a/src/proto-interactive.h b/src/proto-interactive.h index 04852ab..4bb8a5f 100644 --- a/src/proto-interactive.h +++ b/src/proto-interactive.h @@ -3,11 +3,14 @@ #include <stdio.h> struct InteractiveData { - const void *payload; - unsigned length; + const void *m_payload; + unsigned m_length; + unsigned is_payload_dynamic:1; +}; +enum { + TCPTRAN_DYNAMIC = 0x0001, }; - void -tcp_transmit(struct InteractiveData *more, const void *data, size_t length); +tcp_transmit(struct InteractiveData *more, const void *data, size_t length, unsigned flags); #endif diff --git a/src/proto-ntlmssp.c b/src/proto-ntlmssp.c new file mode 100644 index 0000000..250b16d --- /dev/null +++ b/src/proto-ntlmssp.c @@ -0,0 +1,268 @@ +#include "proto-ntlmssp.h" +#include "masscan-app.h" +#include "proto-banout.h" +#include "string_s.h" +#include <string.h> +#include <stdlib.h> + +/* + +--------+--------+--------+--------+ + | '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<value_length; j += 2) { + unsigned c = value[j] | value[j+1]<<8; + banout_append_unicode(banout, PROTO_SMB, c); + } +} + +void +ntlmssp_decode(struct NtlmsspDecode *x, + const unsigned char *px, size_t length, + struct BannerOutput *banout) +{ + unsigned message_type; + unsigned name_offset; + unsigned name_length; + unsigned info_offset; + unsigned info_length; + unsigned flags; + unsigned i; + + if (length > 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 += 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]; + sprintf_s(buf, sizeof(buf), " version=%u.%u.%u ntlm-ver=%u", + px[48], + px[49], + px[50] | px[51]<<8, + px[55] + ); + } + + /* Parse all the fields */ + for (i=info_offset; i+4<info_offset+info_length && i+4<length; ) { + unsigned type = px[i] | px[i+1]<<8; + unsigned len = px[i+2] | px[i+3]<<8; + i += 4; + + if (len > 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 += 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; + +} + diff --git a/src/proto-ntlmssp.h b/src/proto-ntlmssp.h new file mode 100644 index 0000000..d0f155a --- /dev/null +++ b/src/proto-ntlmssp.h @@ -0,0 +1,25 @@ +#ifndef PROTO_NTLMSSP_H +#define PROTO_NTLMSSP_H +#include <stdio.h> +struct BannerOutput; + +struct NtlmsspDecode +{ + unsigned length; + unsigned offset; + unsigned char *buf; +}; + +void +ntlmssp_decode_init(struct NtlmsspDecode *x, size_t length); + +void +ntlmssp_cleanup(struct NtlmsspDecode *x); + +void +ntlmssp_decode(struct NtlmsspDecode *x, + const unsigned char *px, size_t length, + struct BannerOutput *banout); + +#endif + diff --git a/src/proto-pop3.c b/src/proto-pop3.c index af62140..e6435b9 100644 --- a/src/proto-pop3.c +++ b/src/proto-pop3.c @@ -48,7 +48,7 @@ pop3_parse( const struct Banner1 *banner1, case 3: banout_append_char(banout, PROTO_POP3, px[i]); if (px[i] == '\n') { - tcp_transmit(more, "CAPA\r\n", 6); + tcp_transmit(more, "CAPA\r\n", 6, 0); state++; } break; @@ -102,7 +102,7 @@ pop3_parse( const struct Banner1 *banner1, continue; banout_append_char(banout, PROTO_POP3, px[i]); if (px[i] == '\n') { - tcp_transmit(more, "STLS\r\n", 6); + tcp_transmit(more, "STLS\r\n", 6, 0); state = 204; } else { state = 8; @@ -122,8 +122,8 @@ pop3_parse( const struct Banner1 *banner1, pstate->port = (unsigned short)port; state = 0; - more->payload = banner_ssl.hello; - more->length = (unsigned)banner_ssl.hello_length; + tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0); + break; } break; diff --git a/src/proto-sctp.c b/src/proto-sctp.c index 750c33f..2c51a81 100644 --- a/src/proto-sctp.c +++ b/src/proto-sctp.c @@ -9,7 +9,7 @@ #define CRC32C_POLY 0x1EDC6F41 #define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) -static unsigned long crc_c[256] = +static unsigned crc_c[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, diff --git a/src/proto-smb.c b/src/proto-smb.c index 28663a1..5b030e5 100644 --- a/src/proto-smb.c +++ b/src/proto-smb.c @@ -77,46 +77,108 @@ struct SmbParams params[] = { //{0x72, 5, 2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxNumberVcs)}, //{0x72, 7, 4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxBufferSize)}, //{0x72, 11, 4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxRawSize)}, - //{0x72, 15, 4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)}, + {0x72, 15, 4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)}, {0x72, 19, 4, IT_uint32, offsetof(struct Smb72_Negotiate, Capabilities)}, {0x72, 23, 8, IT_uint64, offsetof(struct Smb72_Negotiate, SystemTime)}, {0x72, 31, 2, IT_uint16, offsetof(struct Smb72_Negotiate, ServerTimeZone)}, {0x72, 33, 1, IT_uint8, offsetof(struct Smb72_Negotiate, ChallengeLength)}, + {0x73, 6, 2, IT_uint16, offsetof(struct Smb73_Setup, BlobLength)}, + {0xFF, 0, 65536, IT_uint0, 0}, }; #define memberat(t, s, offset) (t*)((char*)(s)+(offset)) + + static char smb1_null_session_setup[] = { - 0x00, 0x00, 0x00, 0xbe, 0xff, 0x53, 0x4d, 0x42, - 0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x03, 0x80, - 0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06, - 0xe8, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xca, - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x75, 0x00, 0x84, - 0x00, 0x04, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x47, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, - 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4e, 0x00, - 0x54, 0x00, 0x20, 0x00, 0x31, 0x00, 0x33, 0x00, - 0x38, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, - 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, - 0x4e, 0x00, 0x54, 0x00, 0x20, 0x00, 0x34, 0x00, - 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x2f, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, - 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x00, - 0x30, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x30, 0x00, - 0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, - 0x5c, 0x00, 0x49, 0x00, 0x50, 0x00, 0x43, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x00 + 0x00, 0x00, 0x00, 0x7e, 0xff, 0x53, 0x4d, 0x42, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x0d, 0xff, 0x00, 0x00, + 0x00, 0x04, 0x41, 0x32, 0x00, 0xef, 0x00, 0x53, + 0x45, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5c, 0xc0, 0x80, 0x00, 0x41, + 0x00, 0x00, 0x47, 0x00, 0x55, 0x00, 0x45, 0x00, + 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x20, 0x00, + 0x4f, 0x00, 0x53, 0x00, 0x20, 0x00, 0x58, 0x00, + 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x33, 0x00, 0x00, 0x00, 0x53, 0x00, + 0x4d, 0x00, 0x42, 0x00, 0x46, 0x00, 0x53, 0x00, + 0x20, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x32, 0x00, + 0x00, 0x00 }; +static char smb1_null_session_setup_ex[] = { + 0x00, 0x00, 0x00, 0xb8, 0xff, 0x53, 0x4d, 0x42, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x0c, 0xff, 0x00, 0x00, + 0x00, 0x04, 0x41, 0x32, 0x00, 0xf1, 0x00, 0xa5, + 0x12, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5c, 0xc0, 0x80, 0x80, 0x7d, 0x00, 0x60, + 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, 0x30, + 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x2a, 0x04, + 0x28, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x02, 0x88, + 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0xb0, 0x1d, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, 0x63, 0x00, + 0x20, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x20, 0x00, + 0x58, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x46, 0x00, + 0x53, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2e, 0x00, + 0x32, 0x00, 0x00, 0x00 +}; + +char smb2_negotiate_request[] = { + 0x00, 0x00, 0x00, 0x6c, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x17, 0x97, 0x90, 0x40, 0xcd, 0xf0, 0x5e, 0x31, + 0x8d, 0xea, 0xef, 0x98, 0xcd, 0xa5, 0x08, 0xda, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x02, 0x00, 0x03, 0x02, 0x03 +}; +char smb2_null_session_setup[] = { + 0x00, 0x00, 0x00, 0xa2, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e, + 0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a, + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, + 0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54, + 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x15, 0x82, 0x88, 0x62, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, + 0xb0, 0x1d, 0x0f, 0x00, 0x00, 0x00 +}; /***************************************************************************** * @@ -135,34 +197,34 @@ smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, s size_t original_offset = offset; size_t c; - if (max > offset + (smb->smb1.param_length - smb->smb1.param_offset)) - max = offset + (smb->smb1.param_length - smb->smb1.param_offset); + if (max > offset + (smb->hdr.smb1.param_length - smb->hdr.smb1.param_offset)) + max = offset + (smb->hdr.smb1.param_length - smb->hdr.smb1.param_offset); //printf("\n max=%04x \n", *(unsigned short*)(px+max)); /* Find the correct header */ - for (c=0; params[c].command != smb->smb1.command && params[c].command != 0xFF; c++) + for (c=0; params[c].command != smb->hdr.smb1.command && params[c].command != 0xFF; c++) ; - for (; offset < max; offset++, smb->smb1.param_offset++) { + for (; offset < max; offset++, smb->hdr.smb1.param_offset++) { again: - //printf("\n%u/%u %u\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c); + //printf("\n%u/%u %u\n", (unsigned)smb->hdr.smb1.param_offset, (unsigned)smb->hdr.smb1.param_length, (unsigned)c); /* If we've gone past our header, just continue consuming bytes */ - if (params[c].command != smb->smb1.command) + if (params[c].command != smb->hdr.smb1.command) continue; /* If we've gone past the end of this field, goto next field */ - if (params[c].external_offset + params[c].external_length <= smb->smb1.param_offset) { + if (params[c].external_offset + params[c].external_length <= smb->hdr.smb1.param_offset) { c++; goto again; } /* Haven't reached the next field yet */ - if (params[c].external_offset > smb->smb1.param_offset) + if (params[c].external_offset > smb->hdr.smb1.param_offset) continue; - //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c, px[offset]); + //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->hdr.smb1.param_offset, (unsigned)smb->hdr.smb1.param_length, (unsigned)c, px[offset]); /* Shift the type, because all fields little-endian */ switch (params[c].internal_type) { @@ -171,29 +233,29 @@ smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, s break; case IT_uint8: { - uint8_t *x = memberat(uint8_t, &smb->parms1, params[c].internal_offset); + uint8_t *x = memberat(uint8_t, &smb->parms, params[c].internal_offset); *x = px[offset]; } break; case IT_uint16: { - uint16_t *x = memberat(uint16_t, &smb->parms1, params[c].internal_offset); + uint16_t *x = memberat(uint16_t, &smb->parms, params[c].internal_offset); //*x <<= 8; - *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8); + *x |= px[offset] << ((smb->hdr.smb1.param_offset - params[c].external_offset)*8); } break; case IT_uint32: { - uint32_t *x = memberat(uint32_t, &smb->parms1, params[c].internal_offset); + uint32_t *x = memberat(uint32_t, &smb->parms, params[c].internal_offset); //*x <<= 8; - *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8); + *x |= px[offset] << ((smb->hdr.smb1.param_offset - params[c].external_offset)*8); } break; case IT_uint64: { - uint64_t *x = memberat(uint64_t, &smb->parms1, params[c].internal_offset); + uint64_t *x = memberat(uint64_t, &smb->parms, params[c].internal_offset); //*x <<= 8; - *x |= (uint64_t)px[offset] << (uint64_t)((smb->smb1.param_offset - params[c].external_offset)*8); + *x |= (uint64_t)px[offset] << (uint64_t)((smb->hdr.smb1.param_offset - params[c].external_offset)*8); } break; } @@ -218,123 +280,898 @@ convert_windows_time(long long int filetime) /***************************************************************************** *****************************************************************************/ -enum { - D_START, - D_NEGOT_CHALLENGE, - D_NEGOT_DOMAINA, - D_NEGOT_NAMEA, - D_NEGOT_DOMAIN1, - D_NEGOT_DOMAIN2, - D_NEGOT_NAME1, - D_NEGOT_NAME2, - D_NEGOT_END, - - D_UNKNOWN, -}; -/***************************************************************************** - *****************************************************************************/ -static void -name_append_char(struct BannerOutput *banout, unsigned c) -{ - if (c & 0xFF80) - banout_append_char(banout, PROTO_SMB, '.'); - else if (isalnum(c)) - banout_append_char(banout, PROTO_SMB, c); - else switch (c) { - case '-': - case '_': - case '$': - case '*': - banout_append_char(banout, PROTO_SMB, c); - break; - default: - banout_append_char(banout, PROTO_SMB, '.'); - break; - - } -} + + /***************************************************************************** *****************************************************************************/ static size_t -smb1_parse_data(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +smb1_parse_negotiate1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; - unsigned state = smb->smb1.byte_state; - + unsigned state = smb->hdr.smb1.byte_state; + enum { + D_NEGOT_CHALLENGE, + D_NEGOT_DOMAINA, + D_NEGOT_NAMEA, + D_NEGOT_DOMAINU1, + D_NEGOT_DOMAINU2, + D_NEGOT_DOMAIN1, + D_NEGOT_DOMAIN2, + D_NEGOT_NAMEU1, + D_NEGOT_NAMEU2, + D_NEGOT_NAME1, + D_NEGOT_NAME2, + D_NEGOT_END, + + D_UNKNOWN, + }; - if (max > offset + (smb->smb1.byte_count - smb->smb1.byte_offset)) - max = offset + (smb->smb1.byte_count - smb->smb1.byte_offset); + if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) + max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); for (;offset<max; offset++) switch (state) { - case D_START: - state = D_UNKNOWN; - break; case D_NEGOT_CHALLENGE: - if (smb->parms1.negotiate.ChallengeLength == 0) { - if (smb->smb1.flags2 & 0x0080) { - state = D_NEGOT_DOMAIN1; + if (smb->parms.negotiate.ChallengeLength == 0) { + if (smb->hdr.smb1.flags2 & 0x8000) { + state = D_NEGOT_DOMAINU1; } else { state = D_NEGOT_DOMAINA; } offset--; } else - smb->parms1.negotiate.ChallengeLength--; + smb->parms.negotiate.ChallengeLength--; + break; + case D_NEGOT_DOMAINU1: + case D_NEGOT_NAMEU1: + smb->hdr.smb1.unicode_char = px[offset]; + state++; + break; + case D_NEGOT_DOMAINU2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) { + state = D_NEGOT_NAMEU1; + } else { + banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state++; + } + break; + case D_NEGOT_NAMEU2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) { + state = D_NEGOT_END; + } else { + banout_append(banout, PROTO_SMB, " name=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state++; + } break; case D_NEGOT_DOMAIN1: case D_NEGOT_NAME1: - smb->smb1.unicode_char = px[offset]; + smb->hdr.smb1.unicode_char = px[offset]; state++; break; case D_NEGOT_DOMAIN2: - case D_NEGOT_DOMAINA: case D_NEGOT_NAME2: - case D_NEGOT_NAMEA: - smb->smb1.unicode_char |= px[offset] << 8; - if (state == D_NEGOT_DOMAINA || state == D_NEGOT_NAMEA) - smb->smb1.unicode_char >>= 8; - if (smb->smb1.unicode_char == 0) { - banout_append_char(banout, PROTO_SMB, ' '); + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) { state++; } else { - name_append_char(banout, smb->smb1.unicode_char); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; + + case D_NEGOT_DOMAINA: + case D_NEGOT_NAMEA: + if (px[offset] == 0) { + state++; + } else { + banout_append_char(banout, PROTO_SMB, px[offset]); + state++; + } + break; default: break; } - smb->smb1.byte_state = state; - smb->smb1.byte_offset += (offset - original_offset); + smb->hdr.smb1.byte_state = state; + smb->hdr.smb1.byte_offset += (offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ -static void -smb_parse_record( - const struct Banner1 *banner1, - void *banner1_private, - struct ProtocolState *pstate, - const unsigned char *px, size_t length, - struct BannerOutput *banout, +static size_t +smb1_parse_setup1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb1.byte_state; + enum { + D_PADDING, + D_OSA1, + D_OSA2, + D_VERSIONA1, + D_VERSIONA2, + D_DOMAINA1, + D_DOMAINA2, + D_ENDA, + + D_OSU1, + D_OSU2, + D_OSU3, + D_OSU4, + D_VERSION1, + D_VERSION2, + D_VERSION3, + D_VERSION4, + D_DOMAIN1, + D_DOMAIN2, + D_DOMAIN3, + D_DOMAIN4, + + D_UNKNOWN, + }; + + if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) + max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); + + for (;offset<max; offset++) { + //printf("\\x%02x", px[offset]); + switch (state) { + case D_PADDING: + if (smb->hdr.smb1.flags2 & 0x8000) { + state = D_OSU1; + } else { + state = D_OSA1; + } + break; + case D_OSA1: + if (px[offset] == 0) + state = D_VERSIONA1; + else { + banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); + banout_append_char(banout, PROTO_SMB, px[offset]); + state = D_OSA2; + } + break; + case D_OSA2: + if (px[offset] == 0) + state = D_VERSIONA1; + else + banout_append_char(banout, PROTO_SMB, px[offset]); + break; + + case D_VERSIONA1: + if (px[offset] == 0) + state = D_DOMAINA1; + else { + banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); + banout_append_char(banout, PROTO_SMB, px[offset]); + state = D_VERSIONA2; + } + break; + case D_VERSIONA2: + if (px[offset] == 0) + state = D_DOMAINA1; + else + banout_append_char(banout, PROTO_SMB, px[offset]); + break; + case D_DOMAINA1: + if (px[offset] == 0) + state = D_UNKNOWN; + else { + banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); + banout_append_char(banout, PROTO_SMB, px[offset]); + state = D_DOMAINA2; + } + break; + case D_DOMAINA2: + if (px[offset] == 0) + state = D_UNKNOWN; + else + banout_append_char(banout, PROTO_SMB, px[offset]); + break; + + case D_OSU1: + case D_OSU3: + case D_VERSION1: + case D_VERSION3: + case D_DOMAIN1: + case D_DOMAIN3: + smb->hdr.smb1.unicode_char = px[offset]; + state++; + break; + + case D_OSU2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_VERSION1; + else { + banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state = D_OSU3; + } + break; + + case D_OSU4: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_VERSION1; + else { + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state--; + } + break; + + + case D_VERSION2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_DOMAIN1; + else { + banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state = D_VERSION3; + } + break; + + case D_VERSION4: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_DOMAIN1; + else { + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state--; + } + break; + + case D_DOMAIN2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_UNKNOWN; + else { + banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state = D_DOMAIN3; + } + break; + + case D_DOMAIN4: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_UNKNOWN; + else { + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state--; + } + break; + default: + break; + } + } + + smb->hdr.smb1.byte_state = state; + smb->hdr.smb1.byte_offset += (offset - original_offset); + return offset - original_offset; +} +/***************************************************************************** + *****************************************************************************/ +static size_t +smb1_parse_setup2(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb1.byte_state; + enum { + D_BLOB, + D_PADDING, + D_PADDING2, + D_OSA1, + D_OSA2, + D_VERSIONA1, + D_VERSIONA2, + D_DOMAINA1, + D_DOMAINA2, + D_ENDA, + + D_OSU1, + D_OSU2, + D_OSU3, + D_OSU4, + D_VERSION1, + D_VERSION2, + D_VERSION3, + D_VERSION4, + D_DOMAIN1, + D_DOMAIN2, + D_DOMAIN3, + D_DOMAIN4, + + D_UNKNOWN, + }; + + if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) + max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); + + for (;offset<max; offset++) { + //printf("\\x%02x", px[offset]); + switch (state) { + case D_BLOB: + if (smb->parms.setup.BlobOffset == 0) { + spnego_decode_init(&smb->spnego, smb->hdr.smb2.blob_length); + } + { + size_t new_max = max; + if (new_max > offset + smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset) + new_max = offset + smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset; + spnego_decode(&smb->spnego, px+offset, new_max-offset, banout); + + smb->parms.setup.BlobOffset += (new_max-offset); + offset = new_max; + if (smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset == 0) { + offset--; + state = D_PADDING; + } + } + break; + case D_PADDING: + /* If the blog length is odd, then there is no padding. Otherwise, + * there is one byte of padding */ + if (smb->parms.setup.BlobLength & 1) + offset--; + state = D_PADDING2; + break; + case D_PADDING2: + if (smb->hdr.smb1.flags2 & 0x8000) { + state = D_OSU1; + } else { + state = D_OSA1; + } + break; + case D_OSA1: + if (px[offset] == 0) + state = D_VERSIONA1; + else { + banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); + banout_append_char(banout, PROTO_SMB, px[offset]); + state = D_OSA2; + } + break; + case D_OSA2: + if (px[offset] == 0) + state = D_VERSIONA1; + else + banout_append_char(banout, PROTO_SMB, px[offset]); + break; + + case D_VERSIONA1: + if (px[offset] == 0) + state = D_DOMAINA1; + else { + banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); + banout_append_char(banout, PROTO_SMB, px[offset]); + state = D_VERSIONA2; + } + break; + case D_VERSIONA2: + if (px[offset] == 0) + state = D_DOMAINA1; + else + banout_append_char(banout, PROTO_SMB, px[offset]); + break; + case D_DOMAINA1: + if (px[offset] == 0) + state = D_UNKNOWN; + else { + banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); + banout_append_char(banout, PROTO_SMB, px[offset]); + state = D_DOMAINA2; + } + break; + case D_DOMAINA2: + if (px[offset] == 0) + state = D_UNKNOWN; + else + banout_append_char(banout, PROTO_SMB, px[offset]); + break; + + case D_OSU1: + case D_OSU3: + case D_VERSION1: + case D_VERSION3: + case D_DOMAIN1: + case D_DOMAIN3: + smb->hdr.smb1.unicode_char = px[offset]; + state++; + break; + + case D_OSU2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_VERSION1; + else { + banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state = D_OSU3; + } + break; + + case D_OSU4: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_VERSION1; + else { + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state--; + } + break; + + + case D_VERSION2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_DOMAIN1; + else { + banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state = D_VERSION3; + } + break; + + case D_VERSION4: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_DOMAIN1; + else { + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state--; + } + break; + + case D_DOMAIN2: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_UNKNOWN; + else { + banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state = D_DOMAIN3; + } + break; + + case D_DOMAIN4: + smb->hdr.smb1.unicode_char |= px[offset]<<8; + if (smb->hdr.smb1.unicode_char == 0) + state = D_UNKNOWN; + else { + banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); + state--; + } + break; + default: + break; + } + } + + smb->hdr.smb1.byte_state = state; + smb->hdr.smb1.byte_offset += (offset - original_offset); + return offset - original_offset; +} + +/***************************************************************************** + *****************************************************************************/ +static size_t +smb1_parse_negotiate2(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb1.byte_state; + + + if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) + max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); + + for (;offset<max; offset++) + switch (state) { + case 0: + state = 1; + break; + default: + break; + } + + smb->hdr.smb1.byte_state = state; + smb->hdr.smb1.byte_offset += (offset - original_offset); + return offset - original_offset; +} + + +/***************************************************************************** + * A default parser for SMBv2 structs. The simplest implementation would be + * to sipmly skip the "struct_length" bytes. However, we have all this + * extra code to serve as a template for creating additional functions. + *****************************************************************************/ +static size_t +smb2_parse_response(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb2.state; + + if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset)) + max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset); + + for (;offset<max; offset++) + switch (state) { + default: + break; + } + + smb->hdr.smb2.state = state; + smb->hdr.smb2.offset += (offset - original_offset); + return offset - original_offset; +} + +/***************************************************************************** + *****************************************************************************/ +static size_t +smb2_parse_negotiate(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb2.state; + + enum { + N_SECMOD1, N_SECMOD2, + N_DIALECT1, N_DIALECT2, + N_CONTEXTS1, N_CONTEXTS2, + N_GUID01, N_GUID02, N_GUID03, N_GUID04, + N_GUID05, N_GUID06, N_GUID07, N_GUID08, + N_GUID09, N_GUID10, N_GUID11, N_GUID12, + N_GUID13, N_GUID14, N_GUID15, N_GUID16, + N_CAP1, N_CAP2, N_CAP3, N_CAP4, + N_TRANSACTSIZE1, N_TRANSACTSIZE2, N_TRANSACTSIZE3, N_TRANSACTSIZE4, + N_READSIZE1, N_READSIZE2, N_READSIZE3, N_READSIZE4, + N_WRITESIZE1, N_WRITESIZE2, N_WRITESIZE3, N_WRITESIZE4, + N_TIME1, N_TIME2, N_TIME3, N_TIME4, + N_TIME5, N_TIME6, N_TIME7, N_TIME8, + N_BOOT1, N_BOOT2, N_BOOT3, N_BOOT4, + N_BOOT5, N_BOOT6, N_BOOT7, N_BOOT8, + N_BLOB_OFFSET1, N_BLOB_OFFSET2, + N_BLOB_LENGTH1, N_BLOB_LENGTH2, + }; + /* + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Buffer Code | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-+-+-+-+ +-+-+-+-+ + | Server | + +-+-+-+-+ GUID +-+-+-+-+ + | | + +-+-+-+-+ +-+-+-+-+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-+-+-+-+ Current Time +-+-+-+-+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-+-+-+-+ Boot Time +-+-+-+-+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Sec Blob Offset | Sec Blob Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Sec Blob ANS.1/DER encoded blob containing supported authentication mechanisms + +-+-+-+-+... + */ + + if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset)) + max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset); + + for (;offset<max; offset++) + switch (state) { + case N_SECMOD1: case N_SECMOD2: + case N_DIALECT1: case N_DIALECT2: + case N_CONTEXTS1: + state++; + break; + case N_CONTEXTS2: + if (!smb->is_printed_guid) + banout_append(banout, PROTO_SMB, " guid=", AUTO_LEN); + state++; + break; + case N_GUID01: + case N_GUID05: + case N_GUID07: + smb->hdr.smb2.number = px[offset]; + state++; + break; + case N_GUID02: case N_GUID03: case N_GUID04: + smb->hdr.smb2.number |= px[offset] << (8*(state-N_GUID01)); + if (state == N_GUID04 && !smb->is_printed_guid) { + banout_append_hexint(banout, PROTO_SMB, smb->hdr.smb2.number, 8); + banout_append_char(banout, PROTO_SMB, '-'); + } + state++; + break; + case N_GUID06: + case N_GUID08: + smb->hdr.smb2.number |= px[offset] << 8; + if (!smb->is_printed_guid) { + banout_append_hexint(banout, PROTO_SMB, smb->hdr.smb2.number, 4); + banout_append_char(banout, PROTO_SMB, '-'); + } + state++; + break; + case N_GUID10: + if (!smb->is_printed_guid) { + banout_append_hexint(banout, PROTO_SMB, px[offset], 2); + banout_append_char(banout, PROTO_SMB, '-'); + } + state++; + break; + case N_GUID09: case N_GUID11: case N_GUID12: + case N_GUID13: case N_GUID14: case N_GUID15: case N_GUID16: + if (!smb->is_printed_guid) + banout_append_hexint(banout, PROTO_SMB, px[offset], 2); + if (state == N_GUID16) + smb->is_printed_guid = 1; + state++; + break; + case N_CAP1: case N_CAP2: case N_CAP3: case N_CAP4: + case N_TRANSACTSIZE1: case N_TRANSACTSIZE2: case N_TRANSACTSIZE3: case N_TRANSACTSIZE4: + case N_READSIZE1: case N_READSIZE2: case N_READSIZE3: case N_READSIZE4: + case N_WRITESIZE1: case N_WRITESIZE2: case N_WRITESIZE3: case N_WRITESIZE4: + state++; + break; + case N_TIME1: case N_TIME2: case N_TIME3: case N_TIME4: + case N_TIME5: case N_TIME6: case N_TIME7: case N_TIME8: + smb->parms.negotiate2.current_time |= ((uint64_t)px[offset]<<(uint64_t)((state-N_TIME1)*8)); + if (state == N_TIME8 && !smb->is_printed_time) { + char str[64] = "(err)"; + time_t timestamp = convert_windows_time(smb->parms.negotiate2.current_time); + struct tm tm = {0}; + size_t len; + + gmtime_s(&tm, ×tamp); + len = strftime(str, sizeof(str), " time=%Y-%m-%d %H:%M:%S ", &tm); + banout_append(banout, PROTO_SMB, str, len); + smb->is_printed_time = 1; + } + state++; + break; + case N_BOOT1: case N_BOOT2: case N_BOOT3: case N_BOOT4: + case N_BOOT5: case N_BOOT6: case N_BOOT7: case N_BOOT8: + smb->parms.negotiate2.boot_time |= (px[offset]<<((state-N_BOOT1)*8)); + state++; + break; + case N_BLOB_OFFSET1: + smb->hdr.smb2.blob_offset = px[offset]; + state++; + break; + case N_BLOB_OFFSET2: + smb->hdr.smb2.blob_offset |= (px[offset]<<8); + state++; + break; + case N_BLOB_LENGTH1: + smb->hdr.smb2.blob_length = px[offset]; + state++; + break; + case N_BLOB_LENGTH2: + smb->hdr.smb2.blob_length |= (px[offset]<<8); + state++; + break; + default: + break; + } + + smb->hdr.smb2.state = state; + smb->hdr.smb2.offset += (offset - original_offset); + return offset - original_offset; +} + +/***************************************************************************** + *****************************************************************************/ +static size_t +smb2_parse_setup(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb2.state; + + /* + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Buffer Code | Flags | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Sec Blob Offset | Sec Blob Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Sec Blob + +-+-+-+-+... + */ + enum { + N_FLAGS1, N_FLAGS2, + N_BLOB_OFFSET1, N_BLOB_OFFSET2, + N_BLOB_LENGTH1, N_BLOB_LENGTH2, + + }; + + if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset)) + max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset); + + for (;offset<max; offset++) + switch (state) { + case N_FLAGS1: case N_FLAGS2: + state++; + break; + case N_BLOB_OFFSET1: + smb->hdr.smb2.blob_offset = px[offset]; + state++; + break; + case N_BLOB_OFFSET2: + smb->hdr.smb2.blob_offset |= (px[offset]<<8); + state++; + break; + case N_BLOB_LENGTH1: + smb->hdr.smb2.blob_length = px[offset]; + state++; + break; + case N_BLOB_LENGTH2: + smb->hdr.smb2.blob_length |= (px[offset]<<8); + state++; + break; + default: + break; + } + + smb->hdr.smb2.state = state; + smb->hdr.smb2.offset += (offset - original_offset); + return offset - original_offset; +} + + + +/***************************************************************************** + *****************************************************************************/ +static size_t +smb2_parse_header(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->hdr.smb2.state; + enum { + SMB2_CRED_CHARGE1, SMB2_CRED_CHARG2, + SMB2_STATUS1, SMB2_STATUS2, SMB2_STATUS3, SMB2_STATUS4, + SMB2_OPCODE1, SMB2_OPCODE2, + SMB2_CRED_GRANT1, SMB2_CRED_GRANT2, + SMB2_FLAGS1, SMB2_FLAGS2, SMB2_FLAGS3, SMB2_FLAGS4, + SMB2_CHAIN_OFFSET1, SMB2_CHAIN_OFFSET2, + SMB2_CHAIN_OFFSET3, SMB2_CHAIN_OFFSET4, + SMB2_MSGID1, SMB2_MSGID2, SMB2_MSGID3, SMB2_MSGID4, + SMB2_MSGID5, SMB2_MSGID6, SMB2_MSGID7, SMB2_MSGID8, + SMB2_PID1, SMB2_PID2, SMB2_PID3, SMB2_PID4, + SMB2_TID1, SMB2_TID2, SMB2_TID3, SMB2_TID4, + SMB2_SESSID1, SMB2_SESSID2, SMB2_SESSID3, SMB2_SESSID4, + SMB2_SIG01, SMB2_SIG02, SMB2_SIG03, SMB2_SIG04, + SMB2_SIG05, SMB2_SIG06, SMB2_SIG07, SMB2_SIG08, + SMB2_SIG09, SMB2_SIG10, SMB2_SIG11, SMB2_SIG12, + SMB2_SIG13, SMB2_SIG14, SMB2_SIG15, SMB2_SIG16, + SMB2_ERROR + }; + /* + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xFE | 'S' | 'M' | 'B' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Header Length | (padding) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NT_Status | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Opcode | (padding) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | :S:C:P:R| | | | Flags + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chain Offset | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Command Sequence- | + +-+-+-+-+-+-+ +-+-+-+-+-+-+-+ + | Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Process ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Tree ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-+-+-+-+ User ID +-+-+-+-+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-+-+-+-+ +-+-+-+-+ + | | + +-+-+-+-+ Signature +-+-+-+-+ + | | + +-+-+-+-+ +-+-+-+-+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + if (max > offset + (smb->hdr.smb2.header_length - smb->hdr.smb2.offset)) + max = offset + (smb->hdr.smb2.header_length - smb->hdr.smb2.offset); + + for (;offset<max; offset++) + switch (state) { + case SMB2_CRED_CHARGE1: case SMB2_CRED_CHARG2: + state++; + break; + case SMB2_STATUS1: case SMB2_STATUS2: + case SMB2_STATUS3: case SMB2_STATUS4: + smb->hdr.smb2.ntstatus |= (px[offset] << ((state - SMB2_STATUS1)*8)); + state++; + break; + case SMB2_OPCODE1: case SMB2_OPCODE2: + smb->hdr.smb2.opcode |= (px[offset] << ((state - SMB2_OPCODE1)*8)); + state++; + break; + case SMB2_CRED_GRANT1: case SMB2_CRED_GRANT2: + state++; + break; + case SMB2_FLAGS1: + smb->hdr.smb2.flags = px[offset]; + if ((smb->hdr.smb2.flags & 1) == 0) { + banout_append(banout, PROTO_SMB, " PARSERROR[flags] ", AUTO_LEN); + state = SMB2_ERROR; + } else + state++; + break; + case SMB2_FLAGS2: case SMB2_FLAGS3: case SMB2_FLAGS4: + case SMB2_CHAIN_OFFSET1: case SMB2_CHAIN_OFFSET2: + case SMB2_CHAIN_OFFSET3: case SMB2_CHAIN_OFFSET4: + state++; + break; + case SMB2_MSGID1: + case SMB2_MSGID2: case SMB2_MSGID3: case SMB2_MSGID4: + case SMB2_MSGID5: case SMB2_MSGID6: case SMB2_MSGID7: case SMB2_MSGID8: + smb->hdr.smb2.seqno |= (px[offset] << ((state - SMB2_MSGID1)*8)); + state++; + break; + case SMB2_PID1: case SMB2_PID2: case SMB2_PID3: case SMB2_PID4: + case SMB2_TID1: case SMB2_TID2: case SMB2_TID3: case SMB2_TID4: + case SMB2_SESSID1: case SMB2_SESSID2: case SMB2_SESSID3: case SMB2_SESSID4: + case SMB2_SIG01: case SMB2_SIG02: case SMB2_SIG03: case SMB2_SIG04: + case SMB2_SIG05: case SMB2_SIG06: case SMB2_SIG07: case SMB2_SIG08: + case SMB2_SIG09: case SMB2_SIG10: case SMB2_SIG11: case SMB2_SIG12: + case SMB2_SIG13: case SMB2_SIG14: case SMB2_SIG15: case SMB2_SIG16: + state++; + break; + + default: + break; + } + + smb->hdr.smb2.state = state; + smb->hdr.smb2.offset += (offset - original_offset); + return offset - original_offset; +} + + +/***************************************************************************** + *****************************************************************************/ +static size_t +smb_parse_smb(struct SMBSTUFF *smb, const unsigned char *px, size_t max, struct BannerOutput *banout, struct InteractiveData *more) { size_t len; /*scratch variables used in a couple places */ - size_t max; - unsigned state = pstate->state; - struct SMBSTUFF *smb = &pstate->sub.smb; - size_t i; + unsigned state = smb->nbt_state; + size_t i = 0; enum { - NBT_TYPE, - NBT_FLAGS, - NBT_LEN1, - NBT_LEN2, - NBT_ERR, - NBT_DRAIN, - SMB_VER, SMB1_VER_S, SMB1_VER_M, SMB1_VER_B, @@ -374,25 +1211,20 @@ smb_parse_record( SMB2_VER_S, SMB2_VER_M, SMB2_VER_B, - NBT_UNKNOWN, - }; - - /* - * On first run thourhg, the offset will be zero - */ - i = 0; - -again: - - /* - * Make sure we don't go past the end of the NetBIOS header portion - */ - max = length; - if (state > NBT_LEN2) { - if (max > i + smb->length) - max = i + smb->length; - } + SMB2_HDR_LEN1, SMB2_HDR_LEN2, + SMB2_PARSE_HEADER, + SMB2_STRUCT_LEN1, SMB2_STRUCT_LEN2, + SMB2_PARSE_STRUCT, + SMB2_UNTIL_BLOB, + SMB2_PARSE_BLOB, + SMB2_PARSE_REMAINDER, + + SMB_ERROR, + }; + if (max > i + smb->nbt_length) + max = i + smb->nbt_length; + /* * `for all bytes in the segment` @@ -400,169 +1232,74 @@ again: */ for (; i<max; i++) switch (state) { - /* - All session packets are of the following general structure: - - 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | TYPE | FLAGS | LENGTH | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - / TRAILER (Packet Type Dependent) / - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - case NBT_TYPE: - smb->nbt_type = px[i]; - state++; - break; - case NBT_FLAGS: - smb->nbt_flags = px[i]; - smb->length = 0; - state++; - break; - case NBT_LEN1: - smb->length <<= 8; - smb->length |= px[i]; - state++; - break; - case NBT_LEN2: - smb->length <<= 8; - smb->length |= px[i]; - if (max > i + smb->length) - max = i + smb->length; - - /* - 00 - SESSION MESSAGE - 81 - SESSION REQUEST - 82 - POSITIVE SESSION RESPONSE - 83 - NEGATIVE SESSION RESPONSE - 84 - RETARGET SESSION RESPONSE - 85 - SESSION KEEP ALIVE - */ - switch (smb->nbt_type) { - case 0x00: - state = SMB_VER; - break; - case 0x81: - banout_append(banout, PROTO_SMB, "ERR session request", AUTO_LEN); - state = NBT_UNKNOWN; - break; - case 0x82: - case 0x85: - state = NBT_DRAIN; - break; - case 0x83: - state = NBT_ERR; - break; - case 0x84: - banout_append(banout, PROTO_SMB, "ERR retarget", AUTO_LEN); - state = NBT_UNKNOWN; - break; - default: - banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN); - break; - } - break; - case NBT_ERR: - smb->nbt_err = px[i]; - /* - 80 - Not listening on called name - 81 - Not listening for calling name - 82 - Called name not present - 83 - Called name present, but insufficient resources - 8F - Unspecified error - */ - switch (smb->nbt_err) { - case 0x80: - banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN); - break; - case 0x81: - banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN); - break; - case 0x82: - banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN); - break; - case 0x83: - banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN); - break; - case 0x8F: - banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN); - break; - default: - banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN); - break; - - } - state = NBT_DRAIN; - break; - - case NBT_DRAIN: - state = NBT_DRAIN; - break; - case SMB_VER: switch (px[i]) { case 0xFF: - banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN); + if (!smb->is_printed_ver) + banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN); + smb->is_printed_ver = 1; state = SMB1_VER_S; break; case 0xFE: - banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN); + if (!smb->is_printed_ver) + banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN); + smb->is_printed_ver = 1; state = SMB2_VER_S; break; default: - banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN); - state = NBT_UNKNOWN; + if (!smb->is_printed_ver) + banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN); + smb->is_printed_ver = 1; + state = SMB_ERROR; } break; case SMB1_VER_S: case SMB2_VER_S: - if (px[i] != 'S') - state = NBT_UNKNOWN; + state = SMB_ERROR; else state++; break; case SMB1_VER_M: case SMB2_VER_M: if (px[i] != 'M') - state = NBT_UNKNOWN; + state = SMB_ERROR; else state++; break; case SMB1_VER_B: case SMB2_VER_B: if (px[i] != 'B') - state = NBT_UNKNOWN; + state = SMB_ERROR; else state++; break; case SMB1_CMD: - memset(&smb->smb1, 0, sizeof(smb->smb1)); - smb->smb1.command = px[i]; + memset(&smb->hdr, 0, sizeof(smb->hdr)); + smb->hdr.smb1.command = px[i]; state++; break; case SMB1_STATUS1: case SMB1_STATUS2: case SMB1_STATUS3: case SMB1_STATUS4: - smb->smb1.status <<= 8; - smb->smb1.status |= px[i]; + smb->hdr.smb1.status <<= 8; + smb->hdr.smb1.status |= px[i]; state++; break; case SMB1_FLAGS1: - smb->smb1.flags1 = px[i]; + smb->hdr.smb1.flags1 = px[i]; state++; break; case SMB1_FLAGS2: + smb->hdr.smb1.flags2 = px[i]; + state++; + break; case SMB1_FLAGS3: - smb->smb1.flags2 <<= 8; - smb->smb1.flags2 |= px[i]; + smb->hdr.smb1.flags2 |= px[i]<<8; state++; break; case SMB1_PID1: case SMB1_PID2: - smb->smb1.pid <<= 8; - smb->smb1.pid |= px[i]; + smb->hdr.smb1.pid <<= 8; + smb->hdr.smb1.pid |= px[i]; state++; break; case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4: @@ -573,27 +1310,28 @@ again: state++; break; case SMB1_TID1: case SMB1_TID2: - smb->smb1.tid <<= 8; - smb->smb1.tid |= px[i]; + smb->hdr.smb1.tid <<= 8; + smb->hdr.smb1.tid |= px[i]; state++; break; case SMB1_PID3: case SMB1_PID4: - smb->smb1.pid <<= 8; - smb->smb1.pid |= px[i]; + smb->hdr.smb1.pid <<= 8; + smb->hdr.smb1.pid |= px[i]; state++; break; case SMB1_UID1: case SMB1_UID2: - smb->smb1.uid <<= 8; - smb->smb1.uid |= px[i]; + smb->hdr.smb1.uid <<= 8; + smb->hdr.smb1.uid |= px[i]; state++; break; case SMB1_MID1: case SMB1_MID2: - smb->smb1.mid <<= 8; - smb->smb1.mid |= px[i]; + smb->hdr.smb1.mid <<= 8; + smb->hdr.smb1.mid |= px[i]; state++; break; case SMB1_WORD_COUNT: - smb->smb1.param_length = px[i]*2; + smb->hdr.smb1.param_length = px[i]*2; + memset(&smb->parms, 0, sizeof(smb->parms)); state++; break; case SMB1_PARAMETERS: @@ -602,7 +1340,7 @@ again: * (meaning, up to word_count*2 bytes) */ len = smb_params_parse(smb, px, i, max); i += len; - if (smb->smb1.param_offset < smb->smb1.param_length) + if (smb->hdr.smb1.param_offset < smb->hdr.smb1.param_length) break; /* We've reached the end of the parameters field, so go onto @@ -620,44 +1358,77 @@ again: * actually contained * TODO: I should make this a function, but I'm lazy */ - switch (smb->smb1.command) { + switch (smb->hdr.smb1.command) { case 0x72: - { - char str[64] = "(err)"; - time_t timestamp = convert_windows_time(smb->parms1.negotiate.SystemTime); - struct tm tm = {0}; + if (!smb->is_printed_time) { + char str[64] = "(err)"; + time_t timestamp = convert_windows_time(smb->parms.negotiate.SystemTime); + struct tm tm = {0}; + + gmtime_s(&tm, ×tamp); + + len = strftime(str, sizeof(str), " time=%Y-%m-%d %H:%M:%S", &tm); + banout_append(banout, PROTO_SMB, str, len); + sprintf_s(str, sizeof(str), " TZ=%+d ", (short)smb->parms.negotiate.ServerTimeZone); + banout_append(banout, PROTO_SMB, str, AUTO_LEN); + + smb->is_printed_time = 1; + } + smb->hdr.smb1.byte_state = 0; - gmtime_s(&tm, ×tamp); - - len = strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S ", &tm); - banout_append(banout, PROTO_SMB, str, len); - sprintf_s(str, sizeof(str), "TZ%+d ", (short)smb->parms1.negotiate.ServerTimeZone); - banout_append(banout, PROTO_SMB, str, AUTO_LEN); - smb->smb1.byte_state = D_NEGOT_CHALLENGE; - - //more->payload = smb1_null_session_setup; - //more->length = sizeof(smb1_null_session_setup); - - } + if (smb->hdr.smb1.flags2 & 0x0800) { + tcp_transmit(more, smb1_null_session_setup_ex, sizeof(smb1_null_session_setup_ex), 0); + } else { + if (smb->parms.negotiate.SessionKey) { + unsigned char *buf; + buf = malloc(sizeof(smb1_null_session_setup)); + memcpy(buf, smb1_null_session_setup, sizeof(smb1_null_session_setup)); + buf[0x2f] = (unsigned char)(smb->parms.negotiate.SessionKey>> 0) & 0xFF; + buf[0x30] = (unsigned char)(smb->parms.negotiate.SessionKey>> 8) & 0xFF; + buf[0x31] = (unsigned char)(smb->parms.negotiate.SessionKey>>16) & 0xFF; + buf[0x32] = (unsigned char)(smb->parms.negotiate.SessionKey>>24) & 0xFF; + tcp_transmit(more, buf, sizeof(smb1_null_session_setup), TCPTRAN_DYNAMIC); + } else { + tcp_transmit(more, smb1_null_session_setup, sizeof(smb1_null_session_setup), 0); + } + } + + break; + case 0x73: /* session setup and x */ break; default: - banout_append(banout, PROTO_SMB, "-- ", AUTO_LEN); - smb->smb1.byte_state = D_UNKNOWN; + banout_append(banout, PROTO_SMB, " PARSERR(unknown-resonse) ", AUTO_LEN); + smb->hdr.smb1.byte_state = 0; } break; case SMB1_BYTE_COUNT1: - smb->smb1.byte_count = px[i]; + smb->hdr.smb1.byte_count = px[i]; state++; break; case SMB1_BYTE_COUNT2: - smb->smb1.byte_count |= px[i]<<8; + smb->hdr.smb1.byte_count |= px[i]<<8; state++; break; case SMB1_DATA: - i += smb1_parse_data(smb, px, i, max, banout); - if (smb->smb1.byte_offset >= smb->smb1.byte_count) { + switch (smb->hdr.smb1.command) { + case 0x72: + if (smb->hdr.smb1.flags2 & 0x0800) + i += smb1_parse_negotiate2(smb, px, i, max, banout); + else + i += smb1_parse_negotiate1(smb, px, i, max, banout); + break; + case 0x73: /* session setup and x */ + if (smb->hdr.smb1.flags2 & 0x0800) + i += smb1_parse_setup2(smb, px, i, max, banout); + else + i += smb1_parse_setup1(smb, px, i, max, banout); + break; + default: + ; + } + if (smb->hdr.smb1.byte_offset >= smb->hdr.smb1.byte_count) { state = SMB1_DATA_AFTER; i--; /* unconsume byte because of auto-increment */ } @@ -672,23 +1443,309 @@ again: } break; + case SMB2_HDR_LEN1: + memset(&smb->hdr, 0, sizeof(smb->hdr)); + smb->hdr.smb2.header_length = px[i]; + state++; + break; + + case SMB2_HDR_LEN2: + smb->hdr.smb2.header_length |= (px[i]<<8); + if (smb->hdr.smb2.header_length < 12) { + banout_append(banout, PROTO_SMB, " PARSERROR[hdrlen] ", AUTO_LEN); + state = SMB_ERROR; + } else { + smb->hdr.smb2.offset = 6; + state++; + } + break; + + case SMB2_PARSE_HEADER: + i += smb2_parse_header(smb, px, i, max, banout); + if (smb->hdr.smb2.offset >= smb->hdr.smb2.header_length) { + state++; + i--; + } + break; + case SMB2_STRUCT_LEN1: + smb->hdr.smb2.struct_length = px[i]; + state++; + break; + + case SMB2_STRUCT_LEN2: + smb->hdr.smb2.struct_length |= (px[i]<<8); + smb->hdr.smb2.is_dynamic = (smb->hdr.smb2.struct_length&1); + smb->hdr.smb2.struct_length &= 0xFFFe; + smb->hdr.smb2.state = 0; + smb->hdr.smb2.offset = 2; + memset(&smb->parms, 0, sizeof(smb->parms)); + if (smb->hdr.smb2.struct_length < 2) { + banout_append(banout, PROTO_SMB, " PARSERROR[structlen] ", AUTO_LEN); + state = SMB_ERROR; + } else + state++; + break; + case SMB2_PARSE_STRUCT: + /* + * Parse the data portion + */ + switch (smb->hdr.smb2.opcode) { + case 0x00: /* Negotiate Response */ + i += smb2_parse_negotiate(smb, px, i, max, banout); + break; + case 0x01: /* Session Setup */ + i += smb2_parse_setup(smb, px, i, max, banout); + break; + default: + i += smb2_parse_response(smb, px, i, max, banout); + break; + } + + /* + * Respond if necessary + */ + if (smb->hdr.smb2.offset >= smb->hdr.smb2.struct_length) { + switch (smb->hdr.smb2.opcode) { + case 0x00: /* negoiate response */ + if (smb->hdr.smb2.seqno == 0) { + tcp_transmit(more, smb2_negotiate_request, sizeof(smb2_negotiate_request), 0); + } else if (smb->hdr.smb2.seqno == 1) { + tcp_transmit(more, smb2_null_session_setup, sizeof(smb2_null_session_setup), 0); + } + break; + default: + ; + } + i--; + + /* + * Process security blob + */ + if (smb->hdr.smb2.blob_length == 0) + state = SMB2_PARSE_REMAINDER; + else if (smb->hdr.smb2.blob_offset < smb->hdr.smb2.header_length + smb->hdr.smb2.struct_length) { + printf("\n***** parse error *****\n"); + state = SMB2_PARSE_REMAINDER; + } else { + smb->hdr.smb2.blob_offset -= smb->hdr.smb2.header_length; + smb->hdr.smb2.blob_offset -= smb->hdr.smb2.struct_length; + state = SMB2_UNTIL_BLOB; + } + + } + break; + case SMB2_UNTIL_BLOB: + if (smb->hdr.smb2.blob_offset == 0) { + spnego_decode_init(&smb->spnego, smb->hdr.smb2.blob_length); + i--; + state = SMB2_PARSE_BLOB; + } else + smb->hdr.smb2.blob_offset--; + break; + case SMB2_PARSE_BLOB: + { + size_t new_max = max; + if (new_max > i + smb->hdr.smb2.blob_length) + new_max = i + smb->hdr.smb2.blob_length; + spnego_decode(&smb->spnego, px+i, new_max-i, banout); + + smb->hdr.smb2.blob_length -= (new_max-i); + i = new_max; + if (smb->hdr.smb2.blob_length == 0) { + i--; + state = SMB2_PARSE_REMAINDER; + } + } + break; + case SMB2_PARSE_REMAINDER: + case SMB_ERROR: default: - i = length; break; } - - /* - * If there are multiple response packets, then - * loop around and process the next one - */ - if (i < length) { - state = 0; - goto again; - } + smb->nbt_length -= i; + smb->nbt_state = state; + return i; +} + + +/***************************************************************************** + + 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TYPE | FLAGS | LENGTH | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + / TRAILER (Packet Type Dependent) / + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + *****************************************************************************/ +static void +smb_parse_record( + const struct Banner1 *banner1, + void *banner1_private, + struct ProtocolState *pstate, + const unsigned char *px, size_t max, + struct BannerOutput *banout, + struct InteractiveData *more) +{ + size_t i; + unsigned state = pstate->state; + struct SMBSTUFF *smb = &pstate->sub.smb; + + enum { + NBT_TYPE, + NBT_FLAGS, + NBT_LEN1, + NBT_LEN2, + NBT_ERR, + NBT_SMB, + NBT_DRAIN, + NBT_UNKNOWN, + }; + + for (i=0; i<max; i++) + switch (state) { + case NBT_TYPE: + if (smb->spnego.ntlmssp.buf) + ntlmssp_cleanup(&smb->spnego.ntlmssp); + smb->nbt_type = px[i]; + state++; + break; + case NBT_FLAGS: + smb->nbt_flags = px[i] & 0xFE; + smb->nbt_length = px[i] & 0x01; + state++; + break; + case NBT_LEN1: + smb->nbt_length <<= 8; + smb->nbt_length |= px[i]; + state++; + break; + case NBT_LEN2: + smb->nbt_length <<= 8; + smb->nbt_length |= px[i]; + state++; + + + + /* + 00 - SESSION MESSAGE + 81 - SESSION REQUEST + 82 - POSITIVE SESSION RESPONSE + 83 - NEGATIVE SESSION RESPONSE + 84 - RETARGET SESSION RESPONSE + 85 - SESSION KEEP ALIVE + */ + switch (smb->nbt_type) { + case 0x00: + state = NBT_SMB; + smb->nbt_state = 0; + break; + case 0x81: + banout_append(banout, PROTO_SMB, " PARSERR(nbt-sess) ", AUTO_LEN); + state = NBT_UNKNOWN; + break; + case 0x82: + case 0x85: + state = NBT_DRAIN; + break; + case 0x83: + state = NBT_ERR; + break; + case 0x84: + banout_append(banout, PROTO_SMB, " PARSERR(nbt-retarget) ", AUTO_LEN); + state = NBT_UNKNOWN; + break; + default: + banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN); + break; + } + break; + case NBT_ERR: + smb->nbt_err = px[i]; + /* + 80 - Not listening on called name + 81 - Not listening for calling name + 82 - Called name not present + 83 - Called name present, but insufficient resources + 8F - Unspecified error + */ + switch (smb->nbt_err) { + case 0x80: + banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN); + break; + case 0x81: + banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN); + break; + case 0x82: + banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN); + break; + case 0x83: + banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN); + break; + case 0x8F: + banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN); + break; + default: + banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN); + break; + + } + state = NBT_DRAIN; + break; + + case NBT_SMB: + i += smb_parse_smb(smb, px+i, max-i, banout, more); + if (smb->nbt_length == 0) { + state = 0; + i--; + } + break; + + case NBT_DRAIN: + case NBT_UNKNOWN: + default: + break; + } + pstate->state = state; } +/***************************************************************************** + *****************************************************************************/ +static int negot_add_dialect(unsigned char *buf, size_t sizeof_buf, const char *dialect) +{ + size_t nbt_length; + size_t dialect_length = strlen(dialect) + 1; + size_t word_count; + size_t byte_count; + + /* Parse NetBIOS header */ + if (sizeof_buf < 4 || sizeof_buf + 4 < dialect_length) + return -1; + if (buf[0] != 0) + return -1; + nbt_length = buf[2]<<8 | buf[3]; + if (nbt_length <= 4 || nbt_length >= sizeof_buf - dialect_length) + return -1; + + /* Parse SMB header */ + if (memcmp(buf+4, "\xFF" "SMB" "\x72", 5) != 0) + return -1; + if (nbt_length < 39) + return -1; + word_count = buf[36]; + if (word_count != 0) + return -1; + byte_count = buf[37] | buf[38]<<8; + + + + return 0; +} + /***************************************************************************** *****************************************************************************/ static void * @@ -725,7 +1782,7 @@ smb0_hello_template[] = { 0x00 }; static const char -smb1_hello_template[] = { +smb1x_hello_template[] = { 0x00, 0x00, 0x00, 0x2f, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0, /* */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -735,6 +1792,194 @@ smb1_hello_template[] = { 0x31, 0x32, 0x00 }; +static const char +xsmb1_hello_template[] = { + 0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, + 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, + 0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, + 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, + 0x00 +}; + +static const char +smb1_hello_template[] = { + 0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, + 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, + 0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, + 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, + 0x00 + +}; + +static const unsigned char +smb2_negot_response[] = { + + 0x00, 0x00, 0x01, 0xc0, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x01, 0x00, + 0xff, 0x02, 0x00, 0x00, 0x39, 0xf6, 0x39, 0xe2, + 0x4b, 0xac, 0x2e, 0x4d, 0xaf, 0x5e, 0xbf, 0x1a, + 0xfa, 0xe3, 0x2f, 0xa1, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x6e, 0xa8, 0x4f, 0x0d, + 0xf6, 0xfe, 0xd3, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x82, 0x01, 0x3c, + 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, + 0xa0, 0x82, 0x01, 0x30, 0x30, 0x82, 0x01, 0x2c, + 0xa0, 0x1a, 0x30, 0x18, 0x06, 0x0a, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x1e, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x82, 0x01, 0x0c, + 0x04, 0x82, 0x01, 0x08, 0x4e, 0x45, 0x47, 0x4f, + 0x45, 0x58, 0x54, 0x53, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x59, 0x3d, 0x22, 0xb1, + 0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b, + 0xbd, 0x31, 0x9c, 0xfd, 0x0d, 0x6f, 0x2c, 0xaa, + 0x13, 0xd3, 0xe9, 0x37, 0xdc, 0x82, 0xe6, 0x8c, + 0xb6, 0xcd, 0xa5, 0xaf, 0xe0, 0x80, 0xae, 0xa1, + 0xd4, 0x88, 0x5e, 0xb9, 0x55, 0x6b, 0xbc, 0x78, + 0xd9, 0xd3, 0x3f, 0xe8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x33, 0x53, 0x0d, + 0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3, + 0x78, 0x6e, 0xc3, 0x08, 0x4e, 0x45, 0x47, 0x4f, + 0x45, 0x58, 0x54, 0x53, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x59, 0x3d, 0x22, 0xb1, + 0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b, + 0xbd, 0x31, 0x9c, 0xfd, 0x5c, 0x33, 0x53, 0x0d, + 0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3, + 0x78, 0x6e, 0xc3, 0x08, 0x40, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x30, 0x56, 0xa0, 0x54, + 0x30, 0x52, 0x30, 0x27, 0x80, 0x25, 0x30, 0x23, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x4b, 0x65, 0x79, 0x30, 0x27, 0x80, 0x25, 0x30, + 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x4b, 0x65, 0x79, + + 0x00, 0x00, 0x01, 0xc0, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x01, 0x00, + 0x02, 0x03, 0x00, 0x00, 0x39, 0xf6, 0x39, 0xe2, + 0x4b, 0xac, 0x2e, 0x4d, 0xaf, 0x5e, 0xbf, 0x1a, + 0xfa, 0xe3, 0x2f, 0xa1, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x58, 0x06, 0xe8, 0x0d, + 0xf6, 0xfe, 0xd3, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x82, 0x01, 0x3c, + 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, + 0xa0, 0x82, 0x01, 0x30, 0x30, 0x82, 0x01, 0x2c, + 0xa0, 0x1a, 0x30, 0x18, 0x06, 0x0a, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x1e, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x82, 0x01, 0x0c, + 0x04, 0x82, 0x01, 0x08, 0x4e, 0x45, 0x47, 0x4f, + 0x45, 0x58, 0x54, 0x53, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x5a, 0x3d, 0x22, 0xb1, + 0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b, + 0xbd, 0x31, 0x9c, 0xfd, 0x28, 0x43, 0x2b, 0x64, + 0xbd, 0x80, 0x88, 0x56, 0x11, 0xb5, 0xad, 0x5b, + 0x41, 0x4c, 0x68, 0x0b, 0xf7, 0xdf, 0xac, 0x96, + 0xfe, 0xb3, 0xed, 0x9d, 0xe2, 0x5a, 0xb5, 0x8c, + 0x1f, 0x0e, 0xeb, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x33, 0x53, 0x0d, + 0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3, + 0x78, 0x6e, 0xc3, 0x08, 0x4e, 0x45, 0x47, 0x4f, + 0x45, 0x58, 0x54, 0x53, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x5a, 0x3d, 0x22, 0xb1, + 0x12, 0xbb, 0x15, 0xcf, 0x39, 0xad, 0x37, 0x0b, + 0xbd, 0x31, 0x9c, 0xfd, 0x5c, 0x33, 0x53, 0x0d, + 0xea, 0xf9, 0x0d, 0x4d, 0xb2, 0xec, 0x4a, 0xe3, + 0x78, 0x6e, 0xc3, 0x08, 0x40, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x30, 0x56, 0xa0, 0x54, + 0x30, 0x52, 0x30, 0x27, 0x80, 0x25, 0x30, 0x23, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x4b, 0x65, 0x79, 0x30, 0x27, 0x80, 0x25, 0x30, + 0x23, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x18, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x4b, 0x65, 0x79, + + 0x00, 0x00, 0x01, 0x0f, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x01, 0x00, 0x16, 0x00, 0x00, 0xc0, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x0c, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x48, 0x00, 0xc7, 0x00, 0xa1, 0x81, 0xc4, 0x30, + 0x81, 0xc1, 0xa0, 0x03, 0x0a, 0x01, 0x01, 0xa1, + 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x81, 0xab, + 0x04, 0x81, 0xa8, 0x4e, 0x54, 0x4c, 0x4d, 0x53, + 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x10, 0x00, 0x38, 0x00, 0x00, 0x00, 0x15, + 0x82, 0x8a, 0x62, 0x38, 0xe7, 0x63, 0xbc, 0xc7, + 0xfa, 0xc9, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0xee, 0x42, 0x00, + 0x00, 0x00, 0x0f, 0x42, 0x00, 0x52, 0x00, 0x4f, + 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53, + 0x00, 0x4b, 0x00, 0x02, 0x00, 0x10, 0x00, 0x42, + 0x00, 0x52, 0x00, 0x4f, 0x00, 0x41, 0x00, 0x44, + 0x00, 0x45, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x01, + 0x00, 0x10, 0x00, 0x42, 0x00, 0x52, 0x00, 0x4f, + 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53, + 0x00, 0x4b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x62, + 0x00, 0x72, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x64, + 0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x03, + 0x00, 0x10, 0x00, 0x62, 0x00, 0x72, 0x00, 0x6f, + 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, + 0x00, 0x6b, 0x00, 0x07, 0x00, 0x08, 0x00, 0x9c, + 0x8b, 0x80, 0x0e, 0xf6, 0xfe, 0xd3, 0x01, 0x00, + 0x00, 0x00, 0x00, + + + +}; + /***************************************************************************** *****************************************************************************/ static int @@ -785,6 +2030,32 @@ smb_selftest(void) }; + /* + * SMBv2 negotiate response + */ + banner1 = banner1_create(); + banout_init(banout1); + memset(&state[0], 0, sizeof(state[0])); + + smb_parse_record(banner1, + 0, + state, + smb2_negot_response, + sizeof(smb2_negot_response), + banout1, + &more); + x = banout_is_contains(banout1, PROTO_SMB, + "SHIPBAR"); + if (!x) { + printf("smb parser failure: google.com\n"); + return 1; + } + banner1_destroy(banner1); + banout_release(banout1); + + /* + * SMBv1 negotiate response + */ banner1 = banner1_create(); banout_init(banout1); memset(&state[0], 0, sizeof(state[0])); @@ -805,6 +2076,8 @@ smb_selftest(void) banner1_destroy(banner1); banout_release(banout1); + + /* * LET'S FUZZ THIS CRAP!!! * @@ -841,6 +2114,16 @@ smb_selftest(void) return 0; } +/***************************************************************************** + *****************************************************************************/ +static void +smb_cleanup(struct ProtocolState *pstate) +{ + struct SMBSTUFF *smb = &pstate->sub.smb; + if (smb->spnego.ntlmssp.buf) + ntlmssp_cleanup(&smb->spnego.ntlmssp); +} + /***************************************************************************** * This is the 'plugin' structure that registers callbacks for this parser in * the main system. @@ -850,11 +2133,13 @@ struct ProtocolParserStream banner_smb0 = { smb_selftest, smb_init, smb_parse_record, + smb_cleanup }; struct ProtocolParserStream banner_smb1 = { "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0, smb_selftest, smb_init, smb_parse_record, + smb_cleanup }; diff --git a/src/proto-smtp.c b/src/proto-smtp.c index d47986c..6a78eef 100644 --- a/src/proto-smtp.c +++ b/src/proto-smtp.c @@ -94,8 +94,7 @@ smtp_parse( const struct Banner1 *banner1, continue; else if (px[i] == '\n') { if (smtp->is_last) { - more->payload = "EHLO masscan\r\n"; - more->length = 14; + tcp_transmit(more, "EHLO masscan\r\n", 14, 0); state = 100; banout_append_char(banout, PROTO_SMTP, px[i]); } else { @@ -114,8 +113,7 @@ smtp_parse( const struct Banner1 *banner1, continue; else if (px[i] == '\n') { if (smtp->is_last) { - more->payload = "STARTTLS\r\n"; - more->length = 10; + tcp_transmit(more, "STARTTLS\r\n", 10, 0); state = 200; banout_append_char(banout, PROTO_SMTP, px[i]); } else { @@ -144,8 +142,7 @@ smtp_parse( const struct Banner1 *banner1, pstate->port = (unsigned short)port; state = 0; - more->payload = banner_ssl.hello; - more->length = (unsigned)banner_ssl.hello_length; + tcp_transmit(more, banner_ssl.hello, banner_ssl.hello_length, 0); } else { state = STATE_DONE; diff --git a/src/proto-snmp.c b/src/proto-snmp.c index a34135f..fd4ae9e 100644 --- a/src/proto-snmp.c +++ b/src/proto-snmp.c @@ -503,14 +503,14 @@ convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src) while (*src) { const char *next_src; - unsigned long id; + unsigned id; unsigned count; unsigned i; while (*src == '.') src++; - id = strtoul(src, (char**)&next_src, 0); + id = (unsigned)strtoul(src, (char**)&next_src, 0); if (src == next_src) break; else diff --git a/src/proto-spnego.h b/src/proto-spnego.h new file mode 100644 index 0000000..0c25665 --- /dev/null +++ b/src/proto-spnego.h @@ -0,0 +1,28 @@ +#ifndef PROTO_SPNEGO_H +#define PROTO_SPNEGO_H + +#include "proto-x509.h" +#include "proto-ntlmssp.h" + +struct SpnegoDecode +{ + /* + * ====== KLUDGE ALERT: there's no generic ASN.1 encoding, it's specific to + * ====== x.509 parsing, so therefore we are just going to overload that + * ====== a bit until we move the code out into it's own ASN.1 module + */ + struct CertDecode x509[1]; + + struct NtlmsspDecode ntlmssp; +}; + +void +spnego_decode_init(struct SpnegoDecode *x, size_t length); + +void +spnego_decode(struct SpnegoDecode *x, + const unsigned char *px, size_t length, + struct BannerOutput *banout); + +#endif + diff --git a/src/proto-ssl.c b/src/proto-ssl.c index 9982ec9..7942f2e 100644 --- a/src/proto-ssl.c +++ b/src/proto-ssl.c @@ -571,8 +571,7 @@ parse_handshake( static const char heartbleed_request[] = "\x15\x03\x02\x00\x02\x01\x80" "\x18\x03\x02\x00\x03\x01" "\x40\x00"; - more->payload = heartbleed_request; - more->length = sizeof(heartbleed_request)-1; + tcp_transmit(more, heartbleed_request, sizeof(heartbleed_request)-1, 0); } DROPDOWN(i,length,state); diff --git a/src/proto-tcp.c b/src/proto-tcp.c index f04f1df..339e228 100644 --- a/src/proto-tcp.c +++ b/src/proto-tcp.c @@ -20,6 +20,7 @@ #include "proto-banner1.h" #include "proto-ssl.h" #include "proto-http.h" +#include "proto-smb.h" #include "output.h" #include "string_s.h" #include "main-globals.h" @@ -53,6 +54,11 @@ struct TCP_Control_Block unsigned char ttl; unsigned tcpstate:4; + + /* If the payload we've sent was dynamically allocated with + * malloc() from the heap, in which case we'll have to free() + * it. (Most payloads are static memory) */ + unsigned is_payload_dynamic:1; unsigned short payload_length; @@ -512,8 +518,6 @@ tcpcon_destroy_tcb( UNUSEDPARM(reason); -//printf("." "tcb age = %u-sec, reason=%u \n", time(0) - tcb->when_created, reason); - /* * The TCB doesn't point to it's location in the table. Therefore, we * have to do a lookup to find the head pointer in the table. @@ -551,6 +555,15 @@ tcpcon_destroy_tcb( * banners. */ tcpcon_flush_banners(tcpcon, tcb); + if (tcb->is_payload_dynamic && tcb->payload_length && tcb->payload) + free((void*)tcb->payload); + + /* KLUDGE: this needs to be made more elegant */ + switch (tcb->banner1_state.app_proto) { + case PROTO_SMB: + banner_smb1.cleanup(&tcb->banner1_state); + break; + } /* * Unlink this from the timeout system. @@ -1113,8 +1126,8 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, { unsigned err; struct InteractiveData more; - more.payload = 0; - more.length =0; + + memset(&more, 0, sizeof(more)); if ((unsigned)(tcb->seqno_them - seqno_them) > payload_length) { tcpcon_send_packet(tcpcon, tcb, @@ -1154,10 +1167,11 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, tcb->seqno_them += (unsigned)payload_length; /* acknowledge the bytes sent */ - if (more.length) { + if (more.m_length) { //printf("." "sending more data %u bytes\n", more.length); - tcpcon_send_packet(tcpcon, tcb, 0x18, more.payload, more.length, 0); - tcb->seqno_me += (uint32_t)more.length; + tcpcon_send_packet(tcpcon, tcb, 0x18, more.m_payload, more.m_length, 0); + tcb->seqno_me += (uint32_t)more.m_length; + tcb->is_payload_dynamic = more.is_payload_dynamic; } else { tcpcon_send_packet(tcpcon, tcb, 0x10, @@ -1197,6 +1211,11 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, if (tcb->ackno_them - tcb->seqno_me == 0) { /* Now wait for response */ + if (tcb->is_payload_dynamic) + free((void*)tcb->payload); + tcb->payload = 0; + tcb->payload_length = 0; + tcb->is_payload_dynamic = 0; tcb->tcpstate = STATE_WAITING_FOR_RESPONSE; timeouts_add( tcpcon->timeouts, tcb->timeout, diff --git a/src/proto-vnc.c b/src/proto-vnc.c index b79478e..584d904 100644 --- a/src/proto-vnc.c +++ b/src/proto-vnc.c @@ -142,8 +142,8 @@ vnc_parse( const struct Banner1 *banner1, "RFB 003.008\n", }; unsigned version = pstate->sub.vnc.version % 10; - more->payload = response[version]; - more->length = 12; + + tcp_transmit(more, response[version], 12, 0); if (version < 7) /* Version 3.3: the server selects either "none" or @@ -190,8 +190,7 @@ vnc_parse( const struct Banner1 *banner1, else if (pstate->sub.vnc.sectype == 1) { /* v3.3 sectype=none * We move immediately to ClientInit stage */ - more->payload = "\x01"; - more->length = 1; + tcp_transmit(more, "\x01", 1, 0); state = RFB_SERVERINIT; } else state = STATE_DONE; @@ -201,8 +200,7 @@ vnc_parse( const struct Banner1 *banner1, pstate->sub.vnc.sectype |= px[i]; if (pstate->sub.vnc.sectype == 0) { /* security ok, move to client init */ - more->payload = "\x01"; - more->length = 1; + tcp_transmit(more, "\x01", 1, 0); state = RFB_SERVERINIT; } else { /* error occurred, so grab error message */ @@ -241,16 +239,13 @@ vnc_parse( const struct Banner1 *banner1, banout_append(banout, PROTO_VNC_RFB, "]", AUTO_LEN); if (pstate->sub.vnc.version < 7) { state = RFB_SERVERINIT; - more->payload = "\x01"; - more->length = 1; + tcp_transmit(more, "\x01", 1, 0); } else if (pstate->sub.vnc.version == 7) { state = RFB_SERVERINIT; - more->payload = "\x01\x01"; - more->length = 2; + tcp_transmit(more, "\x01\x01", 2, 0); } else { state = RFB_SECURITYRESULT; - more->payload = "\x01"; - more->length = 1; + tcp_transmit(more, "\x01", 1, 0); } } else { banout_append(banout, PROTO_VNC_RFB, "/", AUTO_LEN); diff --git a/src/proto-x509.c b/src/proto-x509.c index 2c99c71..6d991db 100644 --- a/src/proto-x509.c +++ b/src/proto-x509.c @@ -86,11 +86,13 @@ TBSCertificate ::= SEQUENCE { } */ #include "proto-x509.h" +#include "proto-spnego.h" #include "proto-banout.h" #include "masscan-app.h" #include "smack.h" #include "logger.h" #include <assert.h> +#include <ctype.h> #include <string.h> #include <stdlib.h> #include <stddef.h> @@ -208,7 +210,7 @@ convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src) /* 'for all text characters' */ while (*src) { const char *next_src; - unsigned long id; + unsigned id; unsigned count; unsigned i; @@ -217,7 +219,7 @@ convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src) src++; /* parse integer */ - id = strtoul(src, (char**)&next_src, 0); + id = (unsigned)strtoul(src, (char**)&next_src, 0); if (src == next_src) break; /* invalid integer, programming error */ else @@ -467,6 +469,8 @@ enum X509state { ALGOID0_TAG, ALGOID0_LEN, ALGOID0_LENLEN, ALGOID1_TAG, ALGOID1_LEN, ALGOID1_LENLEN, ALGOID1_CONTENTS0, ALGOID1_CONTENTS1, ENC_TAG, ENC_LEN, ENC_LENLEN, ENC_CONTENTS, + + PADDING=254, ERROR=0xFFFFFFFF, }; @@ -1054,6 +1058,314 @@ x509_decode(struct CertDecode *x, if (x->state != 0xFFFFFFFF) x->state = state; } +void +spnego_decode(struct SpnegoDecode *spnego, + const unsigned char *px, size_t length, + struct BannerOutput *banout) +{ + struct CertDecode *x = spnego->x509; + unsigned i; + unsigned state = x->state; + + enum { + /*NegotiationToken ::= CHOICE { + negTokenInit [0] NegTokenInit, + negTokenResp [1] NegTokenResp + }*/ + NegotiationToken_tag, len, lenlen, + + NegTokenInit_tag, + NegTokenInit_choice, + NegTokenResp_tag, + NegTokenResp_choice, + mechType_tag, + + negState_tag, + supportedMech_tag, + responseToken_tag, + mechListMIC_tag, + + mechTypes_tag, + reqFlags_tag, + mechToken_tag, + + mechToken_content, + responseToken_content, + mechToken_content2, + responseToken_content2, + + UnknownContents, + + + }; + +#define GOTO_ERROR(state, i, length) (state)=0xFFFFFFFF;(i)=(length);continue + + /* 'for all bytes in the current fragment ...' + * 'process that byte, causing a state-transition ' + */ + for (i=0; i<length; i++) { + + + /* + * If we've reached the end of the current field, then we need to + * pop up the stack and resume parsing the parent field. Since we + * reach the end of several levels simultaneously, we may need to + * pop several levels at once + */ + while (x->stack.remainings[0] == 0) { + if (x->stack.depth == 0) + return; + state = ASN1_pop(x); + } + + /* + * Decrement the current 'remaining' length field. + */ + x->stack.remainings[0]--; + + /* + * Jump to the current current state + */ + switch (state) { + case NegotiationToken_tag: + x->brother_state = UnknownContents; + switch (px[i]) { + case 0xa0: + x->child_state = NegTokenInit_tag; + break; + case 0xa1: + x->child_state = NegTokenResp_tag; + break; + case 0x60: + x->child_state = mechType_tag; + break; + default: + x->child_state = UnknownContents; + break; + } + state = len; + break; + + case NegTokenResp_choice: + /* + NegTokenResp ::= SEQUENCE { + negState [0] ENUMERATED { + accept-completed (0), + accept-incomplete (1), + reject (2), + request-mic (3) + } OPTIONAL, + -- REQUIRED in the first reply from the target + supportedMech [1] MechType OPTIONAL, + -- present only in the first reply from the target + responseToken [2] OCTET STRING OPTIONAL, + mechListMIC [3] OCTET STRING OPTIONAL, + ... + }*/ + x->brother_state = NegTokenResp_choice; + switch (px[i]) { + case 0xa0: + x->child_state = negState_tag; + break; + case 0xa1: + x->child_state = supportedMech_tag; + break; + case 0xa2: + x->child_state = responseToken_tag; + break; + case 0xa3: + x->child_state = mechListMIC_tag; + break; + default: + x->child_state = UnknownContents; + break; + } + state = len; + break; + + case NegTokenResp_tag: + if (px[i] != 0x30) { + x->brother_state = UnknownContents; + x->child_state = UnknownContents; + } else { + x->brother_state = UnknownContents; + x->child_state = NegTokenResp_choice; + } + state = len; + break; + + case NegTokenInit_choice: + /* + NegTokenInit ::= SEQUENCE { + mechTypes [0] MechTypeList, + reqFlags [1] ContextFlags OPTIONAL, + -- inherited from RFC 2478 for backward compatibility, + -- RECOMMENDED to be left out + mechToken [2] OCTET STRING OPTIONAL, + mechListMIC [3] OCTET STRING OPTIONAL, + ... + } + }*/ + x->brother_state = NegTokenInit_choice; + switch (px[i]) { + case 0xa0: + x->child_state = mechTypes_tag; + break; + case 0xa1: + x->child_state = reqFlags_tag; + break; + case 0xa2: + x->child_state = mechToken_tag; + break; + case 0xa3: + x->child_state = mechListMIC_tag; + break; + default: + x->child_state = UnknownContents; + break; + } + state = len; + break; + + case NegTokenInit_tag: + if (px[i] != 0x30) { + x->brother_state = UnknownContents; + x->child_state = UnknownContents; + } else { + x->brother_state = UnknownContents; + x->child_state = NegTokenInit_choice; + } + state = len; + break; + + case mechType_tag: + if (px[i] == 0x06) { + x->brother_state = NegotiationToken_tag; + x->child_state = UnknownContents; + } else { + x->brother_state = NegotiationToken_tag; + x->child_state = UnknownContents; + } + state = len; + break; + + case negState_tag: + case supportedMech_tag: + case mechListMIC_tag: + case mechTypes_tag: + case reqFlags_tag: + x->brother_state = UnknownContents; + x->child_state = UnknownContents; + state = len; + break; + + case responseToken_tag: + x->brother_state = UnknownContents; + x->child_state = responseToken_content; + state = len; + break; + + case mechToken_tag: + x->brother_state = UnknownContents; + x->child_state = mechToken_content; + state = len; + break; + + case mechToken_content: + case mechToken_content2: + break; + + /************************************************************************ + ************************************************************************ + ************************************************************************ + ************************************************************************ + ************************************************************************ + ************************************************************************ + ************************************************************************ + */ + case responseToken_content: + ntlmssp_decode_init(&spnego->ntlmssp, x->stack.remainings[0] + 1); + state = responseToken_content2; + /* fall through */ + case responseToken_content2: + { + size_t new_max = length - i; + + if (new_max > x->stack.remainings[0] + 1) + new_max = x->stack.remainings[0] + 1; + + ntlmssp_decode(&spnego->ntlmssp, px+i, new_max, banout); + + x->stack.remainings[0] -= (new_max - 1); + if (x->stack.remainings[0] == 0) { + if (spnego->ntlmssp.buf) + free(spnego->ntlmssp.buf); + } + } + break; + + case len: + /* We do the same processing for all the various length fields. + * There are three possible length fields: + * 0x7F - for lengths 127 and below + * 0x81 XX - for lengths 127 to 255 + * 0x82 XX XX - for length 256 to 65535 + * This state processes the first byte, and if it's an extended + * field, switches to the correspondign xxx_LENLEN state + */ + if (px[i] & 0x80) { + x->u.tag.length_of_length = px[i]&0x7F; + x->u.tag.remaining = 0; + state = lenlen; + break; + } else { + x->u.tag.remaining = px[i]; + ASN1_push(x, x->brother_state, x->u.tag.remaining); + state = x->child_state; + memset(&x->u, 0, sizeof(x->u)); + break; + } + break; + case lenlen: + /* We process all multibyte lengths the same way in this + * state. + */ + + /* [ASN1-DER-LENGTH] + * Check for strict DER compliance, which says that there should + * be no leading zero bytes */ + if (x->u.tag.remaining == 0 && px[i] == 0) + x->is_der_failure = 1; + + /* parse this byte */ + x->u.tag.remaining = (x->u.tag.remaining)<<8 | px[i]; + x->u.tag.length_of_length--; + + /* If we aren't finished yet, loop around and grab the next */ + if (x->u.tag.length_of_length) + continue; + + /* [ASN1-DER-LENGTH] + * Check for strict DER compliance, which says that for lengths + * 127 and below, we need only 1 byte to encode it, not many */ + if (x->u.tag.remaining < 128) + x->is_der_failure = 1; + + /* + * We have finished parsing the tag-length fields, and are now + * ready to parse the 'value'. Push the current state on the + * stack, then decend into the child field. + */ + ASN1_push(x, x->brother_state, x->u.tag.remaining); + state = x->child_state; + memset(&x->u, 0, sizeof(x->u)); + break; + default: + ; + } + } +} /**************************************************************************** @@ -1069,3 +1381,12 @@ x509_decode_init(struct CertDecode *x, size_t length) memset(x, 0, sizeof(*x)); ASN1_push(x, 0xFFFFFFFF, length); } +void +spnego_decode_init(struct SpnegoDecode *x, size_t length) +{ + memset(x, 0, sizeof(*x)); + + ASN1_push(x->x509, 0xFFFFFFFF, length); +} + + diff --git a/src/proto-x509.h b/src/proto-x509.h index 638be84..e078695 100644 --- a/src/proto-x509.h +++ b/src/proto-x509.h @@ -47,6 +47,9 @@ struct CertDecode { struct { unsigned type; } subject; + + unsigned child_state; + unsigned brother_state; /** * This union contains the intermediate/partial values as we are decoding diff --git a/xcode4/masscan.xcodeproj/project.pbxproj b/xcode4/masscan.xcodeproj/project.pbxproj index b36144a..8dce0a3 100644 --- a/xcode4/masscan.xcodeproj/project.pbxproj +++ b/xcode4/masscan.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 110ED16820CB0BC200690C91 /* proto-ntlmssp.c in Sources */ = {isa = PBXBuildFile; fileRef = 110ED16720CB0BC200690C91 /* proto-ntlmssp.c */; }; 11126597197A086B00DC5987 /* out-unicornscan.c in Sources */ = {isa = PBXBuildFile; fileRef = 11126596197A086B00DC5987 /* out-unicornscan.c */; }; 112A871A1F9D8DF200D4D240 /* out-ndjson.c in Sources */ = {isa = PBXBuildFile; fileRef = 112A87191F9D8DF200D4D240 /* out-ndjson.c */; }; 11420DD319A2D47A00DB5BFE /* proto-vnc.c in Sources */ = {isa = PBXBuildFile; fileRef = 11420DD219A2D47A00DB5BFE /* proto-vnc.c */; }; @@ -108,6 +109,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 110ED16520CA6A8300690C91 /* proto-spnego.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-spnego.h"; sourceTree = "<group>"; }; + 110ED16620CB0BC200690C91 /* proto-ntlmssp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-ntlmssp.h"; sourceTree = "<group>"; }; + 110ED16720CB0BC200690C91 /* proto-ntlmssp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "proto-ntlmssp.c"; sourceTree = "<group>"; }; 11126596197A086B00DC5987 /* out-unicornscan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "out-unicornscan.c"; sourceTree = "<group>"; }; 112A87191F9D8DF200D4D240 /* out-ndjson.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "out-ndjson.c"; sourceTree = "<group>"; }; 113AD3B818208A1900D5E067 /* masscan-status.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "masscan-status.h"; sourceTree = "<group>"; }; @@ -391,6 +395,9 @@ 11B360CA1F9016C00020F3A3 /* proto */ = { isa = PBXGroup; children = ( + 110ED16720CB0BC200690C91 /* proto-ntlmssp.c */, + 110ED16620CB0BC200690C91 /* proto-ntlmssp.h */, + 110ED16520CA6A8300690C91 /* proto-spnego.h */, 11DE129420ABC2650041135D /* proto-smb.h */, 11DE129520ABC2650041135D /* proto-smb.c */, 119AB2042051FFED008E4DDD /* proto-memcached.c */, @@ -580,6 +587,7 @@ 11A921DC17DBCC7E00DDFD32 /* main.c in Sources */, 11A921DD17DBCC7E00DDFD32 /* out-binary.c in Sources */, 11A921DE17DBCC7E00DDFD32 /* out-null.c in Sources */, + 110ED16820CB0BC200690C91 /* proto-ntlmssp.c in Sources */, 11A921DF17DBCC7E00DDFD32 /* out-text.c in Sources */, 11A921E017DBCC7E00DDFD32 /* out-xml.c in Sources */, 11A921E117DBCC7E00DDFD32 /* output.c in Sources */, -- GitLab