1const inherits = require('util').inherits;
2const type = require('../core').type;
3const events = require('events');
4const crypto = require('crypto');
5const forge = require('node-forge');
6const asn1 = forge.asn1;
10 * NLA layer of rdp stack
12function NLA(transport, nlaCompletedFunc, domain, user, password) {
14 const ntlm = Create_Ntlm();
16 ntlm.completedFunc = nlaCompletedFunc;
18 ntlm.password = password;
19 ntlm.response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain);
20 ntlm.response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain);
24 // Get transport ready
25 this.transport = transport;
27 this.transport.expect(2);
28 // Next state is receive header
31 this.oldDataListeners = this.transport.listeners('data');
32 this.oldCloseListeners = this.transport.listeners('close');
33 this.oldErrorListeners = this.transport.listeners('error');
35 // Unhook the previous transport handler
36 this.transport.removeAllListeners('data');
37 this.transport.removeAllListeners('close');
38 this.transport.removeAllListeners('error');
40 // Hook this module as the transport handler
41 this.transport.once('data', function (s) {
43 }).on('close', function () {
45 }).on('error', function (err) {
46 self.emit('close'); // Errors occur when NLA authentication fails, for now, just close.
47 //self.emit('error', err);
52 * inherit from a packet layer
54inherits(NLA, events.EventEmitter);
57 * Receive correct packet as expected
58 * @param s {type.Stream}
60NLA.prototype.recvHeader = function (s) {
61 //console.log('NLA - recvHeader', s);
63 var derType = new type.UInt8().read(s).value;
64 var derLen = new type.UInt8().read(s).value;
65 self.buffers = [ s.buffer ];
68 // wait for the entire data block
69 this.transport.expect(derLen);
70 this.transport.once('data', function (s) { self.recvData(s); });
72 // wait for the header size
73 this.transport.expect(derLen - 128);
74 this.transport.once('data', function (s) { self.recvHeaderSize(s); });
77 //console.log('NLA - DER', derType, derLen);
81 * Receive correct packet as expected
82 * @param s {type.Stream}
84NLA.prototype.recvHeaderSize = function (s) {
85 //console.log('NLA - recvHeaderSize', s.buffer.length);
87 self.buffers.push(s.buffer);
88 if (s.buffer.length == 1) {
89 // wait for the entire data block
90 var derLen = s.buffer.readUInt8(0);
91 this.transport.expect(derLen);
92 this.transport.once('data', function (s) { self.recvData(s); });
93 } else if (s.buffer.length == 2) {
94 // wait for the entire data block
95 var derLen = s.buffer.readUInt16BE(0);
96 this.transport.expect(derLen);
97 this.transport.once('data', function (s) { self.recvData(s); });
102 * Receive correct packet as expected
103 * @param s {type.Stream}
105NLA.prototype.recvData = function (s) {
106 //console.log('NLA - recvData', s.buffer.length);
108 self.buffers.push(s.buffer);
109 var entireBuffer = Buffer.concat(self.buffers);
110 //console.log('entireBuffer', entireBuffer.toString('hex'));
112 // We have a full ASN1 data block, decode it now
113 const der = asn1.fromDer(entireBuffer.toString('binary'));
114 const derNum = der.value[0].value[0].value.charCodeAt(0);
115 //console.log('NLA - Number', derNum);
118 if (this.state == 1) {
119 const derBuffer = Buffer.from(der.value[1].value[0].value[0].value[0].value[0].value, 'binary');
120 const client_challenge = read_challenge_message(this.ntlm, derBuffer);
121 self.security_interface = build_security_interface(this.ntlm);
122 const peer_cert = this.transport.secureSocket.getPeerCertificate();
123 const challenge = create_ts_authenticate(client_challenge, self.security_interface.gss_wrapex(peer_cert.pubkey.slice(24)));
124 this.ntlm.publicKeyDer = peer_cert.pubkey.slice(24);
125 this.send(challenge);
127 } else if (this.state == 2) {
128 const derBuffer = Buffer.from(der.value[1].value[0].value, 'binary');
129 const publicKeyDer = self.security_interface.gss_unwrapex(derBuffer);
131 // Check that the public key is identical except the first byte which is the DER encoding type.
132 if (!this.ntlm.publicKeyDer.slice(1).equals(publicKeyDer.slice(1))) { console.log('RDP man-in-the-middle detected.'); close(); return; }
133 delete this.ntlm.publicKeyDer; // Clean this up, we don't need it anymore.
135 var xdomain, xuser, xpassword;
136 if (this.ntlm.is_unicode) {
137 xdomain = toUnicode(this.ntlm.domain);
138 xuser = toUnicode(this.ntlm.user);
139 xpassword = toUnicode(this.ntlm.password);
141 xdomain = Buffer.from(this.ntlm.domain, 'utf8');
142 xuser = Buffer.from(this.ntlm.user, 'utf8');
143 xpassword = Buffer.from(this.ntlm.password, 'utf8');
146 const credentials = create_ts_authinfo(self.security_interface.gss_wrapex(create_ts_credentials(xdomain, xuser, xpassword)));
147 this.send(credentials);
149 // Rehook the previous transport handler
150 this.transport.removeAllListeners('data');
151 this.transport.removeAllListeners('close');
152 this.transport.removeAllListeners('error');
154 for (var i in this.oldDataListeners) { this.transport.once('data', this.oldDataListeners[i]); }
155 for (var i in this.oldCloseListeners) { this.transport.on('close', this.oldCloseListeners[i]); }
156 for (var i in this.oldErrorListeners) { this.transport.on('error', this.oldErrorListeners[i]); }
159 this.transport.expect(2);
161 this.ntlm.completedFunc();
166 // Receive next block of data
167 this.transport.expect(2);
168 this.transport.once('data', function (s) { self.recvHeader(s); });
173 * Send message throught NLA layer
174 * @param message {type.*}
176NLA.prototype.send = function (message) {
177 this.transport.sendBuffer(message);
183NLA.prototype.close = function() {
184 this.transport.close();
188NLA.prototype.sendNegotiateMessage = function () {
189 // Create create_ts_request
190 this.ntlm.negotiate_message = create_negotiate_message();
192 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
193 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
194 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
196 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
197 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
198 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
199 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
200 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, this.ntlm.negotiate_message.toString('binary'))
207 // Serialize an ASN.1 object to DER format
208 this.send(Buffer.from(asn1.toDer(asn1obj).data, 'binary'));
221const NegotiateFlags = {
222 NtlmsspNegociate56: 0x80000000,
223 NtlmsspNegociateKeyExch: 0x40000000,
224 NtlmsspNegociate128: 0x20000000,
225 NtlmsspNegociateVersion: 0x02000000,
226 NtlmsspNegociateTargetInfo: 0x00800000,
227 NtlmsspRequestNonNTSessionKey: 0x00400000,
228 NtlmsspNegociateIdentify: 0x00100000,
229 NtlmsspNegociateExtendedSessionSecurity: 0x00080000,
230 NtlmsspTargetTypeServer: 0x00020000,
231 NtlmsspTargetTypeDomain: 0x00010000,
232 NtlmsspNegociateAlwaysSign: 0x00008000,
233 NtlmsspNegociateOEMWorkstationSupplied: 0x00002000,
234 NtlmsspNegociateOEMDomainSupplied: 0x00001000,
235 NtlmsspNegociateNTLM: 0x00000200,
236 NtlmsspNegociateLMKey: 0x00000080,
237 NtlmsspNegociateDatagram: 0x00000040,
238 NtlmsspNegociateSeal: 0x00000020,
239 NtlmsspNegociateSign: 0x00000010,
240 NtlmsspRequestTarget: 0x00000004,
241 NtlmNegotiateOEM: 0x00000002,
242 NtlmsspNegociateUnicode: 0x00000001
245const MajorVersion = {
246 WindowsMajorVersion5: 0x05,
247 WindowsMajorVersion6: 0x06
250const MinorVersion = {
251 WindowsMinorVersion0: 0x00,
252 WindowsMinorVersion1: 0x01,
253 WindowsMinorVersion2: 0x02,
254 WindowsMinorVersion3: 0x03
257const NTLMRevision = {
258 NtlmSspRevisionW2K3: 0x0F
261function decodeTargetInfo(targetInfoBuf) {
262 var r = {}, type, len, data, ptr = 0;
264 type = targetInfoBuf.readInt16LE(ptr);
265 if (type == 0) break;
266 len = targetInfoBuf.readInt16LE(ptr + 2);
267 r[type] = targetInfoBuf.slice(ptr + 4, ptr + 4 + len);
273function bufToArr(b) { var r = []; for (var i = 0; i < b.length; i++) { r.push(b.readUInt8(i)); } return r; } // For unit testing
274function compareArray(a, b) { if (a.length != b.length) return false; for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return true; } // For unit testing
275function toUnicode(str) { return Buffer.from(str, 'ucs2'); }
276function md4(buffer) {
278 return crypto.createHash('md4').update(buffer).digest(); // Built in NodeJS MD4, this does not work starting with NodeJS v17
280 return Buffer.from(require('../security/md4').array(buffer.toString('binary'))); // This is the alternative if NodeJS does not support MD4
283function md5(str) { return crypto.createHash('md5').update(str).digest(); }
284function hmac_md5(key, data) { return crypto.createHmac('md5', key).update(data).digest(); }
285function ntowfv2(password, user, domain) { return hmac_md5(md4(toUnicode(password)), toUnicode(user.toUpperCase() + domain)); }
286function lmowfv2(password, user, domain) { return ntowfv2(password, user, domain); }
287function zeroBuffer(len) { return Buffer.alloc(len); }
288function compute_response_v2(response_key_nt, response_key_lm, server_challenge, client_challenge, time, server_name) {
289 const response_version = Buffer.from('01', 'hex');
290 const hi_response_version = Buffer.from('01', 'hex');
291 const temp = Buffer.concat([response_version, hi_response_version, zeroBuffer(6), time, client_challenge, zeroBuffer(4), server_name]);
292 const nt_proof_str = hmac_md5(response_key_nt, Buffer.concat([server_challenge, temp]));
293 const nt_challenge_response = Buffer.concat([nt_proof_str, temp]);
294 const lm_challenge_response = Buffer.concat([hmac_md5(response_key_lm, Buffer.concat([server_challenge, client_challenge])), client_challenge]);
295 const session_base_key = hmac_md5(response_key_nt, nt_proof_str);
296 return [nt_challenge_response, lm_challenge_response, session_base_key];
298function kx_key_v2(session_base_key, _lm_challenge_response, _server_challenge) { return session_base_key; }
299function rc4k(key, data) { return createRC4(key).update(data); }
301function createRC4(key) {
304 obj.n = crypto.createCipheriv('rc4', key, null); // Built in NodeJS RC4, this does not work starting with NodeJS v17
305 obj.update = function(x) { return obj.n.update(x); }
307 const RC4 = require('../security/rc4'); // This is the alternative if NodeJS does not support RC4
308 obj.r = new RC4(key.toString('binary'));
309 obj.update = function (x) { return Buffer.from(obj.r.encrypt(x.toString('binary')), 'hex'); }
314function create_negotiate_message() {
315 return negotiate_message(
316 NegotiateFlags.NtlmsspNegociateKeyExch |
317 NegotiateFlags.NtlmsspNegociate128 |
318 NegotiateFlags.NtlmsspNegociateExtendedSessionSecurity |
319 NegotiateFlags.NtlmsspNegociateAlwaysSign |
320 NegotiateFlags.NtlmsspNegociateNTLM |
321 NegotiateFlags.NtlmsspNegociateSeal |
322 NegotiateFlags.NtlmsspNegociateSign |
323 NegotiateFlags.NtlmsspRequestTarget |
324 NegotiateFlags.NtlmsspNegociateUnicode, Buffer.alloc(0), Buffer.alloc(0)
328function negotiate_message(flags, domain, workstation) {
329 const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 32 : 40;
330 const buf = Buffer.alloc(offset);
331 buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature (NTLMSP\0)
332 buf.writeInt32LE(1, 8); // MessageType (1)
333 buf.writeInt32LE(flags, 12); // Flags
334 buf.writeInt16LE(domain.length, 16); // DomainNameLen
335 buf.writeInt16LE(domain.length, 18); // DomainNameMaxLen
336 if (domain.length > 0) { buf.writeInt32LE(offset, 20); } // DomainNameBufferOffset
337 buf.writeInt16LE(workstation.length, 24); // WorkstationLen
338 buf.writeInt16LE(workstation.length, 26); // WorkstationMaxLen
339 if (workstation.length > 0) { buf.writeInt32LE(offset + domain.length, 28); } // WorkstationBufferOffset
340 if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) {
341 buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 32); // ProductMajorVersion
342 buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 33); // ProductMinorVersion
343 buf.writeInt16LE(6002, 34); // ProductBuild
344 //buf.writeInt16LE(0, 36); // Reserved
345 //buf.writeUInt8(0, 38); // Reserved
346 buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 39); // NTLMRevisionCurrent
348 return Buffer.concat([buf, domain, workstation]);
351function mac(rc4_handle, signing_key, seq_num, data) {
352 const buf = Buffer.alloc(4);
353 buf.writeInt32LE(seq_num, 0);
354 var signature = hmac_md5(signing_key, Buffer.concat([buf, data]));
355 return message_signature_ex(rc4_handle.update(signature.slice(0, 8)), seq_num);
358function message_signature_ex(check_sum, seq_num) {
359 const buf = Buffer.alloc(16);
360 buf.writeInt32LE(1, 0); // Version
361 if (check_sum) { check_sum.copy(buf, 4, 0, 8); } // check_sum
362 if (seq_num) { buf.writeInt32LE(seq_num, 12); } // seq_num
366/// Compute a signature of all data exchange during NTLMv2 handshake
367function mic(exported_session_key, negotiate_message, challenge_message, authenticate_message) { return hmac_md5(exported_session_key, Buffer.concat([negotiate_message, challenge_message, authenticate_message])); }
369/// NTLMv2 security interface generate a sign key
370/// By using MD5 of the session key + a static member (sentense)
371function sign_key(exported_session_key, is_client) {
373 return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server signing key magic constant\0")]));
375 return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client signing key magic constant\0")]));
379/// NTLMv2 security interface generate a seal key
380/// By using MD5 of the session key + a static member (sentense)
381function seal_key(exported_session_key, is_client) {
383 return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server sealing key magic constant\0")]));
385 return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client sealing key magic constant\0")]));
389/// We are now able to build a security interface
390/// that will be used by the CSSP manager to cipherring message (private keys)
391/// To detect MITM attack
392function build_security_interface(ntlm) {
395 obj.signing_key = sign_key(ntlm.exported_session_key, true);
396 obj.verify_key = sign_key(ntlm.exported_session_key, false);
397 const client_sealing_key = seal_key(ntlm.exported_session_key, true);
398 const server_sealing_key = seal_key(ntlm.exported_session_key, false);
399 obj.encrypt = createRC4(client_sealing_key);
400 obj.decrypt = createRC4(server_sealing_key);
404 obj.gss_wrapex = function (data) {
405 const encrypted_data = obj.encrypt.update(data);
406 const signature = mac(obj.encrypt, obj.signing_key, obj.seq_num, data);
408 return Buffer.concat([signature, encrypted_data]);
411 obj.gss_unwrapex = function (data) {
412 const version = data.readInt32LE(0);
413 const checksum = data.slice(4, 12);
414 const seqnum = data.readInt32LE(12);
415 const payload = data.slice(16);
416 const plaintext_payload = obj.decrypt.update(payload);
417 const plaintext_checksum = obj.decrypt.update(checksum);
418 const seqnumbuf = Buffer.alloc(4);
419 seqnumbuf.writeInt32LE(seqnum, 0);
420 const computed_checksum = hmac_md5(obj.verify_key, Buffer.concat([seqnumbuf, plaintext_payload])).slice(0, 8);
421 if (!plaintext_checksum.equals(computed_checksum)) { console.log("Invalid checksum on NTLMv2"); }
422 return plaintext_payload;
428function Create_Ntlm() {
430 /// Microsoft Domain for Active Directory
431 domain: "", //String,
435 password: "", // String,
436 /// Key generated from NTLM hash
437 response_key_nt: null, // Buffer
438 /// Key generated from NTLM hash
439 response_key_lm: null, // Buffer
440 /// Keep trace of each messages to compute a final hash
441 negotiate_message: null, // Buffer
442 /// Key use to ciphering messages
443 exported_session_key: crypto.randomBytes(16), // Buffer
444 /// True if session use unicode
445 is_unicode: false // Boolean
449function authenticate_message(lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key, flags) {
450 const payload = Buffer.concat([lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key]);
451 const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 80 : 88;
452 const buf = Buffer.alloc(offset - 16);
453 buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature
454 buf.writeInt32LE(3, 8); // MessageType
455 buf.writeInt16LE(lm_challenge_response.length, 12); // LmChallengeResponseLen
456 buf.writeInt16LE(lm_challenge_response.length, 14); // LmChallengeResponseMaxLen
457 buf.writeInt32LE(offset, 16); // LmChallengeResponseBufferOffset
458 buf.writeInt16LE(nt_challenge_response.length, 20); // NtChallengeResponseLen
459 buf.writeInt16LE(nt_challenge_response.length, 22); // NtChallengeResponseMaxLen
460 buf.writeInt32LE(offset + lm_challenge_response.length, 24); // NtChallengeResponseBufferOffset
461 buf.writeInt16LE(domain.length, 28); // DomainNameLen
462 buf.writeInt16LE(domain.length, 30); // DomainNameMaxLen
463 buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length, 32); // DomainNameBufferOffset
464 buf.writeInt16LE(user.length, 36); // UserNameLen
465 buf.writeInt16LE(user.length, 38); // UserNameMaxLen
466 buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length, 40); // UserNameBufferOffset
467 buf.writeInt16LE(workstation.length, 44); // WorkstationLen
468 buf.writeInt16LE(workstation.length, 46); // WorkstationMaxLen
469 buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length, 48); // WorkstationBufferOffset
470 buf.writeInt16LE(encrypted_random_session_key.length, 52); // EncryptedRandomSessionLen
471 buf.writeInt16LE(encrypted_random_session_key.length, 54); // EncryptedRandomSessionMaxLen
472 buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length + workstation.length, 56); // EncryptedRandomSessionBufferOffset
473 buf.writeInt32LE(flags, 60); // NegotiateFlags
474 if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) {
475 buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 64); // ProductMajorVersion
476 buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 65); // ProductMinorVersion
477 buf.writeInt16LE(6002, 66); // ProductBuild
478 //buf.writeInt16LE(0, 68); // Reserved
479 //buf.writeUInt8(0, 70); // Reserved
480 buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 71); // NTLMRevisionCurrent
482 return [buf, payload];
485function create_ts_authinfo(auth_info) {
487 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
488 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
489 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
491 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
492 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, auth_info.toString('binary'))
495 return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
498function create_ts_credentials(domain, user, password) {
500 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
501 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
502 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, domain.toString('binary'))
504 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
505 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, user.toString('binary'))
507 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
508 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, password.toString('binary'))
511 const ts_password_cred_encoded = asn1.toDer(asn1obj).data;
513 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
514 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
515 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(1)),
517 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
518 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ts_password_cred_encoded)
521 return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
524function create_ts_authenticate(nego, pub_key_auth) {
525 // Create create_ts_request
527 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
528 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
529 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
531 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
532 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
533 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
534 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
535 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, nego.toString('binary'))
540 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, [
541 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, pub_key_auth.toString('binary'))
545 // Serialize an ASN.1 object to DER format
546 return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
549function read_challenge_message(ntlm, derBuffer) {
551 //console.log('ntlm.negotiate_message', ntlm.negotiate_message.toString('hex'));
552 //ntlm.negotiate_message = Buffer.from('4e544c4d53535000010000003582086000000000000000000000000000000000', 'hex');
555 //ntlm.exported_session_key = Buffer.from('9a1ed052e932834a311daf90c2750219', 'hex'); // *************************
556 //derBuffer = Buffer.from('4e544c4d53535000020000000e000e003800000035828a6259312ef59a4517dd000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c00070008007b7b3bee9e5ad80100000000', 'hex');
558 //console.log("YST: read_challenge_message1: ", derBuffer.toString('hex'));
560 const headerSignature = derBuffer.slice(0, 8);
561 if (headerSignature.toString('hex') != '4e544c4d53535000') { console.log('BAD SIGNATURE'); }
562 const messageType = derBuffer.readInt32LE(8);
563 if (messageType != 2) { console.log('BAD MESSAGE TYPE'); }
564 const targetNameLen = derBuffer.readInt16LE(12);
565 const targetNameLenMax = derBuffer.readInt16LE(14);
566 const targetNameBufferOffset = derBuffer.readInt32LE(16);
567 const negotiateFlags = derBuffer.readInt32LE(20);
568 const serverChallenge = derBuffer.slice(24, 32);
569 const reserved = derBuffer.slice(32, 40);
570 if (reserved.toString('hex') != '0000000000000000') { console.log('BAD RESERVED'); }
571 const targetInfoLen = derBuffer.readInt16LE(40);
572 const targetInfoLenMax = derBuffer.readInt16LE(42);
573 const targetInfoBufferOffset = derBuffer.readInt32LE(44);
574 const targetName = derBuffer.slice(targetNameBufferOffset, targetNameBufferOffset + targetNameLen);
575 const targetInfoBuf = derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen);
576 const targetInfo = decodeTargetInfo(derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen));
577 const timestamp = targetInfo[7];
578 //const timestamp = Buffer.from('7b7b3bee9e5ad801', 'hex'); // **************
579 if (timestamp == null) { console.log('NO TIMESTAMP'); }
580 const clientChallenge = crypto.randomBytes(8);
581 //const clientChallenge = Buffer.from('10aac9679ef64e66', 'hex'); // *****************************
582 const response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
583 const response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
585 //console.log("YST: target_name:", targetInfoBuf.toString('hex'));
586 //console.log("YST: timestamp:", timestamp.toString('hex'));
587 //console.log('YST: client_challenge:', clientChallenge.toString('hex'));
588 //console.log("YST: response_key_nt:", response_key_nt.toString('hex'));
589 //console.log("YST: response_key_lm:", response_key_lm.toString('hex'));
591 var resp = compute_response_v2(response_key_nt, response_key_lm, serverChallenge, clientChallenge, timestamp, targetInfoBuf);
592 const nt_challenge_response = resp[0];
593 const lm_challenge_response = resp[1];
594 const session_base_key = resp[2];
596 //console.log('YST: nt_challenge_response:', nt_challenge_response.toString('hex'));
597 //console.log('YST: lm_challenge_response:', lm_challenge_response.toString('hex'));
598 //console.log("YST: session_base_key:", session_base_key.toString('hex'));
600 const key_exchange_key = kx_key_v2(session_base_key, lm_challenge_response, serverChallenge);
601 const encrypted_random_session_key = rc4k(key_exchange_key, ntlm.exported_session_key);
603 //console.log("YST: key_exchange_key:", key_exchange_key.toString('hex'));
604 //console.log("YST: self.exported_session_key:", ntlm.exported_session_key.toString('hex'));
605 //console.log("YST: encrypted_random_session_key:", encrypted_random_session_key.toString('hex'));
607 ntlm.is_unicode = ((negotiateFlags & 1) != 0)
608 //console.log("YST: self.is_unicode: {}", ntlm.is_unicode);
611 if (ntlm.is_unicode) {
612 xdomain = toUnicode(ntlm.domain);
613 xuser = toUnicode(ntlm.user);
615 xdomain = Buffer.from(ntlm.domain, 'utf8');
616 xuser = Buffer.from(ntlm.user, 'utf8');
619 //console.log("YST: domain:", xdomain.toString('hex'));
620 //console.log("YST: user:", xuser.toString('hex'));
622 const auth_message_compute = authenticate_message(lm_challenge_response, nt_challenge_response, xdomain, xuser, zeroBuffer(0), encrypted_random_session_key, negotiateFlags);
624 // Write a tmp message to compute MIC and then include it into final message
625 const tmp_final_auth_message = Buffer.concat([auth_message_compute[0], zeroBuffer(16), auth_message_compute[1]]);
627 //console.log("YST: tmp_final_auth_message: {}", tmp_final_auth_message.toString('hex'));
629 const signature = mic(ntlm.exported_session_key, ntlm.negotiate_message, derBuffer, tmp_final_auth_message);
631 //console.log("YST: signature: {}", signature.toString('hex'));
633 const r = Buffer.concat([auth_message_compute[0], signature, auth_message_compute[1]]);
635 //console.log("YST: read_challenge_message2: {}", r.toString('hex'));
641 console.log('--- Starting RDP NLA Unit Tests');
643 // Test format of the first client message
644 var r = create_negotiate_message();
645 console.log(compareArray(bufToArr(r), [78, 84, 76, 77, 83, 83, 80, 0, 1, 0, 0, 0, 53, 130, 8, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) ? "negotiate_message passed." : "negotiate_message failed.");
647 // Test of MD4 hash function
648 r = md4(Buffer.from("foo"));
649 console.log(compareArray(bufToArr(r), [0x0a, 0xc6, 0x70, 0x0c, 0x49, 0x1d, 0x70, 0xfb, 0x86, 0x50, 0x94, 0x0b, 0x1c, 0xa1, 0xe4, 0xb2]) ? "RC4 passed." : "RC4 failed.");
651 // Test of the unicode function
652 r = toUnicode("foo");
653 console.log(compareArray(bufToArr(r), [0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00]) ? "Unicode passed." : "Unicode failed.");
655 // Test HMAC_MD5 function
656 r = hmac_md5(Buffer.from("foo"), Buffer.from("bar"));
657 console.log(compareArray(bufToArr(r), [0x0c, 0x7a, 0x25, 0x02, 0x81, 0x31, 0x5a, 0xb8, 0x63, 0x54, 0x9f, 0x66, 0xcd, 0x8a, 0x3a, 0x53]) ? "HMAC_MD5 passed." : "HMAC_MD5 failed.");
659 // Test NTOWFv2 function
660 r = ntowfv2("foo", "user", "domain");
661 console.log(compareArray(bufToArr(r), [0x6e, 0x53, 0xb9, 0x0, 0x97, 0x8c, 0x87, 0x1f, 0x91, 0xde, 0x6, 0x44, 0x9d, 0x8b, 0x8b, 0x81]) ? "NTOWFv2 passed." : "NTOWFv2 failed.");
663 // Test LMOWFv2 function
664 r = ntowfv2("foo", "user", "domain");
665 console.log(compareArray(bufToArr(r), ntowfv2("foo", "user", "domain")) ? "LMOWFv2 passed." : "LMOWFv2 failed.");
667 // Test compute response v2 function
668 r = compute_response_v2(Buffer.from("a"), Buffer.from("b"), Buffer.from("c"), Buffer.from("d"), Buffer.from("e"), Buffer.from("f"));
669 console.log(compareArray(bufToArr(r[0]), [0xb4, 0x23, 0x84, 0xf, 0x6e, 0x83, 0xc1, 0x5a, 0x45, 0x4f, 0x4c, 0x92, 0x7a, 0xf2, 0xc3, 0x3e, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x64, 0x0, 0x0, 0x0, 0x0, 0x66]) ? "responsev2 1 passed." : "responsev2 1 failed.");
670 console.log(compareArray(bufToArr(r[1]), [0x56, 0xba, 0xff, 0x2d, 0x98, 0xbe, 0xcd, 0xa5, 0x6d, 0xe6, 0x17, 0x89, 0xe1, 0xed, 0xca, 0xae, 0x64]) ? "responsev2 2 passed." : "responsev2 2 failed.");
671 console.log(compareArray(bufToArr(r[2]), [0x40, 0x3b, 0x33, 0xe5, 0x24, 0x34, 0x3c, 0xc3, 0x24, 0xa0, 0x4d, 0x77, 0x75, 0x34, 0xa4, 0xd0]) ? "responsev2 3 passed." : "responsev2 3 failed.");
673 // Test of rc4k function
674 r = rc4k(Buffer.from("foo"), Buffer.from("bar"));
675 console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "rc4k passed." : "rc4k failed.");
677 // Test of sign_key function
678 r = sign_key(Buffer.from("foo"), true);
679 console.log(compareArray(bufToArr(r), [253, 238, 149, 155, 221, 78, 43, 179, 82, 61, 111, 132, 168, 68, 222, 15]) ? "sign_key 1 passed." : "sign_key 1 failed.");
680 r = sign_key(Buffer.from("foo"), false);
681 console.log(compareArray(bufToArr(r), [90, 201, 12, 225, 140, 156, 151, 61, 156, 56, 31, 254, 10, 223, 252, 74]) ? "sign_key 2 passed." : "sign_key 2 failed.");
683 // Test of seal_key function
684 r = seal_key(Buffer.from("foo"), true);
685 console.log(compareArray(bufToArr(r), [20, 213, 185, 176, 168, 142, 134, 244, 36, 249, 89, 247, 180, 36, 162, 101]) ? "seal_key 1 passed." : "seal_key 1 failed.");
686 r = seal_key(Buffer.from("foo"), false);
687 console.log(compareArray(bufToArr(r), [64, 125, 160, 17, 144, 165, 62, 226, 22, 125, 128, 31, 103, 141, 55, 40]) ? "seal_key 2 passed." : "seal_key 2 failed.");
689 // Test signature function
690 var rc4 = createRC4(Buffer.from("foo"));
691 r = mac(rc4, Buffer.from("bar"), 0, Buffer.from("data"));
692 console.log(compareArray(bufToArr(r), [1, 0, 0, 0, 77, 211, 144, 84, 51, 242, 202, 176, 0, 0, 0, 0]) ? "Signature passed." : "Signature failed.");
694 // Test challenge message
695 r = authenticate_message(Buffer.from("foo"), Buffer.from("foo"), Buffer.from("domain"), Buffer.from("user"), Buffer.from("workstation"), Buffer.from("foo"), 0);
696 var buf = Buffer.concat([r[0], Buffer.alloc(16), r[1]]);
697 console.log(compareArray(bufToArr(buf), [78, 84, 76, 77, 83, 83, 80, 0, 3, 0, 0, 0, 3, 0, 3, 0, 80, 0, 0, 0, 3, 0, 3, 0, 83, 0, 0, 0, 6, 0, 6, 0, 86, 0, 0, 0, 4, 0, 4, 0, 92, 0, 0, 0, 11, 0, 11, 0, 96, 0, 0, 0, 3, 0, 3, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 111, 111, 102, 111, 111, 100, 111, 109, 97, 105, 110, 117, 115, 101, 114, 119, 111, 114, 107, 115, 116, 97, 116, 105, 111, 110, 102, 111, 111]) ? "Challenge message passed." : "Challenge message failed.");
700 rc4 = createRC4(Buffer.from("foo"));
701 r = rc4.update(Buffer.from("bar"));
702 console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "RC4 1 passed." : "RC4 1 failed.");
703 r = rc4.update(Buffer.from("bar"));
704 console.log(compareArray(bufToArr(r), [75, 169, 19]) ? "RC4 2 passed." : "RC4 2 failed.");
706 // Test create_ts_authenticate
707 r = create_ts_authenticate(Buffer.from("000102", 'hex'), Buffer.from("000102", 'hex'));
708 console.log(compareArray(bufToArr(r), [48, 25, 160, 3, 2, 1, 2, 161, 11, 48, 9, 48, 7, 160, 5, 4, 3, 0, 1, 2, 163, 5, 4, 3, 0, 1, 2]) ? "create_ts_authenticate passed." : "create_ts_authenticate failed.");
710 // Test test_create_ts_credentials
711 r = create_ts_credentials(Buffer.from("domain"), Buffer.from("user"), Buffer.from("password"));
712 console.log(compareArray(bufToArr(r), [48, 41, 160, 3, 2, 1, 1, 161, 34, 4, 32, 48, 30, 160, 8, 4, 6, 100, 111, 109, 97, 105, 110, 161, 6, 4, 4, 117, 115, 101, 114, 162, 10, 4, 8, 112, 97, 115, 115, 119, 111, 114, 100]) ? "test_create_ts_credentials passed." : "test_create_ts_credentials failed.");
714 // Test create_ts_authinfo
715 r = create_ts_authinfo(Buffer.from("foo"));
716 console.log(compareArray(bufToArr(r), [48, 12, 160, 3, 2, 1, 2, 162, 5, 4, 3, 102, 111, 111]) ? "create_ts_authinfo passed." : "create_ts_authinfo failed.");
718 console.log('--- RDP NLA Unit Tests Completed');