nla.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. const inherits = require('util').inherits;
  2. const type = require('../core').type;
  3. const events = require('events');
  4. const crypto = require('crypto');
  5. const forge = require('node-forge');
  6. const asn1 = forge.asn1;
  7. const pki = forge.pki;
  8. /**
  9. * NLA layer of rdp stack
  10. */
  11. function NLA(transport, nlaCompletedFunc, domain, user, password) {
  12. // Get NTLM ready
  13. const ntlm = Create_Ntlm();
  14. ntlm.domain = domain;
  15. ntlm.completedFunc = nlaCompletedFunc;
  16. ntlm.user = user;
  17. ntlm.password = password;
  18. ntlm.response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain);
  19. ntlm.response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain);
  20. this.ntlm = ntlm;
  21. this.state = 1;
  22. // Get transport ready
  23. this.transport = transport;
  24. // Wait 2 bytes
  25. this.transport.expect(2);
  26. // Next state is receive header
  27. var self = this;
  28. this.oldDataListeners = this.transport.listeners('data');
  29. this.oldCloseListeners = this.transport.listeners('close');
  30. this.oldErrorListeners = this.transport.listeners('error');
  31. // Unhook the previous transport handler
  32. this.transport.removeAllListeners('data');
  33. this.transport.removeAllListeners('close');
  34. this.transport.removeAllListeners('error');
  35. // Hook this module as the transport handler
  36. this.transport.once('data', function (s) {
  37. self.recvHeader(s);
  38. }).on('close', function () {
  39. self.emit('close');
  40. }).on('error', function (err) {
  41. self.emit('close'); // Errors occur when NLA authentication fails, for now, just close.
  42. //self.emit('error', err);
  43. });
  44. }
  45. /**
  46. * inherit from a packet layer
  47. */
  48. inherits(NLA, events.EventEmitter);
  49. /**
  50. * Receive correct packet as expected
  51. * @param s {type.Stream}
  52. */
  53. NLA.prototype.recvHeader = function (s) {
  54. //console.log('NLA - recvHeader', s);
  55. var self = this;
  56. var derType = new type.UInt8().read(s).value;
  57. var derLen = new type.UInt8().read(s).value;
  58. self.buffers = [ s.buffer ];
  59. if (derLen < 128) {
  60. // wait for the entire data block
  61. this.transport.expect(derLen);
  62. this.transport.once('data', function (s) { self.recvData(s); });
  63. } else {
  64. // wait for the header size
  65. this.transport.expect(derLen - 128);
  66. this.transport.once('data', function (s) { self.recvHeaderSize(s); });
  67. }
  68. //console.log('NLA - DER', derType, derLen);
  69. };
  70. /**
  71. * Receive correct packet as expected
  72. * @param s {type.Stream}
  73. */
  74. NLA.prototype.recvHeaderSize = function (s) {
  75. //console.log('NLA - recvHeaderSize', s.buffer.length);
  76. var self = this;
  77. self.buffers.push(s.buffer);
  78. if (s.buffer.length == 1) {
  79. // wait for the entire data block
  80. var derLen = s.buffer.readUInt8(0);
  81. this.transport.expect(derLen);
  82. this.transport.once('data', function (s) { self.recvData(s); });
  83. } else if (s.buffer.length == 2) {
  84. // wait for the entire data block
  85. var derLen = s.buffer.readUInt16BE(0);
  86. this.transport.expect(derLen);
  87. this.transport.once('data', function (s) { self.recvData(s); });
  88. }
  89. }
  90. /**
  91. * Receive correct packet as expected
  92. * @param s {type.Stream}
  93. */
  94. NLA.prototype.recvData = function (s) {
  95. //console.log('NLA - recvData', s.buffer.length);
  96. var self = this;
  97. self.buffers.push(s.buffer);
  98. var entireBuffer = Buffer.concat(self.buffers);
  99. //console.log('entireBuffer', entireBuffer.toString('hex'));
  100. // We have a full ASN1 data block, decode it now
  101. const der = asn1.fromDer(entireBuffer.toString('binary'));
  102. const derNum = der.value[0].value[0].value.charCodeAt(0);
  103. //console.log('NLA - Number', derNum);
  104. if (derNum == 6) {
  105. if (this.state == 1) {
  106. const derBuffer = Buffer.from(der.value[1].value[0].value[0].value[0].value[0].value, 'binary');
  107. const client_challenge = read_challenge_message(this.ntlm, derBuffer);
  108. self.security_interface = build_security_interface(this.ntlm);
  109. const peer_cert = this.transport.secureSocket.getPeerCertificate();
  110. const challenge = create_ts_authenticate(client_challenge, self.security_interface.gss_wrapex(peer_cert.pubkey.slice(24)));
  111. this.ntlm.publicKeyDer = peer_cert.pubkey.slice(24);
  112. this.send(challenge);
  113. this.state = 2;
  114. } else if (this.state == 2) {
  115. const derBuffer = Buffer.from(der.value[1].value[0].value, 'binary');
  116. const publicKeyDer = self.security_interface.gss_unwrapex(derBuffer);
  117. // Check that the public key is identical except the first byte which is the DER encoding type.
  118. if (!this.ntlm.publicKeyDer.slice(1).equals(publicKeyDer.slice(1))) { console.log('RDP man-in-the-middle detected.'); close(); return; }
  119. delete this.ntlm.publicKeyDer; // Clean this up, we don't need it anymore.
  120. var xdomain, xuser, xpassword;
  121. if (this.ntlm.is_unicode) {
  122. xdomain = toUnicode(this.ntlm.domain);
  123. xuser = toUnicode(this.ntlm.user);
  124. xpassword = toUnicode(this.ntlm.password);
  125. } else {
  126. xdomain = Buffer.from(this.ntlm.domain, 'utf8');
  127. xuser = Buffer.from(this.ntlm.user, 'utf8');
  128. xpassword = Buffer.from(this.ntlm.password, 'utf8');
  129. }
  130. const credentials = create_ts_authinfo(self.security_interface.gss_wrapex(create_ts_credentials(xdomain, xuser, xpassword)));
  131. this.send(credentials);
  132. // Rehook the previous transport handler
  133. this.transport.removeAllListeners('data');
  134. this.transport.removeAllListeners('close');
  135. this.transport.removeAllListeners('error');
  136. for (var i in this.oldDataListeners) { this.transport.once('data', this.oldDataListeners[i]); }
  137. for (var i in this.oldCloseListeners) { this.transport.on('close', this.oldCloseListeners[i]); }
  138. for (var i in this.oldErrorListeners) { this.transport.on('error', this.oldErrorListeners[i]); }
  139. // Done!
  140. this.transport.expect(2);
  141. this.state = 3;
  142. this.ntlm.completedFunc();
  143. return;
  144. }
  145. }
  146. // Receive next block of data
  147. this.transport.expect(2);
  148. this.transport.once('data', function (s) { self.recvHeader(s); });
  149. }
  150. /**
  151. * Send message throught NLA layer
  152. * @param message {type.*}
  153. */
  154. NLA.prototype.send = function (message) {
  155. this.transport.sendBuffer(message);
  156. };
  157. /**
  158. * close stack
  159. */
  160. NLA.prototype.close = function() {
  161. this.transport.close();
  162. };
  163. NLA.prototype.sendNegotiateMessage = function () {
  164. // Create create_ts_request
  165. this.ntlm.negotiate_message = create_negotiate_message();
  166. const asn1obj =
  167. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  168. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  169. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
  170. ]),
  171. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
  172. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  173. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  174. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  175. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, this.ntlm.negotiate_message.toString('binary'))
  176. ])
  177. ])
  178. ])
  179. ])
  180. ]);
  181. // Serialize an ASN.1 object to DER format
  182. this.send(Buffer.from(asn1.toDer(asn1obj).data, 'binary'));
  183. }
  184. /**
  185. * Module exports
  186. */
  187. module.exports = NLA;
  188. const NegotiateFlags = {
  189. NtlmsspNegociate56: 0x80000000,
  190. NtlmsspNegociateKeyExch: 0x40000000,
  191. NtlmsspNegociate128: 0x20000000,
  192. NtlmsspNegociateVersion: 0x02000000,
  193. NtlmsspNegociateTargetInfo: 0x00800000,
  194. NtlmsspRequestNonNTSessionKey: 0x00400000,
  195. NtlmsspNegociateIdentify: 0x00100000,
  196. NtlmsspNegociateExtendedSessionSecurity: 0x00080000,
  197. NtlmsspTargetTypeServer: 0x00020000,
  198. NtlmsspTargetTypeDomain: 0x00010000,
  199. NtlmsspNegociateAlwaysSign: 0x00008000,
  200. NtlmsspNegociateOEMWorkstationSupplied: 0x00002000,
  201. NtlmsspNegociateOEMDomainSupplied: 0x00001000,
  202. NtlmsspNegociateNTLM: 0x00000200,
  203. NtlmsspNegociateLMKey: 0x00000080,
  204. NtlmsspNegociateDatagram: 0x00000040,
  205. NtlmsspNegociateSeal: 0x00000020,
  206. NtlmsspNegociateSign: 0x00000010,
  207. NtlmsspRequestTarget: 0x00000004,
  208. NtlmNegotiateOEM: 0x00000002,
  209. NtlmsspNegociateUnicode: 0x00000001
  210. }
  211. const MajorVersion = {
  212. WindowsMajorVersion5: 0x05,
  213. WindowsMajorVersion6: 0x06
  214. }
  215. const MinorVersion = {
  216. WindowsMinorVersion0: 0x00,
  217. WindowsMinorVersion1: 0x01,
  218. WindowsMinorVersion2: 0x02,
  219. WindowsMinorVersion3: 0x03
  220. }
  221. const NTLMRevision = {
  222. NtlmSspRevisionW2K3: 0x0F
  223. }
  224. function decodeTargetInfo(targetInfoBuf) {
  225. var r = {}, type, len, data, ptr = 0;
  226. while (true) {
  227. type = targetInfoBuf.readInt16LE(ptr);
  228. if (type == 0) break;
  229. len = targetInfoBuf.readInt16LE(ptr + 2);
  230. r[type] = targetInfoBuf.slice(ptr + 4, ptr + 4 + len);
  231. ptr += (4 + len);
  232. }
  233. return r;
  234. }
  235. function bufToArr(b) { var r = []; for (var i = 0; i < b.length; i++) { r.push(b.readUInt8(i)); } return r; } // For unit testing
  236. function 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
  237. function toUnicode(str) { return Buffer.from(str, 'ucs2'); }
  238. function md4(buffer) {
  239. try {
  240. return crypto.createHash('md4').update(buffer).digest(); // Built in NodeJS MD4, this does not work starting with NodeJS v17
  241. } catch (ex) {
  242. return Buffer.from(require('../security/md4').array(buffer.toString('binary'))); // This is the alternative if NodeJS does not support MD4
  243. }
  244. }
  245. function md5(str) { return crypto.createHash('md5').update(str).digest(); }
  246. function hmac_md5(key, data) { return crypto.createHmac('md5', key).update(data).digest(); }
  247. function ntowfv2(password, user, domain) { return hmac_md5(md4(toUnicode(password)), toUnicode(user.toUpperCase() + domain)); }
  248. function lmowfv2(password, user, domain) { return ntowfv2(password, user, domain); }
  249. function zeroBuffer(len) { return Buffer.alloc(len); }
  250. function compute_response_v2(response_key_nt, response_key_lm, server_challenge, client_challenge, time, server_name) {
  251. const response_version = Buffer.from('01', 'hex');
  252. const hi_response_version = Buffer.from('01', 'hex');
  253. const temp = Buffer.concat([response_version, hi_response_version, zeroBuffer(6), time, client_challenge, zeroBuffer(4), server_name]);
  254. const nt_proof_str = hmac_md5(response_key_nt, Buffer.concat([server_challenge, temp]));
  255. const nt_challenge_response = Buffer.concat([nt_proof_str, temp]);
  256. const lm_challenge_response = Buffer.concat([hmac_md5(response_key_lm, Buffer.concat([server_challenge, client_challenge])), client_challenge]);
  257. const session_base_key = hmac_md5(response_key_nt, nt_proof_str);
  258. return [nt_challenge_response, lm_challenge_response, session_base_key];
  259. }
  260. function kx_key_v2(session_base_key, _lm_challenge_response, _server_challenge) { return session_base_key; }
  261. function rc4k(key, data) { return createRC4(key).update(data); }
  262. function createRC4(key) {
  263. const obj = {};
  264. try {
  265. obj.n = crypto.createCipheriv('rc4', key, null); // Built in NodeJS RC4, this does not work starting with NodeJS v17
  266. obj.update = function(x) { return obj.n.update(x); }
  267. } catch (ex) {
  268. const RC4 = require('../security/rc4'); // This is the alternative if NodeJS does not support RC4
  269. obj.r = new RC4(key.toString('binary'));
  270. obj.update = function (x) { return Buffer.from(obj.r.encrypt(x.toString('binary')), 'hex'); }
  271. }
  272. return obj;
  273. }
  274. function create_negotiate_message() {
  275. return negotiate_message(
  276. NegotiateFlags.NtlmsspNegociateKeyExch |
  277. NegotiateFlags.NtlmsspNegociate128 |
  278. NegotiateFlags.NtlmsspNegociateExtendedSessionSecurity |
  279. NegotiateFlags.NtlmsspNegociateAlwaysSign |
  280. NegotiateFlags.NtlmsspNegociateNTLM |
  281. NegotiateFlags.NtlmsspNegociateSeal |
  282. NegotiateFlags.NtlmsspNegociateSign |
  283. NegotiateFlags.NtlmsspRequestTarget |
  284. NegotiateFlags.NtlmsspNegociateUnicode, Buffer.alloc(0), Buffer.alloc(0)
  285. );
  286. }
  287. function negotiate_message(flags, domain, workstation) {
  288. const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 32 : 40;
  289. const buf = Buffer.alloc(offset);
  290. buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature (NTLMSP\0)
  291. buf.writeInt32LE(1, 8); // MessageType (1)
  292. buf.writeInt32LE(flags, 12); // Flags
  293. buf.writeInt16LE(domain.length, 16); // DomainNameLen
  294. buf.writeInt16LE(domain.length, 18); // DomainNameMaxLen
  295. if (domain.length > 0) { buf.writeInt32LE(offset, 20); } // DomainNameBufferOffset
  296. buf.writeInt16LE(workstation.length, 24); // WorkstationLen
  297. buf.writeInt16LE(workstation.length, 26); // WorkstationMaxLen
  298. if (workstation.length > 0) { buf.writeInt32LE(offset + domain.length, 28); } // WorkstationBufferOffset
  299. if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) {
  300. buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 32); // ProductMajorVersion
  301. buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 33); // ProductMinorVersion
  302. buf.writeInt16LE(6002, 34); // ProductBuild
  303. //buf.writeInt16LE(0, 36); // Reserved
  304. //buf.writeUInt8(0, 38); // Reserved
  305. buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 39); // NTLMRevisionCurrent
  306. }
  307. return Buffer.concat([buf, domain, workstation]);
  308. }
  309. function mac(rc4_handle, signing_key, seq_num, data) {
  310. const buf = Buffer.alloc(4);
  311. buf.writeInt32LE(seq_num, 0);
  312. var signature = hmac_md5(signing_key, Buffer.concat([buf, data]));
  313. return message_signature_ex(rc4_handle.update(signature.slice(0, 8)), seq_num);
  314. }
  315. function message_signature_ex(check_sum, seq_num) {
  316. const buf = Buffer.alloc(16);
  317. buf.writeInt32LE(1, 0); // Version
  318. if (check_sum) { check_sum.copy(buf, 4, 0, 8); } // check_sum
  319. if (seq_num) { buf.writeInt32LE(seq_num, 12); } // seq_num
  320. return buf;
  321. }
  322. /// Compute a signature of all data exchange during NTLMv2 handshake
  323. function mic(exported_session_key, negotiate_message, challenge_message, authenticate_message) { return hmac_md5(exported_session_key, Buffer.concat([negotiate_message, challenge_message, authenticate_message])); }
  324. /// NTLMv2 security interface generate a sign key
  325. /// By using MD5 of the session key + a static member (sentense)
  326. function sign_key(exported_session_key, is_client) {
  327. if (is_client) {
  328. return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server signing key magic constant\0")]));
  329. } else {
  330. return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client signing key magic constant\0")]));
  331. }
  332. }
  333. /// NTLMv2 security interface generate a seal key
  334. /// By using MD5 of the session key + a static member (sentense)
  335. function seal_key(exported_session_key, is_client) {
  336. if (is_client) {
  337. return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server sealing key magic constant\0")]));
  338. } else {
  339. return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client sealing key magic constant\0")]));
  340. }
  341. }
  342. /// We are now able to build a security interface
  343. /// that will be used by the CSSP manager to cipherring message (private keys)
  344. /// To detect MITM attack
  345. function build_security_interface(ntlm) {
  346. const obj = {};
  347. if (ntlm) {
  348. obj.signing_key = sign_key(ntlm.exported_session_key, true);
  349. obj.verify_key = sign_key(ntlm.exported_session_key, false);
  350. const client_sealing_key = seal_key(ntlm.exported_session_key, true);
  351. const server_sealing_key = seal_key(ntlm.exported_session_key, false);
  352. obj.encrypt = createRC4(client_sealing_key);
  353. obj.decrypt = createRC4(server_sealing_key);
  354. }
  355. obj.seq_num = 0;
  356. obj.gss_wrapex = function (data) {
  357. const encrypted_data = obj.encrypt.update(data);
  358. const signature = mac(obj.encrypt, obj.signing_key, obj.seq_num, data);
  359. obj.seq_num++;
  360. return Buffer.concat([signature, encrypted_data]);
  361. }
  362. obj.gss_unwrapex = function (data) {
  363. const version = data.readInt32LE(0);
  364. const checksum = data.slice(4, 12);
  365. const seqnum = data.readInt32LE(12);
  366. const payload = data.slice(16);
  367. const plaintext_payload = obj.decrypt.update(payload);
  368. const plaintext_checksum = obj.decrypt.update(checksum);
  369. const seqnumbuf = Buffer.alloc(4);
  370. seqnumbuf.writeInt32LE(seqnum, 0);
  371. const computed_checksum = hmac_md5(obj.verify_key, Buffer.concat([seqnumbuf, plaintext_payload])).slice(0, 8);
  372. if (!plaintext_checksum.equals(computed_checksum)) { console.log("Invalid checksum on NTLMv2"); }
  373. return plaintext_payload;
  374. }
  375. return obj;
  376. }
  377. function Create_Ntlm() {
  378. return {
  379. /// Microsoft Domain for Active Directory
  380. domain: "", //String,
  381. /// Username
  382. user: "", //String,
  383. /// Password
  384. password: "", // String,
  385. /// Key generated from NTLM hash
  386. response_key_nt: null, // Buffer
  387. /// Key generated from NTLM hash
  388. response_key_lm: null, // Buffer
  389. /// Keep trace of each messages to compute a final hash
  390. negotiate_message: null, // Buffer
  391. /// Key use to ciphering messages
  392. exported_session_key: crypto.randomBytes(16), // Buffer
  393. /// True if session use unicode
  394. is_unicode: false // Boolean
  395. }
  396. }
  397. function authenticate_message(lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key, flags) {
  398. const payload = Buffer.concat([lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key]);
  399. const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 80 : 88;
  400. const buf = Buffer.alloc(offset - 16);
  401. buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature
  402. buf.writeInt32LE(3, 8); // MessageType
  403. buf.writeInt16LE(lm_challenge_response.length, 12); // LmChallengeResponseLen
  404. buf.writeInt16LE(lm_challenge_response.length, 14); // LmChallengeResponseMaxLen
  405. buf.writeInt32LE(offset, 16); // LmChallengeResponseBufferOffset
  406. buf.writeInt16LE(nt_challenge_response.length, 20); // NtChallengeResponseLen
  407. buf.writeInt16LE(nt_challenge_response.length, 22); // NtChallengeResponseMaxLen
  408. buf.writeInt32LE(offset + lm_challenge_response.length, 24); // NtChallengeResponseBufferOffset
  409. buf.writeInt16LE(domain.length, 28); // DomainNameLen
  410. buf.writeInt16LE(domain.length, 30); // DomainNameMaxLen
  411. buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length, 32); // DomainNameBufferOffset
  412. buf.writeInt16LE(user.length, 36); // UserNameLen
  413. buf.writeInt16LE(user.length, 38); // UserNameMaxLen
  414. buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length, 40); // UserNameBufferOffset
  415. buf.writeInt16LE(workstation.length, 44); // WorkstationLen
  416. buf.writeInt16LE(workstation.length, 46); // WorkstationMaxLen
  417. buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length, 48); // WorkstationBufferOffset
  418. buf.writeInt16LE(encrypted_random_session_key.length, 52); // EncryptedRandomSessionLen
  419. buf.writeInt16LE(encrypted_random_session_key.length, 54); // EncryptedRandomSessionMaxLen
  420. buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length + workstation.length, 56); // EncryptedRandomSessionBufferOffset
  421. buf.writeInt32LE(flags, 60); // NegotiateFlags
  422. if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) {
  423. buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 64); // ProductMajorVersion
  424. buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 65); // ProductMinorVersion
  425. buf.writeInt16LE(6002, 66); // ProductBuild
  426. //buf.writeInt16LE(0, 68); // Reserved
  427. //buf.writeUInt8(0, 70); // Reserved
  428. buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 71); // NTLMRevisionCurrent
  429. }
  430. return [buf, payload];
  431. }
  432. function create_ts_authinfo(auth_info) {
  433. asn1obj =
  434. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  435. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  436. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
  437. ]),
  438. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
  439. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, auth_info.toString('binary'))
  440. ])
  441. ]);
  442. return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
  443. }
  444. function create_ts_credentials(domain, user, password) {
  445. var asn1obj =
  446. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  447. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  448. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, domain.toString('binary'))
  449. ]),
  450. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
  451. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, user.toString('binary'))
  452. ]),
  453. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
  454. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, password.toString('binary'))
  455. ])
  456. ]);
  457. const ts_password_cred_encoded = asn1.toDer(asn1obj).data;
  458. asn1obj =
  459. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  460. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  461. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(1)),
  462. ]),
  463. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
  464. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ts_password_cred_encoded)
  465. ])
  466. ]);
  467. return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
  468. }
  469. function create_ts_authenticate(nego, pub_key_auth) {
  470. // Create create_ts_request
  471. const asn1obj =
  472. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  473. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  474. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
  475. ]),
  476. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
  477. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  478. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  479. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  480. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, nego.toString('binary'))
  481. ])
  482. ])
  483. ])
  484. ]),
  485. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, [
  486. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, pub_key_auth.toString('binary'))
  487. ]),
  488. ]);
  489. // Serialize an ASN.1 object to DER format
  490. return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
  491. }
  492. function read_challenge_message(ntlm, derBuffer) {
  493. //console.log('ntlm.negotiate_message', ntlm.negotiate_message.toString('hex'));
  494. //ntlm.negotiate_message = Buffer.from('4e544c4d53535000010000003582086000000000000000000000000000000000', 'hex');
  495. // ********
  496. //ntlm.exported_session_key = Buffer.from('9a1ed052e932834a311daf90c2750219', 'hex'); // *************************
  497. //derBuffer = Buffer.from('4e544c4d53535000020000000e000e003800000035828a6259312ef59a4517dd000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c00070008007b7b3bee9e5ad80100000000', 'hex');
  498. //console.log("YST: read_challenge_message1: ", derBuffer.toString('hex'));
  499. const headerSignature = derBuffer.slice(0, 8);
  500. if (headerSignature.toString('hex') != '4e544c4d53535000') { console.log('BAD SIGNATURE'); }
  501. const messageType = derBuffer.readInt32LE(8);
  502. if (messageType != 2) { console.log('BAD MESSAGE TYPE'); }
  503. const targetNameLen = derBuffer.readInt16LE(12);
  504. const targetNameLenMax = derBuffer.readInt16LE(14);
  505. const targetNameBufferOffset = derBuffer.readInt32LE(16);
  506. const negotiateFlags = derBuffer.readInt32LE(20);
  507. const serverChallenge = derBuffer.slice(24, 32);
  508. const reserved = derBuffer.slice(32, 40);
  509. if (reserved.toString('hex') != '0000000000000000') { console.log('BAD RESERVED'); }
  510. const targetInfoLen = derBuffer.readInt16LE(40);
  511. const targetInfoLenMax = derBuffer.readInt16LE(42);
  512. const targetInfoBufferOffset = derBuffer.readInt32LE(44);
  513. const targetName = derBuffer.slice(targetNameBufferOffset, targetNameBufferOffset + targetNameLen);
  514. const targetInfoBuf = derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen);
  515. const targetInfo = decodeTargetInfo(derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen));
  516. const timestamp = targetInfo[7];
  517. //const timestamp = Buffer.from('7b7b3bee9e5ad801', 'hex'); // **************
  518. if (timestamp == null) { console.log('NO TIMESTAMP'); }
  519. const clientChallenge = crypto.randomBytes(8);
  520. //const clientChallenge = Buffer.from('10aac9679ef64e66', 'hex'); // *****************************
  521. const response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
  522. const response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
  523. //console.log("YST: target_name:", targetInfoBuf.toString('hex'));
  524. //console.log("YST: timestamp:", timestamp.toString('hex'));
  525. //console.log('YST: client_challenge:', clientChallenge.toString('hex'));
  526. //console.log("YST: response_key_nt:", response_key_nt.toString('hex'));
  527. //console.log("YST: response_key_lm:", response_key_lm.toString('hex'));
  528. var resp = compute_response_v2(response_key_nt, response_key_lm, serverChallenge, clientChallenge, timestamp, targetInfoBuf);
  529. const nt_challenge_response = resp[0];
  530. const lm_challenge_response = resp[1];
  531. const session_base_key = resp[2];
  532. //console.log('YST: nt_challenge_response:', nt_challenge_response.toString('hex'));
  533. //console.log('YST: lm_challenge_response:', lm_challenge_response.toString('hex'));
  534. //console.log("YST: session_base_key:", session_base_key.toString('hex'));
  535. const key_exchange_key = kx_key_v2(session_base_key, lm_challenge_response, serverChallenge);
  536. const encrypted_random_session_key = rc4k(key_exchange_key, ntlm.exported_session_key);
  537. //console.log("YST: key_exchange_key:", key_exchange_key.toString('hex'));
  538. //console.log("YST: self.exported_session_key:", ntlm.exported_session_key.toString('hex'));
  539. //console.log("YST: encrypted_random_session_key:", encrypted_random_session_key.toString('hex'));
  540. ntlm.is_unicode = ((negotiateFlags & 1) != 0)
  541. //console.log("YST: self.is_unicode: {}", ntlm.is_unicode);
  542. var xdomain = null;
  543. var xuser = null;
  544. if (ntlm.is_unicode) {
  545. xdomain = toUnicode(ntlm.domain);
  546. xuser = toUnicode(ntlm.user);
  547. } else {
  548. xdomain = Buffer.from(ntlm.domain, 'utf8');
  549. xuser = Buffer.from(ntlm.user, 'utf8');
  550. }
  551. //console.log("YST: domain:", xdomain.toString('hex'));
  552. //console.log("YST: user:", xuser.toString('hex'));
  553. const auth_message_compute = authenticate_message(lm_challenge_response, nt_challenge_response, xdomain, xuser, zeroBuffer(0), encrypted_random_session_key, negotiateFlags);
  554. // Write a tmp message to compute MIC and then include it into final message
  555. const tmp_final_auth_message = Buffer.concat([auth_message_compute[0], zeroBuffer(16), auth_message_compute[1]]);
  556. //console.log("YST: tmp_final_auth_message: {}", tmp_final_auth_message.toString('hex'));
  557. const signature = mic(ntlm.exported_session_key, ntlm.negotiate_message, derBuffer, tmp_final_auth_message);
  558. //console.log("YST: signature: {}", signature.toString('hex'));
  559. const r = Buffer.concat([auth_message_compute[0], signature, auth_message_compute[1]]);
  560. //console.log("YST: read_challenge_message2: {}", r.toString('hex'));
  561. return r;
  562. }
  563. function unitTest() {
  564. console.log('--- Starting RDP NLA Unit Tests');
  565. // Test format of the first client message
  566. var r = create_negotiate_message();
  567. 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.");
  568. // Test of MD4 hash function
  569. r = md4(Buffer.from("foo"));
  570. 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.");
  571. // Test of the unicode function
  572. r = toUnicode("foo");
  573. console.log(compareArray(bufToArr(r), [0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00]) ? "Unicode passed." : "Unicode failed.");
  574. // Test HMAC_MD5 function
  575. r = hmac_md5(Buffer.from("foo"), Buffer.from("bar"));
  576. 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.");
  577. // Test NTOWFv2 function
  578. r = ntowfv2("foo", "user", "domain");
  579. 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.");
  580. // Test LMOWFv2 function
  581. r = ntowfv2("foo", "user", "domain");
  582. console.log(compareArray(bufToArr(r), ntowfv2("foo", "user", "domain")) ? "LMOWFv2 passed." : "LMOWFv2 failed.");
  583. // Test compute response v2 function
  584. r = compute_response_v2(Buffer.from("a"), Buffer.from("b"), Buffer.from("c"), Buffer.from("d"), Buffer.from("e"), Buffer.from("f"));
  585. 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.");
  586. 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.");
  587. 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.");
  588. // Test of rc4k function
  589. r = rc4k(Buffer.from("foo"), Buffer.from("bar"));
  590. console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "rc4k passed." : "rc4k failed.");
  591. // Test of sign_key function
  592. r = sign_key(Buffer.from("foo"), true);
  593. 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.");
  594. r = sign_key(Buffer.from("foo"), false);
  595. 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.");
  596. // Test of seal_key function
  597. r = seal_key(Buffer.from("foo"), true);
  598. 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.");
  599. r = seal_key(Buffer.from("foo"), false);
  600. 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.");
  601. // Test signature function
  602. var rc4 = createRC4(Buffer.from("foo"));
  603. r = mac(rc4, Buffer.from("bar"), 0, Buffer.from("data"));
  604. 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.");
  605. // Test challenge message
  606. r = authenticate_message(Buffer.from("foo"), Buffer.from("foo"), Buffer.from("domain"), Buffer.from("user"), Buffer.from("workstation"), Buffer.from("foo"), 0);
  607. var buf = Buffer.concat([r[0], Buffer.alloc(16), r[1]]);
  608. 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.");
  609. // Test RC4
  610. rc4 = createRC4(Buffer.from("foo"));
  611. r = rc4.update(Buffer.from("bar"));
  612. console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "RC4 1 passed." : "RC4 1 failed.");
  613. r = rc4.update(Buffer.from("bar"));
  614. console.log(compareArray(bufToArr(r), [75, 169, 19]) ? "RC4 2 passed." : "RC4 2 failed.");
  615. // Test create_ts_authenticate
  616. r = create_ts_authenticate(Buffer.from("000102", 'hex'), Buffer.from("000102", 'hex'));
  617. 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.");
  618. // Test test_create_ts_credentials
  619. r = create_ts_credentials(Buffer.from("domain"), Buffer.from("user"), Buffer.from("password"));
  620. 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.");
  621. // Test create_ts_authinfo
  622. r = create_ts_authinfo(Buffer.from("foo"));
  623. 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.");
  624. console.log('--- RDP NLA Unit Tests Completed');
  625. }