UNPKG

15.4 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013 Sam Decrock https://github.com/SamDecrock/
3 * All rights reserved.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9var crypto = require('crypto');
10
11var flags = {
12 NTLM_NegotiateUnicode : 0x00000001,
13 NTLM_NegotiateOEM : 0x00000002,
14 NTLM_RequestTarget : 0x00000004,
15 NTLM_Unknown9 : 0x00000008,
16 NTLM_NegotiateSign : 0x00000010,
17 NTLM_NegotiateSeal : 0x00000020,
18 NTLM_NegotiateDatagram : 0x00000040,
19 NTLM_NegotiateLanManagerKey : 0x00000080,
20 NTLM_Unknown8 : 0x00000100,
21 NTLM_NegotiateNTLM : 0x00000200,
22 NTLM_NegotiateNTOnly : 0x00000400,
23 NTLM_Anonymous : 0x00000800,
24 NTLM_NegotiateOemDomainSupplied : 0x00001000,
25 NTLM_NegotiateOemWorkstationSupplied : 0x00002000,
26 NTLM_Unknown6 : 0x00004000,
27 NTLM_NegotiateAlwaysSign : 0x00008000,
28 NTLM_TargetTypeDomain : 0x00010000,
29 NTLM_TargetTypeServer : 0x00020000,
30 NTLM_TargetTypeShare : 0x00040000,
31 NTLM_NegotiateExtendedSecurity : 0x00080000,
32 NTLM_NegotiateIdentify : 0x00100000,
33 NTLM_Unknown5 : 0x00200000,
34 NTLM_RequestNonNTSessionKey : 0x00400000,
35 NTLM_NegotiateTargetInfo : 0x00800000,
36 NTLM_Unknown4 : 0x01000000,
37 NTLM_NegotiateVersion : 0x02000000,
38 NTLM_Unknown3 : 0x04000000,
39 NTLM_Unknown2 : 0x08000000,
40 NTLM_Unknown1 : 0x10000000,
41 NTLM_Negotiate128 : 0x20000000,
42 NTLM_NegotiateKeyExchange : 0x40000000,
43 NTLM_Negotiate56 : 0x80000000
44};
45var typeflags = {
46 NTLM_TYPE1_FLAGS : flags.NTLM_NegotiateUnicode
47 + flags.NTLM_NegotiateOEM
48 + flags.NTLM_RequestTarget
49 + flags.NTLM_NegotiateNTLM
50 + flags.NTLM_NegotiateOemDomainSupplied
51 + flags.NTLM_NegotiateOemWorkstationSupplied
52 + flags.NTLM_NegotiateAlwaysSign
53 + flags.NTLM_NegotiateExtendedSecurity
54 + flags.NTLM_NegotiateVersion
55 + flags.NTLM_Negotiate128
56 + flags.NTLM_Negotiate56,
57
58 NTLM_TYPE2_FLAGS : flags.NTLM_NegotiateUnicode
59 + flags.NTLM_RequestTarget
60 + flags.NTLM_NegotiateNTLM
61 + flags.NTLM_NegotiateAlwaysSign
62 + flags.NTLM_NegotiateExtendedSecurity
63 + flags.NTLM_NegotiateTargetInfo
64 + flags.NTLM_NegotiateVersion
65 + flags.NTLM_Negotiate128
66 + flags.NTLM_Negotiate56
67};
68
69function createType1Message(options){
70 var domain = escape(options.domain.toUpperCase());
71 var workstation = escape(options.workstation.toUpperCase());
72 var protocol = 'NTLMSSP\0';
73
74 var BODY_LENGTH = 40;
75
76 var type1flags = typeflags.NTLM_TYPE1_FLAGS;
77 if(!domain || domain === '')
78 type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied;
79
80 var pos = 0;
81 var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length);
82
83
84 buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol
85 buf.writeUInt32LE(1, pos); pos += 4; // type 1
86 buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag
87
88 buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length
89 buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length
90 buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset
91
92 buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length
93 buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length
94 buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset
95
96 buf.writeUInt8(5, pos); pos += 1; //ProductMajorVersion
97 buf.writeUInt8(1, pos); pos += 1; //ProductMinorVersion
98 buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild
99
100 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1
101 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2
102 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3
103 buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent
104
105
106 // length checks is to fix issue #46 and possibly #57
107 if(workstation.length !=0) buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string
108 if(domain.length !=0) buf.write(domain , pos, domain.length , 'ascii'); pos += domain.length; // domain string
109
110 return 'NTLM ' + buf.toString('base64');
111}
112
113function parseType2Message(rawmsg, callback){
114 var match = rawmsg.match(/NTLM (.+)?/);
115 if(!match || !match[1]) {
116 callback(new Error("Couldn't find NTLM in the message type2 comming from the server"));
117 return null;
118 }
119
120 var buf = new Buffer(match[1], 'base64');
121
122 var msg = {};
123
124 msg.signature = buf.slice(0, 8);
125 msg.type = buf.readInt16LE(8);
126
127 if(msg.type != 2) {
128 callback(new Error("Server didn't return a type 2 message"));
129 return null;
130 }
131
132 msg.targetNameLen = buf.readInt16LE(12);
133 msg.targetNameMaxLen = buf.readInt16LE(14);
134 msg.targetNameOffset = buf.readInt32LE(16);
135 msg.targetName = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen);
136
137 msg.negotiateFlags = buf.readInt32LE(20);
138 msg.serverChallenge = buf.slice(24, 32);
139 msg.reserved = buf.slice(32, 40);
140
141 if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){
142 msg.targetInfoLen = buf.readInt16LE(40);
143 msg.targetInfoMaxLen = buf.readInt16LE(42);
144 msg.targetInfoOffset = buf.readInt32LE(44);
145 msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen);
146 }
147 return msg;
148}
149
150function createType3Message(msg2, options){
151 var nonce = msg2.serverChallenge;
152 var username = options.username;
153 var password = options.password;
154 var lm_password = options.lm_password;
155 var nt_password = options.nt_password;
156 var negotiateFlags = msg2.negotiateFlags;
157
158 var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode;
159 var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity;
160
161 var BODY_LENGTH = 72;
162
163 var domainName = escape(options.domain.toUpperCase());
164 var workstation = escape(options.workstation.toUpperCase());
165
166 var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes;
167
168 var encryptedRandomSessionKey = "";
169 if(isUnicode){
170 workstationBytes = new Buffer(workstation, 'utf16le');
171 domainNameBytes = new Buffer(domainName, 'utf16le');
172 usernameBytes = new Buffer(username, 'utf16le');
173 encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le');
174 }else{
175 workstationBytes = new Buffer(workstation, 'ascii');
176 domainNameBytes = new Buffer(domainName, 'ascii');
177 usernameBytes = new Buffer(username, 'ascii');
178 encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii');
179 }
180
181 var lmChallengeResponse = calc_resp((lm_password!=null)?lm_password:create_LM_hashed_password_v1(password), nonce);
182 var ntChallengeResponse = calc_resp((nt_password!=null)?nt_password:create_NT_hashed_password_v1(password), nonce);
183
184 if(isNegotiateExtendedSecurity){
185 var pwhash = (nt_password!=null)?nt_password:create_NT_hashed_password_v1(password);
186 var clientChallenge = "";
187 for(var i=0; i < 8; i++){
188 clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) );
189 }
190 var clientChallengeBytes = new Buffer(clientChallenge, 'ascii');
191 var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes);
192 lmChallengeResponse = challenges.lmChallengeResponse;
193 ntChallengeResponse = challenges.ntChallengeResponse;
194 }
195
196 var signature = 'NTLMSSP\0';
197
198 var pos = 0;
199 var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length);
200
201 buf.write(signature, pos, signature.length); pos += signature.length;
202 buf.writeUInt32LE(3, pos); pos += 4; // type 1
203
204 buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen
205 buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen
206 buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset
207
208 buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen
209 buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen
210 buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset
211
212 buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen
213 buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen
214 buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // DomainNameOffset
215
216 buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen
217 buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen
218 buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset
219
220 buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen
221 buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen
222 buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset
223
224 buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen
225 buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen
226 buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset
227
228 buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags
229
230 buf.writeUInt8(5, pos); pos++; // ProductMajorVersion
231 buf.writeUInt8(1, pos); pos++; // ProductMinorVersion
232 buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild
233 buf.writeUInt8(0, pos); pos++; // VersionReserved1
234 buf.writeUInt8(0, pos); pos++; // VersionReserved2
235 buf.writeUInt8(0, pos); pos++; // VersionReserved3
236 buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent
237
238 domainNameBytes.copy(buf, pos); pos += domainNameBytes.length;
239 usernameBytes.copy(buf, pos); pos += usernameBytes.length;
240 workstationBytes.copy(buf, pos); pos += workstationBytes.length;
241 lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length;
242 ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length;
243 encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length;
244
245 return 'NTLM ' + buf.toString('base64');
246}
247
248function create_LM_hashed_password_v1(password){
249 // fix the password length to 14 bytes
250 password = password.toUpperCase();
251 var passwordBytes = new Buffer(password, 'ascii');
252
253 var passwordBytesPadded = new Buffer(14);
254 passwordBytesPadded.fill("\0");
255 var sourceEnd = 14;
256 if(passwordBytes.length < 14) sourceEnd = passwordBytes.length;
257 passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd);
258
259 // split into 2 parts of 7 bytes:
260 var firstPart = passwordBytesPadded.slice(0,7);
261 var secondPart = passwordBytesPadded.slice(7);
262
263 function encrypt(buf){
264 var key = insertZerosEvery7Bits(buf);
265 var des = crypto.createCipheriv('DES-ECB', key, '');
266 return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]);
267 }
268
269 var firstPartEncrypted = encrypt(firstPart);
270 var secondPartEncrypted = encrypt(secondPart);
271
272 return Buffer.concat([firstPartEncrypted, secondPartEncrypted]);
273}
274
275function insertZerosEvery7Bits(buf){
276 var binaryArray = bytes2binaryArray(buf);
277 var newBinaryArray = [];
278 for(var i=0; i<binaryArray.length; i++){
279 newBinaryArray.push(binaryArray[i]);
280
281 if((i+1)%7 === 0){
282 newBinaryArray.push(0);
283 }
284 }
285 return binaryArray2bytes(newBinaryArray);
286}
287
288function bytes2binaryArray(buf){
289 var hex2binary = {
290 0: [0,0,0,0],
291 1: [0,0,0,1],
292 2: [0,0,1,0],
293 3: [0,0,1,1],
294 4: [0,1,0,0],
295 5: [0,1,0,1],
296 6: [0,1,1,0],
297 7: [0,1,1,1],
298 8: [1,0,0,0],
299 9: [1,0,0,1],
300 A: [1,0,1,0],
301 B: [1,0,1,1],
302 C: [1,1,0,0],
303 D: [1,1,0,1],
304 E: [1,1,1,0],
305 F: [1,1,1,1]
306 };
307
308 var hexString = buf.toString('hex').toUpperCase();
309 var array = [];
310 for(var i=0; i<hexString.length; i++){
311 var hexchar = hexString.charAt(i);
312 array = array.concat(hex2binary[hexchar]);
313 }
314 return array;
315}
316
317function binaryArray2bytes(array){
318 var binary2hex = {
319 '0000': 0,
320 '0001': 1,
321 '0010': 2,
322 '0011': 3,
323 '0100': 4,
324 '0101': 5,
325 '0110': 6,
326 '0111': 7,
327 '1000': 8,
328 '1001': 9,
329 '1010': 'A',
330 '1011': 'B',
331 '1100': 'C',
332 '1101': 'D',
333 '1110': 'E',
334 '1111': 'F'
335 };
336
337 var bufArray = [];
338
339 for(var i=0; i<array.length; i +=8 ){
340 if((i+7) > array.length)
341 break;
342
343 var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3];
344 var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7];
345 var hexchar1 = binary2hex[binString1];
346 var hexchar2 = binary2hex[binString2];
347
348 var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex');
349 bufArray.push(buf);
350 }
351
352 return Buffer.concat(bufArray);
353}
354
355function create_NT_hashed_password_v1(password){
356 var buf = new Buffer(password, 'utf16le');
357 var md4 = crypto.createHash('md4');
358 md4.update(buf);
359 return new Buffer(md4.digest());
360}
361
362function calc_resp(password_hash, server_challenge){
363 // padding with zeros to make the hash 21 bytes long
364 var passHashPadded = new Buffer(21);
365 passHashPadded.fill("\0");
366 password_hash.copy(passHashPadded, 0, 0, password_hash.length);
367
368 var resArray = [];
369
370 var des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), '');
371 resArray.push( des.update(server_challenge.slice(0,8)) );
372
373 des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), '');
374 resArray.push( des.update(server_challenge.slice(0,8)) );
375
376 des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), '');
377 resArray.push( des.update(server_challenge.slice(0,8)) );
378
379 return Buffer.concat(resArray);
380}
381
382function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){
383 // padding with zeros to make the hash 16 bytes longer
384 var lmChallengeResponse = new Buffer(clientChallenge.length + 16);
385 lmChallengeResponse.fill("\0");
386 clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length);
387
388 var buf = Buffer.concat([serverChallenge, clientChallenge]);
389 var md5 = crypto.createHash('md5');
390 md5.update(buf);
391 var sess = md5.digest();
392 var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8));
393
394 return {
395 lmChallengeResponse: lmChallengeResponse,
396 ntChallengeResponse: ntChallengeResponse
397 };
398}
399
400exports.createType1Message = createType1Message;
401exports.parseType2Message = parseType2Message;
402exports.createType3Message = createType3Message;
403exports.create_NT_hashed_password = create_NT_hashed_password_v1;
404exports.create_LM_hashed_password = create_LM_hashed_password_v1;
405
406
407
408