UNPKG

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