var crypto = require('crypto'); var flags = { NTLM_NegotiateUnicode : 0x00000001, NTLM_NegotiateOEM : 0x00000002, NTLM_RequestTarget : 0x00000004, NTLM_Unknown9 : 0x00000008, NTLM_NegotiateSign : 0x00000010, NTLM_NegotiateSeal : 0x00000020, NTLM_NegotiateDatagram : 0x00000040, NTLM_NegotiateLanManagerKey : 0x00000080, NTLM_Unknown8 : 0x00000100, NTLM_NegotiateNTLM : 0x00000200, NTLM_NegotiateNTOnly : 0x00000400, NTLM_Anonymous : 0x00000800, NTLM_NegotiateOemDomainSupplied : 0x00001000, NTLM_NegotiateOemWorkstationSupplied : 0x00002000, NTLM_Unknown6 : 0x00004000, NTLM_NegotiateAlwaysSign : 0x00008000, NTLM_TargetTypeDomain : 0x00010000, NTLM_TargetTypeServer : 0x00020000, NTLM_TargetTypeShare : 0x00040000, NTLM_NegotiateExtendedSecurity : 0x00080000, NTLM_NegotiateIdentify : 0x00100000, NTLM_Unknown5 : 0x00200000, NTLM_RequestNonNTSessionKey : 0x00400000, NTLM_NegotiateTargetInfo : 0x00800000, NTLM_Unknown4 : 0x01000000, NTLM_NegotiateVersion : 0x02000000, NTLM_Unknown3 : 0x04000000, NTLM_Unknown2 : 0x08000000, NTLM_Unknown1 : 0x10000000, NTLM_Negotiate128 : 0x20000000, NTLM_NegotiateKeyExchange : 0x40000000, NTLM_Negotiate56 : 0x80000000 }; var typeflags = { NTLM_TYPE1_FLAGS : flags.NTLM_NegotiateUnicode + flags.NTLM_NegotiateOEM + flags.NTLM_RequestTarget + flags.NTLM_NegotiateNTLM + flags.NTLM_NegotiateOemDomainSupplied + flags.NTLM_NegotiateOemWorkstationSupplied + flags.NTLM_NegotiateAlwaysSign + flags.NTLM_NegotiateExtendedSecurity + flags.NTLM_NegotiateVersion + flags.NTLM_Negotiate128 + flags.NTLM_Negotiate56, NTLM_TYPE2_FLAGS : flags.NTLM_NegotiateUnicode + flags.NTLM_RequestTarget + flags.NTLM_NegotiateNTLM + flags.NTLM_NegotiateAlwaysSign + flags.NTLM_NegotiateExtendedSecurity + flags.NTLM_NegotiateTargetInfo + flags.NTLM_NegotiateVersion + flags.NTLM_Negotiate128 + flags.NTLM_Negotiate56 }; function createType1Message(options){ var domain = escape(options.domain.toUpperCase()); var workstation = escape(options.workstation.toUpperCase()); var protocol = 'NTLMSSP\0'; var BODY_LENGTH = 40; var type1flags = typeflags.NTLM_TYPE1_FLAGS; if(!domain || domain === '') type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied; var pos = 0; var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length); buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol buf.writeUInt32LE(1, pos); pos += 4; // type 1 buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset buf.writeUInt8(5, pos); pos += 1; //ProductMajorVersion buf.writeUInt8(1, pos); pos += 1; //ProductMinorVersion buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2 buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3 buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string buf.write(domain , pos, domain.length , 'ascii'); pos += domain.length; return 'NTLM ' + buf.toString('base64'); } function parseType2Message(rawmsg, callback){ var match = rawmsg.match(/NTLM (.+)?/); if(!match || !match[1]) return callback(new Error("Couldn't find NTLM in the message type2 comming from the server")); var buf = new Buffer(match[1], 'base64'); var msg = {}; msg.signature = buf.slice(0, 8); msg.type = buf.readInt16LE(8); if(msg.type != 2) return callback(new Error("Server didn't return a type 2 message")); msg.targetNameLen = buf.readInt16LE(12); msg.targetNameMaxLen = buf.readInt16LE(14); msg.targetNameOffset = buf.readInt32LE(16); msg.targetName = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen); msg.negotiateFlags = buf.readInt32LE(20); msg.serverChallenge = buf.slice(24, 32); msg.reserved = buf.slice(32, 40); if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){ msg.targetInfoLen = buf.readInt16LE(40); msg.targetInfoMaxLen = buf.readInt16LE(42); msg.targetInfoOffset = buf.readInt32LE(44); msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen); } return msg; } function createType3Message(msg2, options){ var nonce = msg2.serverChallenge; var username = options.username; var password = options.password; var negotiateFlags = msg2.negotiateFlags; var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode; var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity; var BODY_LENGTH = 72; var domainName = escape(options.domain.toUpperCase()); var workstation = escape(options.workstation.toUpperCase()); var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes; var encryptedRandomSessionKey = ""; if(isUnicode){ workstationBytes = new Buffer(workstation, 'utf16le'); domainNameBytes = new Buffer(domainName, 'utf16le'); usernameBytes = new Buffer(username, 'utf16le'); encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le'); }else{ workstationBytes = new Buffer(workstation, 'ascii'); domainNameBytes = new Buffer(domainName, 'ascii'); usernameBytes = new Buffer(username, 'ascii'); encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii'); } var lmChallengeResponse = calc_resp(create_LM_hashed_password_v1(password), nonce); var ntChallengeResponse = calc_resp(create_NT_hashed_password_v1(password), nonce); if(isNegotiateExtendedSecurity){ var pwhash = create_NT_hashed_password_v1(password); var clientChallenge = ""; for(var i=0; i < 8; i++){ clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) ); } var clientChallengeBytes = new Buffer(clientChallenge, 'ascii'); var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes); lmChallengeResponse = challenges.lmChallengeResponse; ntChallengeResponse = challenges.ntChallengeResponse; } var signature = 'NTLMSSP\0'; var pos = 0; var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length); buf.write(signature, pos, signature.length); pos += signature.length; buf.writeUInt32LE(3, pos); pos += 4; // type 1 buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // DomainNameOffset buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags buf.writeUInt8(5, pos); pos++; // ProductMajorVersion buf.writeUInt8(1, pos); pos++; // ProductMinorVersion buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild buf.writeUInt8(0, pos); pos++; // VersionReserved1 buf.writeUInt8(0, pos); pos++; // VersionReserved2 buf.writeUInt8(0, pos); pos++; // VersionReserved3 buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent domainNameBytes.copy(buf, pos); pos += domainNameBytes.length; usernameBytes.copy(buf, pos); pos += usernameBytes.length; workstationBytes.copy(buf, pos); pos += workstationBytes.length; lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length; ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length; encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length; return 'NTLM ' + buf.toString('base64'); } function create_LM_hashed_password_v1(password){ // fix the password length to 14 bytes password = password.toUpperCase(); var passwordBytes = new Buffer(password, 'ascii'); var passwordBytesPadded = new Buffer(14); passwordBytesPadded.fill("\0"); var sourceEnd = 14; if(passwordBytes.length < 14) sourceEnd = passwordBytes.length; passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd); // split into 2 parts of 7 bytes: var firstPart = passwordBytesPadded.slice(0,7); var secondPart = passwordBytesPadded.slice(7); function encrypt(buf){ var key = insertZerosEvery7Bits(buf); var des = crypto.createCipheriv('DES-ECB', key, ''); return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]); } var firstPartEncrypted = encrypt(firstPart); var secondPartEncrypted = encrypt(secondPart); return Buffer.concat([firstPartEncrypted, secondPartEncrypted]); } function insertZerosEvery7Bits(buf){ var binaryArray = bytes2binaryArray(buf); var newBinaryArray = []; for(var i=0; i array.length) break; var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3]; var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7]; var hexchar1 = binary2hex[binString1]; var hexchar2 = binary2hex[binString2]; var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex'); bufArray.push(buf); } return Buffer.concat(bufArray); } function create_NT_hashed_password_v1(password){ var buf = new Buffer(password, 'utf16le'); var md4 = crypto.createHash('md4'); md4.update(buf); return new Buffer(md4.digest()); } function calc_resp(password_hash, server_challenge){ // padding with zeros to make the hash 21 bytes long var passHashPadded = new Buffer(21); passHashPadded.fill("\0"); password_hash.copy(passHashPadded, 0, 0, password_hash.length); var resArray = []; var des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), ''); resArray.push( des.update(server_challenge.slice(0,8)) ); des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), ''); resArray.push( des.update(server_challenge.slice(0,8)) ); des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), ''); resArray.push( des.update(server_challenge.slice(0,8)) ); return Buffer.concat(resArray); } function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){ // padding with zeros to make the hash 16 bytes longer var lmChallengeResponse = new Buffer(clientChallenge.length + 16); lmChallengeResponse.fill("\0"); clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length); var buf = Buffer.concat([serverChallenge, clientChallenge]); var md5 = crypto.createHash('md5'); md5.update(buf); var sess = md5.digest(); var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8)); return { lmChallengeResponse: lmChallengeResponse, ntChallengeResponse: ntChallengeResponse }; } exports.createType1Message = createType1Message; exports.parseType2Message = parseType2Message; exports.createType3Message = createType3Message;