diff options
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | Makefile.nmake | 3 | ||||
-rw-r--r-- | crypt-des.c | 311 | ||||
-rw-r--r-- | crypt-des.h | 26 | ||||
-rw-r--r-- | packet-dcerpc.c | 237 | ||||
-rw-r--r-- | packet-ntlmssp.c | 536 |
7 files changed, 982 insertions, 137 deletions
@@ -1234,6 +1234,8 @@ Devin Heitmueller <dheitmueller[AT]netilla.com> { UnicodeChangePassword2 Supply offset to dissectors for connection-oriented DCERPC PDU types + Support for decrypting DCERPC conversations using NTLMSSP + version 1 } Chenjiang Hu <chu[AT]chiaro.com> { diff --git a/Makefile.am b/Makefile.am index f6a9b76a34..d56ac0fcb4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for Ethereal # -# $Id: Makefile.am,v 1.530 2002/12/20 07:56:07 sharpe Exp $ +# $Id: Makefile.am,v 1.531 2002/12/31 08:05:28 guy Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@ethereal.com> @@ -626,6 +626,8 @@ ETHEREAL_COMMON_SRC = \ column.h \ conditions.c \ conditions.h \ + crypt-des.c \ + crypt-des.h \ crypt-md4.c \ crypt-md4.h \ crypt-md5.c \ diff --git a/Makefile.nmake b/Makefile.nmake index fb41bd30b8..19b8f44e44 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -1,7 +1,7 @@ ## Makefile for building ethereal.exe with Microsoft C and nmake ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake # -# $Id: Makefile.nmake,v 1.264 2002/12/18 17:44:54 gerald Exp $ +# $Id: Makefile.nmake,v 1.265 2002/12/31 08:05:29 guy Exp $ include config.nmake include <win32.mak> @@ -351,6 +351,7 @@ ETHEREAL_COMMON_OBJECTS = \ cfile.obj \ column.obj \ conditions.obj \ + crypt-des.obj \ crypt-md4.obj \ crypt-md5.obj \ crypt-rc4.obj \ diff --git a/crypt-des.c b/crypt-des.c new file mode 100644 index 0000000000..f39e821579 --- /dev/null +++ b/crypt-des.c @@ -0,0 +1,311 @@ +/* + Unix SMB/CIFS implementation. + + a partial implementation of DES designed for use in the + SMB authentication protocol + + Copyright (C) Andrew Tridgell 1998 + + $Id: crypt-des.c,v 1.1 2002/12/31 08:05:29 guy Exp $ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <glib.h> + +/* NOTES: + + This code makes no attempt to be fast! In fact, it is a very + slow implementation + + This code is NOT a complete DES implementation. It implements only + the minimum necessary for SMB authentication, as used by all SMB + products (including every copy of Microsoft Windows95 ever sold) + + In particular, it can only do a unchained forward DES pass. This + means it is not possible to use this code for encryption/decryption + of data, instead it is only useful as a "hash" algorithm. + + There is no entry point into this code that allows normal DES operation. + + I believe this means that this code does not come under ITAR + regulations but this is NOT a legal opinion. If you are concerned + about the applicability of ITAR regulations to this code then you + should confirm it for yourself (and maybe let me know if you come + up with a different answer to the one above) +*/ + + +#define uchar unsigned char + +static const uchar perm1[56] = {57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4}; + +static const uchar perm2[48] = {14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32}; + +static const uchar perm3[64] = {58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7}; + +static const uchar perm4[48] = { 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1}; + +static const uchar perm5[32] = { 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25}; + + +static const uchar perm6[64] ={ 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25}; + + +static const uchar sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; + +static const uchar sbox[8][4][16] = { + {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, + {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, + {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, + {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}}, + + {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, + {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, + {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, + {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}}, + + {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, + {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, + {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, + {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}}, + + {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, + {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, + {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, + {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}}, + + {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, + {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, + {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14}, + {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}}, + + {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11}, + {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8}, + {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6}, + {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}}, + + {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, + {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6}, + {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2}, + {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}}, + + {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7}, + {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, + {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8}, + {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}}; + +static void permute(char *out, const char *in, const uchar *p, int n) +{ + int i; + for (i=0;i<n;i++) + out[i] = in[p[i]-1]; +} + +static void lshift(char *d, int count, int n) +{ + char out[64]; + int i; + for (i=0;i<n;i++) + out[i] = d[(i+count)%n]; + for (i=0;i<n;i++) + d[i] = out[i]; +} + +static void concat(char *out, char *in1, char *in2, int l1, int l2) +{ + while (l1--) + *out++ = *in1++; + while (l2--) + *out++ = *in2++; +} + +static void xor(char *out, char *in1, char *in2, int n) +{ + int i; + for (i=0;i<n;i++) + out[i] = in1[i] ^ in2[i]; +} + +static void dohash(char *out, char *in, char *key, int forw) +{ + int i, j, k; + char pk1[56]; + char c[28]; + char d[28]; + char cd[56]; + char ki[16][48]; + char pd1[64]; + char l[32], r[32]; + char rl[64]; + + permute(pk1, key, perm1, 56); + + for (i=0;i<28;i++) + c[i] = pk1[i]; + for (i=0;i<28;i++) + d[i] = pk1[i+28]; + + for (i=0;i<16;i++) { + lshift(c, sc[i], 28); + lshift(d, sc[i], 28); + + concat(cd, c, d, 28, 28); + permute(ki[i], cd, perm2, 48); + } + + permute(pd1, in, perm3, 64); + + for (j=0;j<32;j++) { + l[j] = pd1[j]; + r[j] = pd1[j+32]; + } + + for (i=0;i<16;i++) { + char er[48]; + char erk[48]; + char b[8][6]; + char cb[32]; + char pcb[32]; + char r2[32]; + + permute(er, r, perm4, 48); + + xor(erk, er, ki[forw ? i : 15 - i], 48); + + for (j=0;j<8;j++) + for (k=0;k<6;k++) + b[j][k] = erk[j*6 + k]; + + for (j=0;j<8;j++) { + int m, n; + m = (b[j][0]<<1) | b[j][5]; + + n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4]; + + for (k=0;k<4;k++) + b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0; + } + + for (j=0;j<8;j++) + for (k=0;k<4;k++) + cb[j*4+k] = b[j][k]; + permute(pcb, cb, perm5, 32); + + xor(r2, l, pcb, 32); + + for (j=0;j<32;j++) + l[j] = r[j]; + + for (j=0;j<32;j++) + r[j] = r2[j]; + } + + concat(rl, r, l, 32, 32); + + permute(out, rl, perm6, 64); +} + +static void str_to_key(const unsigned char *str,unsigned char *key) +{ + int i; + + key[0] = str[0]>>1; + key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); + key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); + key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); + key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); + key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); + key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); + key[7] = str[6]&0x7F; + for (i=0;i<8;i++) { + key[i] = (key[i]<<1); + } +} + + +void crypt_des_ecb(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw) +{ + int i; + char outb[64]; + char inb[64]; + char keyb[64]; + unsigned char key2[8]; + + str_to_key(key, key2); + + for (i=0;i<64;i++) { + inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0; + keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0; + outb[i] = 0; + } + + dohash(outb, inb, keyb, forw); + + for (i=0;i<8;i++) { + out[i] = 0; + } + + for (i=0;i<64;i++) { + if (outb[i]) + out[i/8] |= (1<<(7-(i%8))); + } +} + diff --git a/crypt-des.h b/crypt-des.h new file mode 100644 index 0000000000..8f799be2d6 --- /dev/null +++ b/crypt-des.h @@ -0,0 +1,26 @@ +/* + Unix SMB/CIFS implementation. + + a partial implementation of DES designed for use in the + SMB authentication protocol + + Copyright (C) Andrew Tridgell 1998 + + $Id: crypt-des.h,v 1.1 2002/12/31 08:05:29 guy Exp $ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +void crypt_des_ecb(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw); diff --git a/packet-dcerpc.c b/packet-dcerpc.c index ac35fcbd44..4e6b89ce25 100644 --- a/packet-dcerpc.c +++ b/packet-dcerpc.c @@ -2,7 +2,7 @@ * Routines for DCERPC packet disassembly * Copyright 2001, Todd Sabin <tas@webspan.net> * - * $Id: packet-dcerpc.c,v 1.92 2002/12/19 11:22:16 sahlberg Exp $ + * $Id: packet-dcerpc.c,v 1.93 2002/12/31 08:05:29 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -404,7 +404,8 @@ static gint ett_dcerpc_fragments = -1; static gint ett_dcerpc_fragment = -1; static gint ett_decrpc_krb5_auth_verf = -1; -static dissector_handle_t ntlmssp_handle, ntlmssp_verf_handle; +static dissector_handle_t ntlmssp_handle, ntlmssp_verf_handle, + ntlmssp_enc_payload_handle; static dissector_handle_t gssapi_handle, gssapi_verf_handle; static const fragment_items dcerpc_frag_items = { @@ -422,6 +423,13 @@ static const fragment_items dcerpc_frag_items = { "fragments" }; +typedef struct _dcerpc_auth_info { + guint8 auth_pad_len; + guint8 auth_level; + guint8 auth_type; + guint32 auth_size; +} dcerpc_auth_info; + /* try to desegment big DCE/RPC packets over TCP? */ static gboolean dcerpc_cn_desegment = TRUE; @@ -1315,7 +1323,7 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree, proto_tree *dcerpc_tree, tvbuff_t *tvb, gint offset, char *drep, dcerpc_info *info, - int auth_level) + dcerpc_auth_info *auth_info) { dcerpc_uuid_key key; dcerpc_uuid_value *sub_proto; @@ -1393,14 +1401,28 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree, /* * If the authentication level is DCE_C_AUTHN_LEVEL_PKT_PRIVACY, - * the stub data is encrypted, and we can't dissect it. + * the stub data is encrypted, and we'd have to decrypt it in + * order to dissect it. */ - if (auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) { + if (auth_info != NULL && + auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) { length = tvb_length_remaining (tvb, offset); if (length > 0) { proto_tree_add_text(sub_tree, tvb, offset, length, "Encrypted stub data (%d byte%s)", length, plurality(length, "", "s")); + + switch (auth_info->auth_type) { + + case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: { + /* NTLMSSP */ + tvbuff_t *ntlmssp_tvb; + ntlmssp_tvb = tvb_new_subset(tvb, offset, length, length); + + call_dissector(ntlmssp_enc_payload_handle, ntlmssp_tvb, pinfo, + sub_tree); + break; + } } } else { sub_dissect = info->request ? proc->dissect_rqst : proc->dissect_resp; @@ -1429,21 +1451,62 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree, } static int +dissect_dcerpc_verifier (tvbuff_t *tvb, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr, + dcerpc_auth_info *auth_info) +{ + int auth_offset; + + if (auth_info->auth_size != 0) { + auth_offset = hdr->frag_len - hdr->auth_len; + switch (auth_info->auth_type) { + + case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: { + /* NTLMSSP */ + tvbuff_t *ntlmssp_tvb; + + ntlmssp_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len, + hdr->auth_len); + + call_dissector(ntlmssp_verf_handle, ntlmssp_tvb, pinfo, + dcerpc_tree); + + break; + } + + case DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO: { + /* SPNEGO (rfc2478) */ + tvbuff_t *gssapi_tvb; + + gssapi_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len, + hdr->auth_len); + + call_dissector(gssapi_verf_handle, gssapi_tvb, pinfo, dcerpc_tree); + + break; + } + + default: + proto_tree_add_text (dcerpc_tree, tvb, auth_offset, hdr->auth_len, + "Auth Verifier"); + } + } + + return hdr->auth_len; +} + +static void dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tree, - e_dce_cn_common_hdr_t *hdr, int *auth_level_p, - gboolean are_credentials) + e_dce_cn_common_hdr_t *hdr, gboolean are_credentials, + dcerpc_auth_info *auth_info) { int offset; - guint8 auth_pad_len; - guint8 auth_level; - guint8 auth_type; /* - * Initially set "*auth_level_p" to -1 to indicate that we haven't + * Initially set auth_level to -1 to indicate that we haven't * yet seen any authentication level information. */ - if (auth_level_p != NULL) - *auth_level_p = -1; + auth_info->auth_level = -1; /* * The authentication information is at the *end* of the PDU; in @@ -1460,13 +1523,15 @@ dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr offset = hdr->frag_len - (hdr->auth_len + 8); offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, - hf_dcerpc_auth_type, &auth_type); + hf_dcerpc_auth_type, + &auth_info->auth_type); offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, - hf_dcerpc_auth_level, &auth_level); - if (auth_level_p != NULL) - *auth_level_p = auth_level; + hf_dcerpc_auth_level, + &auth_info->auth_level); + offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, - hf_dcerpc_auth_pad_len, &auth_pad_len); + hf_dcerpc_auth_pad_len, + &auth_info->auth_pad_len); offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, hf_dcerpc_auth_rsrvd, NULL); offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, @@ -1479,7 +1544,7 @@ dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr /* * The authentication data are credentials. */ - switch (auth_type) { + switch (auth_info->auth_type) { case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: { /* NTLMSSP */ @@ -1510,54 +1575,30 @@ dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr proto_tree_add_text (dcerpc_tree, tvb, offset, hdr->auth_len, "Auth Credentials"); } - } else { - /* - * The authentication data are a verifier. - */ - switch (auth_type) { - - case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: { - /* NTLMSSP */ - tvbuff_t *ntlmssp_tvb; - - ntlmssp_tvb = tvb_new_subset(tvb, offset, hdr->auth_len, - hdr->auth_len); - - call_dissector(ntlmssp_verf_handle, ntlmssp_tvb, pinfo, - dcerpc_tree); - - break; - } - - case DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO: { - /* SPNEGO (rfc2478) */ - tvbuff_t *gssapi_tvb; - - gssapi_tvb = tvb_new_subset(tvb, offset, hdr->auth_len, - hdr->auth_len); - - call_dissector(gssapi_verf_handle, gssapi_tvb, pinfo, dcerpc_tree); - - break; - } - - default: - proto_tree_add_text (dcerpc_tree, tvb, offset, hdr->auth_len, - "Auth Verifier"); - } } + /* + * XXX - sometimes the padding is a multiple of 4 and greater + * than 3, meaning it's not padding to put the authentication + * data on a 4-byte boundary. + * + * For now, we take its value mod 4. + * + * XXX - what is going on there? + */ + auth_info->auth_pad_len %= 4; + /* figure out where the auth padding starts */ - offset = hdr->frag_len - (hdr->auth_len + 8 + auth_pad_len); - if (offset > 0 && auth_pad_len) { + offset = hdr->frag_len - (hdr->auth_len + 8 + auth_info->auth_pad_len); + if (offset > 0 && auth_info->auth_pad_len) { proto_tree_add_text (dcerpc_tree, tvb, offset, - auth_pad_len, "Auth padding"); - return hdr->auth_len + 8 + auth_pad_len; + auth_info->auth_pad_len, "Auth padding"); + auth_info->auth_size = hdr->auth_len + 8 + auth_info->auth_pad_len; } else { - return hdr->auth_len + 8; + auth_info->auth_size = hdr->auth_len + 8; } } else { - return 0; + auth_info->auth_size = 0; } } @@ -1604,6 +1645,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo, guint16 if_ver, if_ver_minor; char uuid_str[DCERPC_UUID_STR_LEN]; int uuid_str_len; + dcerpc_auth_info auth_info; offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, hf_dcerpc_cn_max_xmit, NULL); @@ -1740,7 +1782,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo, * an authentication header, and associate it with an authentication * context, so subsequent PDUs can use that context. */ - dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, NULL, TRUE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, TRUE, &auth_info); } static void @@ -1757,6 +1799,7 @@ dissect_dcerpc_cn_bind_ack (tvbuff_t *tvb, gint offset, packet_info *pinfo, guint32 trans_ver; char uuid_str[DCERPC_UUID_STR_LEN]; int uuid_str_len; + dcerpc_auth_info auth_info; offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, hf_dcerpc_cn_max_xmit, &max_xmit); @@ -1826,7 +1869,7 @@ dissect_dcerpc_cn_bind_ack (tvbuff_t *tvb, gint offset, packet_info *pinfo, * XXX - do we need to do anything with the authentication level * we get back from this? */ - dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, NULL, TRUE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, TRUE, &auth_info); if (check_col (pinfo->cinfo, COL_INFO)) { if (num_results != 0 && result == 0) { @@ -1882,7 +1925,7 @@ static void dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *dcerpc_tree, proto_tree *tree, e_dce_cn_common_hdr_t *hdr, dcerpc_info *di, - int auth_sz, int auth_level, guint32 alloc_hint, + dcerpc_auth_info *auth_info, guint32 alloc_hint, guint32 frame) { int length, reported_length, stub_length; @@ -1890,7 +1933,7 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo, length = tvb_length_remaining(tvb, offset); reported_length = tvb_reported_length_remaining(tvb, offset); - stub_length = hdr->frag_len - offset - auth_sz; + stub_length = hdr->frag_len - offset - auth_info->auth_size; if (length > stub_length) length = stub_length; if (reported_length > stub_length) @@ -1911,7 +1954,7 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo, dcerpc_try_handoff (pinfo, tree, dcerpc_tree, tvb_new_subset (tvb, offset, length, reported_length), - 0, hdr->drep, di, auth_level); + 0, hdr->drep, di, auth_info); } else { /* PDU is fragmented and this isn't the first fragment */ if (check_col(pinfo->cinfo, COL_INFO)) { @@ -1978,7 +2021,7 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo, pinfo->fragmented = FALSE; dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb, - 0, hdr->drep, di, auth_level); + 0, hdr->drep, di, auth_info); } else { /* Reassembly not complete - some fragments are missing */ @@ -2016,8 +2059,7 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo, guint16 ctx_id; guint16 opnum; e_uuid_t obj_id; - int auth_sz = 0; - int auth_level; + dcerpc_auth_info auth_info; guint32 alloc_hint; int length; char uuid_str[DCERPC_UUID_STR_LEN]; @@ -2064,13 +2106,17 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo, * XXX - what if this was set when the connection was set up, * and we just have a security context? */ - auth_sz = dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, - &auth_level, FALSE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info); conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); if (!conv) { - + length = tvb_length_remaining (tvb, offset); + if (length > 0) { + proto_tree_add_text (dcerpc_tree, tvb, offset, length, + "Stub data (%d byte%s)", length, + plurality(length, "", "s")); + } } else { dcerpc_call_value *value; @@ -2139,7 +2185,7 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo, } dissect_dcerpc_cn_stub (tvb, offset, pinfo, dcerpc_tree, tree, - hdr, &di, auth_sz, auth_level, alloc_hint, + hdr, &di, &auth_info, alloc_hint, value->req_frame); } else { length = tvb_length_remaining (tvb, offset); @@ -2150,6 +2196,9 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo, } } } + + /* Decrypt the verifier, if present */ + dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info); } static void @@ -2159,8 +2208,7 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo, dcerpc_call_value *value = NULL; conversation_t *conv; guint16 ctx_id; - int auth_sz = 0; - int auth_level; + dcerpc_auth_info auth_info; guint32 alloc_hint; int length; @@ -2183,13 +2231,18 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo, * XXX - what if this was set when the connection was set up, * and we just have a security context? */ - auth_sz = dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, - &auth_level, FALSE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info); conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); if (!conv) { /* no point in creating one here, really */ + length = tvb_length_remaining (tvb, offset); + if (length > 0) { + proto_tree_add_text (dcerpc_tree, tvb, offset, length, + "Stub data (%d byte%s)", length, + plurality(length, "", "s")); + } } else { /* !!! we can NOT check flags.visited here since this will interact @@ -2241,7 +2294,7 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo, } dissect_dcerpc_cn_stub (tvb, offset, pinfo, dcerpc_tree, tree, - hdr, &di, auth_sz, auth_level, alloc_hint, + hdr, &di, &auth_info, alloc_hint, value->rep_frame); } else { length = tvb_length_remaining (tvb, offset); @@ -2252,6 +2305,9 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo, } } } + + /* Decrypt the verifier, if present */ + dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info); } static void @@ -2262,9 +2318,8 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo, conversation_t *conv; guint16 ctx_id; guint32 status; - int auth_sz = 0; - int auth_level; guint32 alloc_hint; + dcerpc_auth_info auth_info; offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep, hf_dcerpc_cn_alloc_hint, &alloc_hint); @@ -2294,8 +2349,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo, * XXX - what if this was set when the connection was set up, * and we just have a security context? */ - auth_sz = dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, - &auth_level, FALSE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info); conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); @@ -2354,7 +2408,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo, length = tvb_length_remaining(tvb, offset); reported_length = tvb_reported_length_remaining(tvb, offset); - stub_length = hdr->frag_len - offset - auth_sz; + stub_length = hdr->frag_len - offset - auth_info.auth_size; if (length > stub_length) length = stub_length; if (reported_length > stub_length) @@ -2513,6 +2567,7 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *cn_flags_tree = NULL; proto_tree *drep_tree = NULL; e_dce_cn_common_hdr_t hdr; + dcerpc_auth_info auth_info; /* * when done over nbt, dcerpc requests are padded with 4 bytes of null @@ -2634,7 +2689,8 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo, /* * Nothing after the common header other than credentials. */ - dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, NULL, TRUE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, TRUE, + &auth_info); break; case PDU_REQ: @@ -2659,7 +2715,8 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo, * Nothing after the common header other than an authentication * verifier. */ - dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, NULL, FALSE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, FALSE, + &auth_info); break; case PDU_SHUTDOWN: @@ -2671,7 +2728,8 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo, default: /* might as well dissect the auth info */ - dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, NULL, FALSE); + dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, FALSE, + &auth_info); break; } return hdr.frag_len + padding; @@ -2951,13 +3009,13 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo, /* First fragment, possibly the only fragment */ /* - * XXX - authentication level? + * XXX - authentication info? */ pinfo->fragmented = (hdr->flags1 & PFCL1_FRAG); dcerpc_try_handoff (pinfo, tree, dcerpc_tree, tvb_new_subset (tvb, offset, length, reported_length), - 0, hdr->drep, di, 0); + 0, hdr->drep, di, NULL); } else { /* PDU is fragmented and this isn't the first fragment */ if (check_col(pinfo->cinfo, COL_INFO)) { @@ -2998,11 +3056,11 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo, dcerpc_tree, pinfo, next_tvb); /* - * XXX - authentication level? + * XXX - authentication info? */ pinfo->fragmented = FALSE; dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb, - 0, hdr->drep, di, 0); + 0, hdr->drep, di, NULL); } else { /* Reassembly isn't completed yet */ if (check_col(pinfo->cinfo, COL_INFO)) { @@ -3809,6 +3867,7 @@ proto_reg_handoff_dcerpc (void) heur_dissector_add ("smb_transact", dissect_dcerpc_cn_bs, proto_dcerpc); ntlmssp_handle = find_dissector("ntlmssp"); ntlmssp_verf_handle = find_dissector("ntlmssp_verf"); + ntlmssp_enc_payload_handle = find_dissector("ntlmssp_encrypted_payload"); gssapi_handle = find_dissector("gssapi"); gssapi_verf_handle = find_dissector("gssapi_verf"); } diff --git a/packet-ntlmssp.c b/packet-ntlmssp.c index c04d7632f6..875122df4a 100644 --- a/packet-ntlmssp.c +++ b/packet-ntlmssp.c @@ -2,7 +2,7 @@ * Routines for NTLM Secure Service Provider * Devin Heitmueller <dheitmueller@netilla.com> * - * $Id: packet-ntlmssp.c,v 1.32 2002/11/28 06:48:42 guy Exp $ + * $Id: packet-ntlmssp.c,v 1.33 2002/12/31 08:05:29 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -34,6 +34,10 @@ #include "asn1.h" /* XXX - needed for subid_t */ #include "packet-gssapi.h" #include "packet-frame.h" +#include "prefs.h" +#include "crypt-rc4.h" +#include "crypt-md4.h" +#include "crypt-des.h" /* Message types */ @@ -156,8 +160,13 @@ static int hf_ntlmssp_address_list_server_nb = -1; static int hf_ntlmssp_address_list_domain_nb = -1; static int hf_ntlmssp_address_list_server_dns = -1; static int hf_ntlmssp_address_list_domain_dns = -1; +static int hf_ntlmssp_verf = -1; static int hf_ntlmssp_verf_vers = -1; static int hf_ntlmssp_verf_body = -1; +static int hf_ntlmssp_verf_unknown1 = -1; +static int hf_ntlmssp_verf_crc32 = -1; +static int hf_ntlmssp_verf_sequence = -1; +static int hf_ntlmssp_decrypted_payload = -1; static gint ett_ntlmssp = -1; static gint ett_ntlmssp_negotiate_flags = -1; @@ -165,8 +174,128 @@ static gint ett_ntlmssp_string = -1; static gint ett_ntlmssp_blob = -1; static gint ett_ntlmssp_address_list = -1; -static GMemChunk *flag_info_chunk; -static int flag_info_chunk_count = 100; +/* Configuration variables */ +static char *nt_password = NULL; + +#define MAX_BLOB_SIZE 256 +typedef struct _ntlmssp_blob { + guint16 length; + guint8 contents[MAX_BLOB_SIZE]; +} ntlmssp_blob; + +/* Used in the conversation function */ +typedef struct _ntlmssp_info { + guint32 flags; + guint8 challenge[8]; + rc4_state_struct rc4_state_peer1; + rc4_state_struct rc4_state_peer2; + guint32 peer1_dest_port; + int rc4_state_initialized; + ntlmssp_blob ntlm_response; + ntlmssp_blob lm_response; +} ntlmssp_info; + +/* If this struct exists in the payload_decrypt, then we have already + decrypted it once */ +typedef struct _ntlmssp_packet_info { + guint32 flags; + guint8 challenge[8]; + guint8 decrypted_payload[1500]; /* 1500 is an arbitrary size */ + guint8 verifier[16]; + gboolean payload_decrypted; + gboolean verifier_decrypted; +} ntlmssp_packet_info; + +/* + Generate a challenge response, given an eight byte challenge and + either the NT or the Lan Manager password hash (16 bytes). + Returns output in response, which is expected to be 24 bytes. +*/ +static int ntlmssp_generate_challenge_response(guint8 *response, + const guint8 *passhash, + const guint8 *challenge) +{ + guint8 pw21[21]; /* Password hash padded to 21 bytes */ + + memset(pw21, 0x0, sizeof(pw21)); + memcpy(pw21, passhash, 16); + + memset(response, 0, 24); + + crypt_des_ecb(response, challenge, pw21, 1); + crypt_des_ecb(response + 8, challenge, pw21 + 7, 1); + crypt_des_ecb(response + 16, challenge, pw21 + 14, 1); + + return 1; +} + +/* Create an NTLMSSP version 1 key. + * password points to the ANSI password to encrypt, challenge points to + * the 8 octet challenge string, key128 will do a 128 bit key if set to 1, + * otherwise it will do a 40 bit key. The result is stored in + * sspkey (expected to be 16 octets) + */ +static void +create_ntlmssp_v1_key(const char *nt_password, const guint8 *challenge, + int use_key_128, guint8 *sspkey) +{ + unsigned char lm_password_upper[16]; + unsigned char lm_password_hash[16]; + guint8 lm_challenge_response[24]; + guint8 rc4key[24]; + guint8 pw21[21]; /* Password hash padded to 21 bytes */ + size_t password_len; + unsigned int i; + unsigned char lmhash_key[] = + {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + + memset(lm_password_upper, 0, sizeof(lm_password_upper)); + + /* Create a Lan Manager hash of the input password */ + if (nt_password) { + password_len = strlen(nt_password); + /* Truncate password if too long */ + if (password_len > 16) + password_len = 16; + for (i = 0; i < password_len; i++) { + lm_password_upper[i] = toupper(nt_password[i]); + } + } + + crypt_des_ecb(lm_password_hash, lmhash_key, lm_password_upper, 1); + crypt_des_ecb(lm_password_hash+8, lmhash_key, lm_password_upper+7, 1); + + /* Generate the LanMan Challenge Response */ + ntlmssp_generate_challenge_response(lm_challenge_response, + lm_password_hash, challenge); + + /* Generate the NTLMSSP-v1 RC4 Key. + * The RC4 key is derived from the Lan Manager Hash. + * See lkcl "DCE/RPC over SMB" page 254 for the algorithm. + */ + memset(pw21, 0xBD, sizeof(pw21)); + memcpy(pw21, lm_password_hash, sizeof(lm_password_hash)); + + /* Only the first eight bytes of challenge_reponse is used */ + crypt_des_ecb(rc4key, lm_challenge_response, pw21, 1); + crypt_des_ecb(rc4key + 8, lm_challenge_response, pw21 + 7, 1); + crypt_des_ecb(rc4key + 16, lm_challenge_response, pw21 + 14, 1); + + /* Create the SSP Key */ + memset(sspkey, 0, sizeof(sspkey)); + if (use_key_128) { + /* Create 128 bit key */ + memcpy(sspkey, rc4key, 16); + } + else { + /* Create 40 bit key */ + memcpy(sspkey, rc4key, 5); + sspkey[5]=0xe5; + sspkey[6]=0x38; + sspkey[7]=0xb0; + } + return; +} /* dissect a string - header area contains: two byte len @@ -234,7 +363,7 @@ dissect_ntlmssp_string (tvbuff_t *tvb, int offset, static int dissect_ntlmssp_blob (tvbuff_t *tvb, int offset, proto_tree *ntlmssp_tree, - int blob_hf, int *end) + int blob_hf, int *end, ntlmssp_blob *result) { proto_item *tf = NULL; proto_tree *tree = NULL; @@ -266,6 +395,15 @@ dissect_ntlmssp_blob (tvbuff_t *tvb, int offset, offset += 4; *end = blob_offset + blob_length; + + if (result != NULL) { + result->length = blob_length; + memset(result->contents, 0, MAX_BLOB_SIZE); + if (blob_length < MAX_BLOB_SIZE) + tvb_memcpy(tvb, result->contents, blob_offset, blob_length); + } + + return offset; } @@ -504,34 +642,17 @@ dissect_ntlmssp_challenge (tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 negotiate_flags; int item_start, item_end; int data_start, data_end; - guint32 *flag_info; + ntlmssp_info *conv_ntlmssp_info; conversation_t *conversation; gboolean unicode_strings = FALSE; + guint8 sspkey[16]; /* NTLMSSP cipher key */ + guint8 ssp_key_len; /* Either 8 or 16 (40 bit or 128) */ /* need to find unicode flag */ negotiate_flags = tvb_get_letohl (tvb, offset+8); if (negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE) unicode_strings = TRUE; - /* - * Store the flags with the conversation, as they're needed in order - * to dissect subsequent messages. - */ - conversation = find_conversation(&pinfo->src, &pinfo->dst, - pinfo->ptype, pinfo->srcport, - pinfo->destport, 0); - if (!conversation) { /* Create one */ - conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, - pinfo->srcport, pinfo->destport, 0); - } - - if (!conversation_get_proto_data(conversation, proto_ntlmssp)) { - flag_info = g_mem_chunk_alloc(flag_info_chunk); - *flag_info = negotiate_flags; - - conversation_add_proto_data(conversation, proto_ntlmssp, flag_info); - } - /* Domain name */ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree, unicode_strings, hf_ntlmssp_challenge_domain, @@ -547,6 +668,45 @@ dissect_ntlmssp_challenge (tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_ntlm_challenge, tvb, offset, 8, FALSE); + + /* + * Store the flags and the challenge with the conversation, as they're + * needed in order to dissect subsequent messages. + */ + conversation = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + if (!conversation) { /* Create one */ + conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + } + + if (!conversation_get_proto_data(conversation, proto_ntlmssp)) { + conv_ntlmssp_info = g_malloc(sizeof(ntlmssp_info)); + /* Insert the flags into the conversation */ + conv_ntlmssp_info->flags = negotiate_flags; + /* Insert the challenge into the conversation */ + tvb_memcpy(tvb, conv_ntlmssp_info->challenge, offset, 8); + + /* Between the challenge and the user provided password, we can build the + NTLMSSP key and initialize the cipher */ + if (conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_128) { + create_ntlmssp_v1_key(nt_password, conv_ntlmssp_info->challenge, + 1, sspkey); + ssp_key_len = 16; + } + else { + create_ntlmssp_v1_key(nt_password, conv_ntlmssp_info->challenge, + 0, sspkey); + ssp_key_len = 8; + } + crypt_rc4_init(&conv_ntlmssp_info->rc4_state_peer1, sspkey, ssp_key_len); + crypt_rc4_init(&conv_ntlmssp_info->rc4_state_peer2, sspkey, ssp_key_len); + conv_ntlmssp_info->peer1_dest_port = pinfo->destport; + conv_ntlmssp_info->rc4_state_initialized = 1; + + conversation_add_proto_data(conversation, proto_ntlmssp, conv_ntlmssp_info); + } offset += 8; /* Reserved (function not completely known) */ @@ -581,7 +741,7 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset, int data_start, data_end = 0; guint32 negotiate_flags; gboolean unicode_strings = FALSE; - guint32 *flag_info; + ntlmssp_info *conv_ntlmssp_info; conversation_t *conversation; /* @@ -590,8 +750,8 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset, * the AUTHENTICATE message, so we can't figure out whether * strings are Unicode or not by looking at *our* flags. */ - flag_info = p_get_proto_data(pinfo->fd, proto_ntlmssp); - if (flag_info == NULL) { + conv_ntlmssp_info = p_get_proto_data(pinfo->fd, proto_ntlmssp); + if (conv_ntlmssp_info == NULL) { /* * There isn't any. Is there any from this conversation? If so, * it means this is the first time we've dissected this frame, so @@ -601,17 +761,17 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); if (conversation != NULL) { - flag_info = conversation_get_proto_data(conversation, proto_ntlmssp); - if (flag_info != NULL) { + conv_ntlmssp_info = conversation_get_proto_data(conversation, proto_ntlmssp); + if (conv_ntlmssp_info != NULL) { /* * We have flag info; attach it to the frame. */ - p_add_proto_data(pinfo->fd, proto_ntlmssp, flag_info); + p_add_proto_data(pinfo->fd, proto_ntlmssp, conv_ntlmssp_info); } } } - if (flag_info != NULL) { - if (*flag_info & NTLMSSP_NEGOTIATE_UNICODE) + if (conv_ntlmssp_info != NULL) { + if (conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_UNICODE) unicode_strings = TRUE; } @@ -635,14 +795,18 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset, data_start = tvb_get_letohl(tvb, offset+4); offset = dissect_ntlmssp_blob(tvb, offset, ntlmssp_tree, hf_ntlmssp_auth_lmresponse, - &item_end); + &item_end, + conv_ntlmssp_info == NULL ? NULL : + &conv_ntlmssp_info->lm_response); data_end = MAX(data_end, item_end); /* NTLM response */ item_start = tvb_get_letohl(tvb, offset+4); offset = dissect_ntlmssp_blob(tvb, offset, ntlmssp_tree, hf_ntlmssp_auth_ntresponse, - &item_end); + &item_end, + conv_ntlmssp_info == NULL ? NULL : + &conv_ntlmssp_info->ntlm_response); data_start = MIN(data_start, item_start); data_end = MAX(data_end, item_end); @@ -677,7 +841,7 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset, /* Session Key */ offset = dissect_ntlmssp_blob(tvb, offset, ntlmssp_tree, hf_ntlmssp_auth_sesskey, - &item_end); + &item_end, NULL); data_end = MAX(data_end, item_end); } @@ -768,19 +932,181 @@ dissect_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) } /* + * Get the encryption state tied to this conversation. cryptpeer indicates + * whether to retrieve the data for peer1 or peer2. + */ +static rc4_state_struct * +get_encrypted_state(packet_info *pinfo, int cryptpeer) +{ + conversation_t *conversation; + ntlmssp_info *conv_ntlmssp_info; + + conversation = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + if (conversation == NULL) { + /* We don't have a conversation. In this case, stop processing + because we do not have enough info to decrypt the payload */ + return NULL; + } + else { + /* We have a conversation, check for encryption state */ + conv_ntlmssp_info = conversation_get_proto_data(conversation, + proto_ntlmssp); + if (conv_ntlmssp_info == NULL) { + /* No encryption state tied to the conversation. Therefore, we + cannot decrypt the payload */ + return NULL; + } + else { + /* We have the encryption state in the conversation. So return the + crypt state tied to the requested peer + */ + if (cryptpeer == 1) { + return &conv_ntlmssp_info->rc4_state_peer1; + } else { + return &conv_ntlmssp_info->rc4_state_peer2; + } + } + } + return NULL; +} + +/* * See page 45 of "DCE/RPC over SMB" by Luke Kenneth Casson Leighton. */ +static void +decrypt_verifier(tvbuff_t *tvb, int offset, guint32 encrypted_block_length, + packet_info *pinfo, proto_tree *tree) +{ + proto_tree *decr_tree = NULL; + proto_item *tf = NULL; + conversation_t *conversation; + rc4_state_struct *rc4_state; + rc4_state_struct *rc4_state_peer; + tvbuff_t *decr_tvb; /* Used to display decrypted buffer */ + guint8 *peer_block; + ntlmssp_info *conv_ntlmssp_info = NULL; + ntlmssp_packet_info *packet_ntlmssp_info = NULL; + int decrypted_offset = 0; + + packet_ntlmssp_info = p_get_proto_data(pinfo->fd, proto_ntlmssp); + if (packet_ntlmssp_info == NULL) { + /* We don't have data for this packet */ + return; + } + if (!packet_ntlmssp_info->verifier_decrypted) { + conversation = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + if (conversation == NULL) { + /* There is no conversation, thus no encryption state */ + return; + } + + conv_ntlmssp_info = conversation_get_proto_data(conversation, + proto_ntlmssp); + if (conv_ntlmssp_info == NULL) { + /* There is no NTLMSSP state tied to the conversation */ + return; + } + if (conv_ntlmssp_info->rc4_state_initialized != 1 ) { + /* The crypto sybsystem is not initialized. This means that either + the conversation did not include a challenge, or we are doing + something other than NTLMSSP v1 */ + return; + } + + if (conv_ntlmssp_info->peer1_dest_port == pinfo->destport) { + rc4_state = get_encrypted_state(pinfo, 1); + rc4_state_peer = get_encrypted_state(pinfo, 0); + } else { + rc4_state = get_encrypted_state(pinfo, 0); + rc4_state_peer = get_encrypted_state(pinfo, 1); + } + + if (rc4_state == NULL || rc4_state_peer == NULL) { + /* There is no encryption state, so we cannot decrypt */ + return; + } + + /* Setup the buffer to decrypt to */ + tvb_memcpy(tvb, packet_ntlmssp_info->verifier, + offset, encrypted_block_length); + + /* Do the actual decryption of the verifier */ + crypt_rc4(rc4_state, packet_ntlmssp_info->verifier, + encrypted_block_length); + + /* We setup a temporary buffer so we can re-encrypt the payload after + decryption. This is to update the opposite peer's RC4 state */ + peer_block = g_malloc(encrypted_block_length); + memcpy(peer_block, packet_ntlmssp_info->verifier, + encrypted_block_length); + crypt_rc4(rc4_state_peer, peer_block, encrypted_block_length); + g_free(peer_block); + + /* Mark the packet as decrypted so that subsequent attempts to dissect + the packet use the already decrypted payload instead of attempting + to decrypt again */ + packet_ntlmssp_info->verifier_decrypted = TRUE; + } + + /* Show the decrypted buffer in a new window */ + decr_tvb = tvb_new_real_data(packet_ntlmssp_info->verifier, + encrypted_block_length, + encrypted_block_length); + tvb_set_child_real_data_tvbuff(tvb, decr_tvb); + add_new_data_source(pinfo, decr_tvb, + "Decrypted NTLMSSP Verifier"); + + /* Show the decrypted payload in the tree */ + tf = proto_tree_add_text(tree, decr_tvb, 0, -1, + "Decrypted Verifier (%d byte%s)", + encrypted_block_length, + plurality(encrypted_block_length, "", "s")); + decr_tree = proto_item_add_subtree (tf, ett_ntlmssp); + + /* LKCL page 45 says this is a "reserved" field. I'm not sure if it's + garbage because it's some sort of nonce, or because there is a problem + with the verifier decryption routine. */ + proto_tree_add_item (decr_tree, hf_ntlmssp_verf_unknown1, + decr_tvb, decrypted_offset, 4, TRUE); + decrypted_offset += 4; + + /* CRC32 of the DCE fragment data */ + proto_tree_add_item (decr_tree, hf_ntlmssp_verf_crc32, + decr_tvb, decrypted_offset, 4, TRUE); + decrypted_offset += 4; + + /* Incrementing sequence number of DCE conversation */ + proto_tree_add_item (decr_tree, hf_ntlmssp_verf_sequence, + decr_tvb, decrypted_offset, 4, TRUE); + decrypted_offset += 4; +} + static int dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { volatile int offset = 0; proto_tree *volatile ntlmssp_tree = NULL; proto_item *tf = NULL; + guint32 verifier_length; + guint32 encrypted_block_length; + + verifier_length = tvb_length_remaining (tvb, offset); + encrypted_block_length = verifier_length - 4; + + if (encrypted_block_length < 12) { + /* Don't know why this would happen, but if it does, don't even bother + attempting decryption/dissection */ + return offset + verifier_length; + } /* Setup a new tree for the NTLMSSP payload */ if (tree) { tf = proto_tree_add_item (tree, - hf_ntlmssp, + hf_ntlmssp_verf, tvb, offset, -1, FALSE); ntlmssp_tree = proto_item_add_subtree (tf, @@ -804,11 +1130,15 @@ dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_vers, tvb, offset, 4, TRUE); offset += 4; - + /* Encrypted body */ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_body, - tvb, offset, 12, TRUE); - offset += 12; + tvb, offset, encrypted_block_length, TRUE); + + /* Try to decrypt */ + decrypt_verifier (tvb, offset, encrypted_block_length, pinfo, ntlmssp_tree); + + offset += encrypted_block_length; } CATCH(BoundsError) { RETHROW; } CATCH(ReportedBoundsError) { @@ -818,14 +1148,108 @@ dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) return offset; } +static int +dissect_ntlmssp_encrypted_payload(tvbuff_t *tvb, + packet_info *pinfo, proto_tree *tree) +{ + volatile int offset = 0; + tvbuff_t *decr_tvb; /* Used to display decrypted buffer */ + guint8 *peer_block; + conversation_t *conversation; + guint32 encrypted_block_length; + rc4_state_struct *rc4_state; + rc4_state_struct *rc4_state_peer; + ntlmssp_info *conv_ntlmssp_info = NULL; + ntlmssp_packet_info *packet_ntlmssp_info = NULL; + + encrypted_block_length = tvb_length_remaining (tvb, offset); + + /* Check to see if we already have state for this packet */ + packet_ntlmssp_info = p_get_proto_data(pinfo->fd, proto_ntlmssp); + if (packet_ntlmssp_info == NULL) { + /* We don't have any packet state, so create one */ + packet_ntlmssp_info = g_malloc(sizeof(ntlmssp_packet_info)); + memset(packet_ntlmssp_info, 0, sizeof(ntlmssp_packet_info)); + p_add_proto_data(pinfo->fd, proto_ntlmssp, packet_ntlmssp_info); + } + + if (!packet_ntlmssp_info->payload_decrypted) { + /* Pull the challenge info from the conversation */ + conversation = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + if (conversation == NULL) { + /* There is no conversation, thus no encryption state */ + return offset + encrypted_block_length; + } + + conv_ntlmssp_info = conversation_get_proto_data(conversation, + proto_ntlmssp); + if (conv_ntlmssp_info == NULL) { + /* There is no NTLMSSP state tied to the conversation */ + return offset + encrypted_block_length; + } + + /* Get the pair of RC4 state structures. One is used for to decrypt the + payload. The other is used to re-encrypt the payload to represent + the peer */ + if (conv_ntlmssp_info->peer1_dest_port == pinfo->destport) { + rc4_state = get_encrypted_state(pinfo, 1); + rc4_state_peer = get_encrypted_state(pinfo, 0); + } else { + rc4_state = get_encrypted_state(pinfo, 0); + rc4_state_peer = get_encrypted_state(pinfo, 1); + } + + if (rc4_state == NULL || rc4_state_peer == NULL) { + /* There is no encryption state, so we cannot decrypt */ + return offset + encrypted_block_length; + } + + /* Store the decrypted contents in the packet state struct + (of course at this point, they aren't decrypted yet) */ + tvb_memcpy(tvb, packet_ntlmssp_info->decrypted_payload, + offset, encrypted_block_length); + + /* Do the decryption of the payload */ + crypt_rc4(rc4_state, packet_ntlmssp_info->decrypted_payload, + encrypted_block_length); + + /* We setup a temporary buffer so we can re-encrypt the payload after + decryption. This is to update the opposite peer's RC4 state */ + peer_block = g_malloc(encrypted_block_length); + memcpy(peer_block, packet_ntlmssp_info->decrypted_payload, + encrypted_block_length); + crypt_rc4(rc4_state_peer, peer_block, encrypted_block_length); + g_free(peer_block); + + packet_ntlmssp_info->payload_decrypted = TRUE; + } + + /* Show the decrypted buffer in a new window */ + decr_tvb = tvb_new_real_data(packet_ntlmssp_info->decrypted_payload, + encrypted_block_length, + encrypted_block_length); + tvb_set_child_real_data_tvbuff(tvb, decr_tvb); + add_new_data_source(pinfo, decr_tvb, + "Decrypted NTLMSSP block"); + + /* Show the decrypted payload in the tree */ + if (tree) { + proto_tree_add_text(tree, decr_tvb, 0, -1, + "Decrypted stub data (%d byte%s)", + encrypted_block_length, + plurality(encrypted_block_length, "", "s")); + } + + offset += encrypted_block_length; + return offset; +} + static void ntlmssp_init_protocol(void) { - if (flag_info_chunk) - g_mem_chunk_destroy(flag_info_chunk); - flag_info_chunk = g_mem_chunk_new("flag_info", sizeof(guint32 *), - flag_info_chunk_count * sizeof(guint32 *), - G_ALLOC_ONLY); + } void @@ -972,10 +1396,20 @@ proto_register_ntlmssp(void) { &hf_ntlmssp_address_list_domain_dns, { "Domain DNS Name", "ntlmssp.challenge.addresslist.domaindns", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }}, + { &hf_ntlmssp_verf, + { "NTLMSSP Verifier", "ntlmssp.verf", FT_NONE, BASE_NONE, NULL, 0x0, "NTLMSSP Verifier", HFILL }}, { &hf_ntlmssp_verf_vers, { "Version Number", "ntlmssp.verf.vers", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }}, { &hf_ntlmssp_verf_body, - { "Verifier Body", "ntlmssp.verf.body", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }} + { "Verifier Body", "ntlmssp.verf.body", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }}, + { &hf_ntlmssp_decrypted_payload, + { "NTLM Decrypted Payload", "ntlmssp.decrypted_payload", FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL }}, + { &hf_ntlmssp_verf_unknown1, + { "Unknown 1", "ntlmssp.verf.unknown1", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }}, + { &hf_ntlmssp_verf_crc32, + { "Verifier CRC32", "ntlmssp.verf.crc32", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }}, + { &hf_ntlmssp_verf_sequence, + { "Verifier Sequence Number", "ntlmssp.verf.sequence", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }} }; @@ -987,7 +1421,8 @@ proto_register_ntlmssp(void) &ett_ntlmssp_blob, &ett_ntlmssp_address_list }; - + module_t *ntlmssp_module; + proto_ntlmssp = proto_register_protocol ( "NTLM Secure Service Provider", /* name */ "NTLMSSP", /* short name */ @@ -997,8 +1432,17 @@ proto_register_ntlmssp(void) proto_register_subtree_array (ett, array_length (ett)); register_init_routine(&ntlmssp_init_protocol); + ntlmssp_module = prefs_register_protocol(proto_ntlmssp, NULL); + + prefs_register_string_preference(ntlmssp_module, "nt_password", + "NT Password", + "NT Password (used to decrypt payloads)", + &nt_password); + register_dissector("ntlmssp", dissect_ntlmssp, proto_ntlmssp); new_register_dissector("ntlmssp_verf", dissect_ntlmssp_verf, proto_ntlmssp); + new_register_dissector("ntlmssp_encrypted_payload", + dissect_ntlmssp_encrypted_payload, proto_ntlmssp); } void |