UNPKG

node-ntlm-client

Version:

A node.js NTLM client with support for NTLM and NTLMv2 authentication

171 lines (128 loc) 4.46 kB
'use strict'; const crypto = require('crypto'); function createLMResponse(challenge, lmhash) { let buf = new Buffer(24), pwBuffer = new Buffer(21).fill(0); lmhash.copy(pwBuffer); calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf); calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8); calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16); return buf; } function createLMHash(password) { let buf = new Buffer(16), pwBuffer = new Buffer(14), magicKey = new Buffer('KGS!@#$%', 'ascii'); if (password.length > 14) { buf.fill(0); return buf; } pwBuffer.fill(0); pwBuffer.write(password.toUpperCase(), 0, 'ascii'); return Buffer.concat([ calculateDES(pwBuffer.slice(0, 7), magicKey), calculateDES(pwBuffer.slice(7), magicKey) ]); } function calculateDES(key, message) { let desKey = new Buffer(8); desKey[0] = key[0] & 0xFE; desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1); desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2); desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3); desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4); desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5); desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6); desKey[7] = (key[6] << 1) & 0xFF; for (let i = 0; i < 8; i++) { let parity = 0; for (let j = 1; j < 8; j++) { parity += (desKey[i] >> j) % 2; } desKey[i] |= (parity % 2) === 0 ? 1 : 0; } let des = crypto.createCipheriv('DES-ECB', desKey, ''); return des.update(message); } function createNTLMResponse(challenge, ntlmhash) { let buf = new Buffer(24), ntlmBuffer = new Buffer(21).fill(0); ntlmhash.copy(ntlmBuffer); calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf); calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8); calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16); return buf; } function createNTLMHash(password) { let md4sum = crypto.createHash('md4'); md4sum.update(new Buffer(password, 'ucs2')); return md4sum.digest(); } function createNTLMv2Hash(ntlmhash, username, authTargetName) { let hmac = crypto.createHmac('md5', ntlmhash); hmac.update(new Buffer(username.toUpperCase() + authTargetName, 'ucs2')); return hmac.digest(); } function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) { let buf = new Buffer(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash); //server challenge type2message.challenge.copy(buf, 8); //client nonce buf.write(nonce || createPseudoRandomValue(16), 16, 'hex'); //create hash hmac.update(buf.slice(8)); let hashedBuffer = hmac.digest(); hashedBuffer.copy(buf); return buf; } function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) { let buf = new Buffer(48 + type2message.targetInfo.buffer.length), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash); //the first 8 bytes are spare to store the hashed value before the blob //server challenge type2message.challenge.copy(buf, 8); //blob signature buf.writeUInt32BE(0x01010000, 16); //reserved buf.writeUInt32LE(0, 20); //timestamp //TODO: we are loosing precision here since js is not able to handle those large integers // maybe think about a different solution here // 11644473600000 = diff between 1970 and 1601 let timestamp = ((Date.now() + 11644473600000) * 10000).toString(16); let timestampLow = Number('0x' + timestamp.substring(Math.max(0, timestamp.length - 8))); let timestampHigh = Number('0x' + timestamp.substring(0, Math.max(0, timestamp.length - 8))); buf.writeUInt32LE(timestampLow, 24, false); buf.writeUInt32LE(timestampHigh, 28, false); //random client nonce buf.write(nonce || createPseudoRandomValue(16), 32, 'hex'); //zero buf.writeUInt32LE(0, 40); //complete target information block from type 2 message type2message.targetInfo.buffer.copy(buf, 44); //zero buf.writeUInt32LE(0, 44 + type2message.targetInfo.buffer.length); hmac.update(buf.slice(8)); let hashedBuffer = hmac.digest(); hashedBuffer.copy(buf); return buf; } function createPseudoRandomValue(length) { let str = ''; while (str.length < length) { str += Math.floor(Math.random() * 16).toString(16); } return str; } module.exports = { createLMHash, createNTLMHash, createLMResponse, createNTLMResponse, createLMv2Response, createNTLMv2Response, createPseudoRandomValue };