From 99cbcd76a6cf3edb5128bb9c3c3ac814f8975fd9 Mon Sep 17 00:00:00 2001 From: Robert David Graham <robert_david_graham@yahoo.com> Date: Mon, 28 May 2018 17:41:59 -0400 Subject: [PATCH] SMBv1 (SMBv2 not yet done) --- src/masscan-app.c | 2 + src/masscan-app.h | 1 + src/proto-banner1.c | 33 +- src/proto-banner1.h | 43 ++ src/proto-smb.c | 860 +++++++++++++++++++++++ src/proto-smb.h | 8 + xcode4/masscan.xcodeproj/project.pbxproj | 6 + 7 files changed, 952 insertions(+), 1 deletion(-) create mode 100644 src/proto-smb.c create mode 100644 src/proto-smb.h diff --git a/src/masscan-app.c b/src/masscan-app.c index a7d88a4..9241c40 100644 --- a/src/masscan-app.c +++ b/src/masscan-app.c @@ -21,6 +21,7 @@ masscan_app_to_string(enum ApplicationProtocol proto) case PROTO_SNMP: return "snmp"; case PROTO_NBTSTAT: return "nbtstat"; case PROTO_SSL3: return "ssl"; + case PROTO_SMB: return "smb"; case PROTO_SMTP: return "smtp"; case PROTO_POP3: return "pop"; case PROTO_IMAP4: return "imap"; @@ -61,6 +62,7 @@ masscan_string_to_app(const char *str) {"ssh2", PROTO_SSH2}, {"nbtstat", PROTO_NBTSTAT}, {"ssl", PROTO_SSL3}, + {"smb", PROTO_SMB}, {"pop", PROTO_POP3}, {"imap", PROTO_IMAP4}, {"x509", PROTO_X509_CERT}, diff --git a/src/masscan-app.h b/src/masscan-app.h index 402bd1b..3dcfaff 100644 --- a/src/masscan-app.h +++ b/src/masscan-app.h @@ -16,6 +16,7 @@ enum ApplicationProtocol { PROTO_SNMP, /* simple network management protocol, udp/161 */ PROTO_NBTSTAT, /* netbios, udp/137 */ PROTO_SSL3, + PROTO_SMB, /* SMB tcp/139 and tcp/445 */ PROTO_SMTP, PROTO_POP3, PROTO_IMAP4, diff --git a/src/proto-banner1.c b/src/proto-banner1.c index b1d8b03..1bc30d3 100644 --- a/src/proto-banner1.c +++ b/src/proto-banner1.c @@ -8,6 +8,7 @@ #include "proto-banner1.h" #include "proto-http.h" #include "proto-ssl.h" +#include "proto-smb.h" #include "proto-ssh.h" #include "proto-ftp.h" #include "proto-smtp.h" @@ -24,6 +25,16 @@ struct Patterns patterns[] = { + {"\x00\x00" "**" "\xff" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS}, + {"\x00\x00" "**" "\xfe" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS}, + + {"\x83\x00\x00\x01\x80", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening on called name */ + {"\x83\x00\x00\x01\x81", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening for calling name */ + {"\x83\x00\x00\x01\x82", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name not present */ + {"\x83\x00\x00\x01\x83", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name present, but insufficient resources */ + {"\x83\x00\x00\x01\x8f", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Unspecified error */ + + /* ...the remainder can be in any order */ {"SSH-1.", 6, PROTO_SSH1, SMACK_ANCHOR_BEGIN}, {"SSH-2.", 6, PROTO_SSH2, SMACK_ANCHOR_BEGIN}, {"HTTP/1.", 7, PROTO_HTTP, SMACK_ANCHOR_BEGIN}, @@ -31,6 +42,7 @@ struct Patterns patterns[] = { {"220 ", 4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 1}, {"+OK ", 4, PROTO_POP3, SMACK_ANCHOR_BEGIN}, {"* OK ", 5, PROTO_IMAP4, SMACK_ANCHOR_BEGIN}, + {"521 ", 4, PROTO_SMTP, SMACK_ANCHOR_BEGIN}, {"\x16\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN}, {"\x16\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN}, {"\x16\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN}, @@ -197,6 +209,15 @@ banner1_parse( banout, more); break; + case PROTO_SMB: + banner_smb1.parse( + banner1, + banner1->http_fields, + tcb_state, + px, length, + banout, + more); + break; case PROTO_VNC_RFB: banner_vnc.parse( banner1, banner1->http_fields, @@ -265,10 +286,14 @@ banner1_create(void) banner_smtp.init(b); banner_ssh.init(b); banner_ssl.init(b); + banner_smb0.init(b); + banner_smb1.init(b); banner_vnc.init(b); b->tcp_payloads[80] = &banner_http; b->tcp_payloads[8080] = &banner_http; + b->tcp_payloads[139] = (void*)&banner_smb0; + b->tcp_payloads[445] = (void*)&banner_smb1; b->tcp_payloads[443] = (void*)&banner_ssl; /* HTTP/s */ b->tcp_payloads[465] = (void*)&banner_ssl; /* SMTP/s */ @@ -449,7 +474,13 @@ banner1_selftest() fprintf(stderr, "SSL banner: selftest failed\n"); return 1; } - + + x = banner_smb1.selftest(); + if (x) { + fprintf(stderr, "SMB banner: selftest failed\n"); + return 1; + } + x = banner_http.selftest(); if (x) { fprintf(stderr, "HTTP banner: selftest failed\n"); diff --git a/src/proto-banner1.h b/src/proto-banner1.h index ce99ce7..21a572d 100644 --- a/src/proto-banner1.h +++ b/src/proto-banner1.h @@ -135,6 +135,48 @@ struct MEMCACHEDSTUFF { unsigned match; }; +struct Smb72_Negotiate { + uint16_t DialectIndex; + uint16_t SecurityMode; + uint64_t SystemTime; + uint32_t Capabilities; + uint16_t ServerTimeZone; + uint8_t ChallengeLength; + uint8_t ChallengeOffset; +}; + +struct SMBSTUFF { + unsigned char nbt_type; + unsigned char nbt_flags; + unsigned 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 Smb72_Negotiate negotiate; + } parms1; + + union { + + } pkt; +}; + struct ProtocolState { unsigned state; unsigned remaining; @@ -151,6 +193,7 @@ struct ProtocolState { struct SMTPSTUFF smtp; struct POP3STUFF pop3; struct MEMCACHEDSTUFF memcached; + struct SMBSTUFF smb; } sub; }; diff --git a/src/proto-smb.c b/src/proto-smb.c new file mode 100644 index 0000000..28663a1 --- /dev/null +++ b/src/proto-smb.c @@ -0,0 +1,860 @@ +/* + SMB parser + + */ +#include "proto-smb.h" +#include "proto-interactive.h" +#include "unusedparm.h" +#include "masscan-app.h" +#include "siphash24.h" +#include "string_s.h" +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <stddef.h> + + +/* + "NT LM 0.12" - Win2k + "SMB 2.002" 0x0202 Vista + "SMB 2.???" 0x02FF Win7, Windows 2008 + "PC NETWORK PROGRAM 1.0" MS-DOS + "MICROSOFT NETWORKS 1.03" MS-DOS + "MICROSOFT NETWORKS 3.0" MS-DOS + "LANMAN 1.0" OS/2 + "LM1.2X002" OS/2 + */ +/* +PC NETWORK PROGRAM 1.0 +LANMAN1.0 +Windows for Workgroups 3.1a +LM1.2X002 +LANMAN2.1 +NT LM 0.12 +SMB 2.002 +SMB 2.??? +Samba +XENIX CORE +*/ +/* + References: + http://pubs.opengroup.org/onlinepubs/9697999099/toc.pdf + */ + +struct SmbParams { + unsigned short command; + unsigned short external_offset; + unsigned char external_length; + unsigned char internal_type; + unsigned short internal_offset; +}; + +enum InternalType { + IT_uint0, + IT_uint8, + IT_uint16, + IT_uint32, + IT_uint64, +}; + +struct SmbParams params[] = { +/* + USHORT DialectIndex; + UCHAR SecurityMode; + USHORT MaxMpxCount; + USHORT MaxNumberVcs; + ULONG MaxBufferSize; + ULONG MaxRawSize; + ULONG SessionKey; + ULONG Capabilities; + FILETIME SystemTime; + SHORT ServerTimeZone; + UCHAR ChallengeLength; + */ + {0x72, 0, 2, IT_uint16, offsetof(struct Smb72_Negotiate, DialectIndex)}, + {0x72, 2, 1, IT_uint8, offsetof(struct Smb72_Negotiate, SecurityMode)}, + //{0x72, 3, 2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxMpxCount)}, + //{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, 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)}, + + {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 +}; + + +/***************************************************************************** + * + * ****** WARNING: UGLY HACK !!!! ****** + * + * This code is an ugly hack so I can express SMB parameters (word_count) + * headers as a structure instead of writing individual parsers for them. + * This code makes no sense. If you find a bug in it, it's probably worth + * rewriting rather than figure out its convoluted logic. No really, I mean + * this. + * + *****************************************************************************/ +static size_t +smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max) +{ + 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); + + //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 (; offset < max; offset++, smb->smb1.param_offset++) { + again: + + //printf("\n%u/%u %u\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c); + + /* If we've gone past our header, just continue consuming bytes */ + if (params[c].command != smb->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) { + c++; + goto again; + } + /* Haven't reached the next field yet */ + if (params[c].external_offset > smb->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]); + + /* Shift the type, because all fields little-endian */ + switch (params[c].internal_type) { + case IT_uint0: + default: + break; + case IT_uint8: + { + uint8_t *x = memberat(uint8_t, &smb->parms1, params[c].internal_offset); + *x = px[offset]; + } + break; + case IT_uint16: + { + uint16_t *x = memberat(uint16_t, &smb->parms1, params[c].internal_offset); + //*x <<= 8; + *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8); + } + break; + case IT_uint32: + { + uint32_t *x = memberat(uint32_t, &smb->parms1, params[c].internal_offset); + //*x <<= 8; + *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8); + } + break; + case IT_uint64: + { + uint64_t *x = memberat(uint64_t, &smb->parms1, params[c].internal_offset); + //*x <<= 8; + *x |= (uint64_t)px[offset] << (uint64_t)((smb->smb1.param_offset - params[c].external_offset)*8); + } + break; + } + + } + + /* Return the number of bytes processed */ + return offset - original_offset; +} + +/***************************************************************************** + *****************************************************************************/ +static const unsigned long long TICKS_PER_SECOND = 10000000LL; +static const unsigned long long EPOCH_DIFFERENCE = 11644473600LL; +static time_t +convert_windows_time(long long int filetime) +{ + unsigned long long seconds = filetime / TICKS_PER_SECOND; + seconds -= EPOCH_DIFFERENCE; + return (time_t)seconds; +} + +/***************************************************************************** + *****************************************************************************/ +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) +{ + size_t original_offset = offset; + unsigned state = smb->smb1.byte_state; + + + if (max > offset + (smb->smb1.byte_count - smb->smb1.byte_offset)) + max = offset + (smb->smb1.byte_count - smb->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; + } else { + state = D_NEGOT_DOMAINA; + } + offset--; + } else + smb->parms1.negotiate.ChallengeLength--; + break; + case D_NEGOT_DOMAIN1: + case D_NEGOT_NAME1: + smb->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, ' '); + state++; + } else { + name_append_char(banout, smb->smb1.unicode_char); + state--; + } + break; + default: + break; + } + + smb->smb1.byte_state = state; + smb->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, + 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; + enum { + NBT_TYPE, + NBT_FLAGS, + NBT_LEN1, + NBT_LEN2, + NBT_ERR, + NBT_DRAIN, + + SMB_VER, + SMB1_VER_S, SMB1_VER_M, SMB1_VER_B, + + SMB1_CMD, + SMB1_STATUS1, SMB1_STATUS2, SMB1_STATUS3, SMB1_STATUS4, + SMB1_FLAGS1, + SMB1_FLAGS2, + SMB1_FLAGS3, + SMB1_PID1, SMB1_PID2, + SMB1_SIG1, SMB1_SIG2, SMB1_SIG3, SMB1_SIG4, + SMB1_SIG5, SMB1_SIG6, SMB1_SIG7, SMB1_SIG8, + SMB1_RSVD1,SMB1_RSVD2, + SMB1_TID1, SMB1_TID2, + SMB1_PID3, SMB1_PID4, + SMB1_UID1, SMB1_UID2, + SMB1_MID1, SMB1_MID2, + SMB1_WORD_COUNT, + SMB1_PARAMETERS, + SMB1_BYTE_COUNT1, + SMB1_BYTE_COUNT2, + SMB1_DATA, + SMB1_DATA_AFTER, + /* + UCHAR Protocol[4]; + UCHAR Command; + SMB_ERROR Status; + UCHAR Flags; + USHORT Flags2; + USHORT PIDHigh; + UCHAR SecurityFeatures[8]; + USHORT Reserved; + USHORT TID; + USHORT PIDLow; + USHORT UID; + USHORT MID; + */ + + + 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; + } + + + /* + * `for all bytes in the segment` + * `do a state transition for that byte ` + */ + 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); + state = SMB1_VER_S; + break; + case 0xFE: + banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN); + state = SMB2_VER_S; + break; + default: + banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN); + state = NBT_UNKNOWN; + } + break; + case SMB1_VER_S: + case SMB2_VER_S: + + if (px[i] != 'S') + state = NBT_UNKNOWN; + else + state++; + break; + case SMB1_VER_M: + case SMB2_VER_M: + if (px[i] != 'M') + state = NBT_UNKNOWN; + else + state++; + break; + case SMB1_VER_B: + case SMB2_VER_B: + if (px[i] != 'B') + state = NBT_UNKNOWN; + else + state++; + break; + + case SMB1_CMD: + memset(&smb->smb1, 0, sizeof(smb->smb1)); + smb->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]; + state++; + break; + case SMB1_FLAGS1: + smb->smb1.flags1 = px[i]; + state++; + break; + case SMB1_FLAGS2: + case SMB1_FLAGS3: + smb->smb1.flags2 <<= 8; + smb->smb1.flags2 |= px[i]; + state++; + break; + case SMB1_PID1: case SMB1_PID2: + smb->smb1.pid <<= 8; + smb->smb1.pid |= px[i]; + state++; + break; + case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4: + case SMB1_SIG5: case SMB1_SIG6: case SMB1_SIG7: case SMB1_SIG8: + state++; + break; + case SMB1_RSVD1:case SMB1_RSVD2: + state++; + break; + case SMB1_TID1: case SMB1_TID2: + smb->smb1.tid <<= 8; + smb->smb1.tid |= px[i]; + state++; + break; + case SMB1_PID3: case SMB1_PID4: + smb->smb1.pid <<= 8; + smb->smb1.pid |= px[i]; + state++; + break; + case SMB1_UID1: case SMB1_UID2: + smb->smb1.uid <<= 8; + smb->smb1.uid |= px[i]; + state++; + break; + case SMB1_MID1: case SMB1_MID2: + smb->smb1.mid <<= 8; + smb->smb1.mid |= px[i]; + state++; + break; + case SMB1_WORD_COUNT: + smb->smb1.param_length = px[i]*2; + state++; + break; + case SMB1_PARAMETERS: + /* Transfer control to a sub-parser, which may consume zero + * or more bytes, up to the end of the parameters field + * (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) + break; + + /* We've reached the end of the parameters field, so go onto + * read the byte-count/data field */ + state = SMB1_BYTE_COUNT1; + + /* Unconsume the next byte. The "word-count" field may have been + * zero when we get to this state, so therefore the logic needs + * to be written to handle this. That means when we loop around + * again, we need to counter-act the fact that we will automatically + * increment the index, so we substract one from it here. */ + i--; + + /* Process the parameter/word-count field according to what it + * actually contained + * TODO: I should make this a function, but I'm lazy + */ + switch (smb->smb1.command) { + case 0x72: + { + char str[64] = "(err)"; + time_t timestamp = convert_windows_time(smb->parms1.negotiate.SystemTime); + struct tm tm = {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); + + } + break; + default: + banout_append(banout, PROTO_SMB, "-- ", AUTO_LEN); + smb->smb1.byte_state = D_UNKNOWN; + } + + break; + + case SMB1_BYTE_COUNT1: + smb->smb1.byte_count = px[i]; + state++; + break; + case SMB1_BYTE_COUNT2: + smb->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) { + state = SMB1_DATA_AFTER; + i--; /* unconsume byte because of auto-increment */ + } + break; + + case SMB1_DATA_AFTER: + if (i < max) { + ; + } else { + state = 0; + i--; + } + break; + + 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; + } + + pstate->state = state; +} + +/***************************************************************************** + *****************************************************************************/ +static void * +smb_init(struct Banner1 *banner1) +{ + UNUSEDPARM(banner1); + return 0; +} + +/***************************************************************************** + *****************************************************************************/ + +static const char +smb0_hello_template[] = { + 0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46, + 0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45, + 0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46, + 0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x45, + 0x44, 0x46, 0x43, 0x45, 0x46, 0x45, 0x46, 0x45, + 0x44, 0x45, 0x49, 0x45, 0x46, 0x46, 0x43, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00, + + 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, 0x2f, 0xff, 0x53, 0x4d, 0x42, + 0x72, 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, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, + 0x31, 0x32, 0x00 +}; + +/***************************************************************************** + *****************************************************************************/ +static int +smb_selftest(void) +{ + struct Banner1 *banner1; + struct ProtocolState state[1]; + struct BannerOutput banout1[1]; + struct InteractiveData more; + int x; + size_t i; + + unsigned char packet_bytes[] = { + 0x00, 0x00, 0x00, 0x69, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x11, 0x00, 0x00, 0x03, + 0x10, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0xe3, 0x01, 0x00, 0x1f, 0xac, 0xe7, 0x7f, + 0x8a, 0xf0, 0xd3, 0x01, 0xf0, 0x00, 0x08, 0x24, + 0x00, 0xc2, 0xe5, 0x34, 0x10, 0xfd, 0x29, 0xa7, + 0x75, 0x42, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x00, + 0x00, 0x53, 0x00, 0x48, 0x00, 0x49, 0x00, 0x50, + 0x00, 0x42, 0x00, 0x41, 0x00, 0x52, 0x00, 0x42, + 0x00, 0x4f, 0x00, 0x00, 0x00, + + /*0x00, 0x00, 0x00, 0x90, 0xff, 0x53, 0x4d, 0x42, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x98, 0x03, 0x80, + 0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06, + 0xe8, 0xfc, 0x00, 0x00, 0x00, 0x08, 0xfe, 0xca, + 0x00, 0x08, 0x00, 0x00, 0x03, 0x75, 0x00, 0x81, + 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x35, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00, + 0x4c, 0x00, 0x41, 0x00, 0x4e, 0x00, 0x20, 0x00, + 0x4d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x53, 0x00, 0x45, 0x00, + 0x41, 0x00, 0x55, 0x00, 0x00, 0x03, 0xff, 0x00, + 0x90, 0x00, 0x01, 0x00, 0x06, 0x00, 0x49, 0x50, + 0x43, 0x00, 0x00, 0x00*/ + + }; + + banner1 = banner1_create(); + banout_init(banout1); + memset(&state[0], 0, sizeof(state[0])); + + smb_parse_record(banner1, + 0, + state, + packet_bytes, + sizeof(packet_bytes), + 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); + + /* + * LET'S FUZZ THIS CRAP!!! + * + * We are going to re-parse the response packet as many times as needed, + * each time flipping one bit in the packet. This should crash the + * parser if it has such a bug that will crash it. + */ + for (i=2; i< 5 && i<sizeof(packet_bytes); i++) { + size_t j; + + for (j=0; j<8; j++) { + size_t flip = 1<<j; + + packet_bytes[i] ^= flip; + + banner1 = banner1_create(); + banout_init(banout1); + memset(&state[0], 0, sizeof(state[0])); + + smb_parse_record(banner1, + 0, + state, + packet_bytes, + sizeof(packet_bytes), + banout1, + &more); + banner1_destroy(banner1); + banout_release(banout1); + + packet_bytes[i] ^= flip; + + } + } + return 0; +} + +/***************************************************************************** + * This is the 'plugin' structure that registers callbacks for this parser in + * the main system. + *****************************************************************************/ +struct ProtocolParserStream banner_smb0 = { + "smb", 139, smb0_hello_template, sizeof(smb0_hello_template), 0, + smb_selftest, + smb_init, + smb_parse_record, +}; +struct ProtocolParserStream banner_smb1 = { + "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0, + smb_selftest, + smb_init, + smb_parse_record, +}; + diff --git a/src/proto-smb.h b/src/proto-smb.h new file mode 100644 index 0000000..98649e8 --- /dev/null +++ b/src/proto-smb.h @@ -0,0 +1,8 @@ +#ifndef PROTO_SMB_H +#define PROTO_SMB_H +#include "proto-banner1.h" + +extern struct ProtocolParserStream banner_smb0; +extern struct ProtocolParserStream banner_smb1; + +#endif diff --git a/xcode4/masscan.xcodeproj/project.pbxproj b/xcode4/masscan.xcodeproj/project.pbxproj index c008f4f..b36144a 100644 --- a/xcode4/masscan.xcodeproj/project.pbxproj +++ b/xcode4/masscan.xcodeproj/project.pbxproj @@ -89,6 +89,7 @@ 11C936C31EDCE77F0023D32E /* in-filter.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936BF1EDCE77F0023D32E /* in-filter.c */; }; 11C936C41EDCE77F0023D32E /* in-report.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C11EDCE77F0023D32E /* in-report.c */; }; 11C936C71EDCE8B40023D32E /* rawsock-pcap.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C51EDCE8B40023D32E /* rawsock-pcap.c */; }; + 11DE129620ABC2650041135D /* proto-smb.c in Sources */ = {isa = PBXBuildFile; fileRef = 11DE129520ABC2650041135D /* proto-smb.c */; }; 11E76DB41889BC5200061F45 /* pixie-backtrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 11E76DB21889BC5200061F45 /* pixie-backtrace.c */; }; 11F9375419F1A54200C1947F /* script-sslv3.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375319F1A54200C1947F /* script-sslv3.c */; }; 11F9375719F1AD5000C1947F /* script-heartbleed.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375619F1AD5000C1947F /* script-heartbleed.c */; }; @@ -261,6 +262,8 @@ 11C936C21EDCE77F0023D32E /* in-report.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "in-report.h"; sourceTree = "<group>"; }; 11C936C51EDCE8B40023D32E /* rawsock-pcap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "rawsock-pcap.c"; sourceTree = "<group>"; }; 11C936C61EDCE8B40023D32E /* rawsock-pcap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "rawsock-pcap.h"; sourceTree = "<group>"; }; + 11DE129420ABC2650041135D /* proto-smb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-smb.h"; sourceTree = "<group>"; }; + 11DE129520ABC2650041135D /* proto-smb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "proto-smb.c"; sourceTree = "<group>"; }; 11E76DB21889BC5200061F45 /* pixie-backtrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pixie-backtrace.c"; sourceTree = "<group>"; }; 11E76DB31889BC5200061F45 /* pixie-backtrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pixie-backtrace.h"; sourceTree = "<group>"; }; 11F9375319F1A54200C1947F /* script-sslv3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "script-sslv3.c"; sourceTree = "<group>"; }; @@ -388,6 +391,8 @@ 11B360CA1F9016C00020F3A3 /* proto */ = { isa = PBXGroup; children = ( + 11DE129420ABC2650041135D /* proto-smb.h */, + 11DE129520ABC2650041135D /* proto-smb.c */, 119AB2042051FFED008E4DDD /* proto-memcached.c */, 119AB2052051FFED008E4DDD /* proto-memcached.h */, 11A921AC17DBCC7E00DDFD32 /* proto-arp.c */, @@ -604,6 +609,7 @@ 11A921F817DBCC7E00DDFD32 /* syn-cookie.c in Sources */, 11A921F917DBCC7E00DDFD32 /* templ-pkt.c in Sources */, 11A921FA17DBCC7E00DDFD32 /* xring.c in Sources */, + 11DE129620ABC2650041135D /* proto-smb.c in Sources */, 11B2DD9E17DE4DD8007FC363 /* templ-payloads.c in Sources */, 11AC80ED17E0DAD4001BCE3A /* proto-http.c in Sources */, 11AC80EE17E0DAD4001BCE3A /* proto-icmp.c in Sources */, -- GitLab