@awo00/smb2
Version:
A SMB2 implementation in TypeScript
346 lines (345 loc) • 13.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateServerChallenge = exports.encodeAuthenticationMessage = exports.decodeChallengeMessage = exports.encodeChallengeMessage = exports.decodeNegotiationMessage = exports.encodeNegotiationMessage = void 0;
const crypto_1 = __importDefault(require("crypto"));
const des_js_1 = __importDefault(require("des.js"));
const js_md4_1 = __importDefault(require("js-md4"));
const NegotiateFlag_1 = __importDefault(require("./NegotiateFlag"));
const encodeNegotiationMessage = (hostname, domain) => {
hostname = hostname.toUpperCase();
domain = domain.toUpperCase();
const hostnameLength = Buffer.byteLength(hostname, "ascii");
const domainLength = Buffer.byteLength(domain, "ascii");
let offset = 0;
const buffer = Buffer.alloc(32 + hostnameLength + domainLength);
buffer.write("NTLMSSP", offset, 7, "ascii");
offset += 7;
buffer.writeUInt8(0, offset);
offset += 1;
buffer.writeUInt32LE(1, offset);
offset += 4;
const negotiateFlags = NegotiateFlag_1.default.UnicodeEncoding | NegotiateFlag_1.default.NTLMSessionSecurity | NegotiateFlag_1.default.AlwaysSign;
buffer.writeUInt32LE(negotiateFlags, offset);
offset += 4;
buffer.writeUInt16LE(domainLength, offset);
offset += 2;
buffer.writeUInt16LE(domainLength, offset);
offset += 2;
const domainOffset = 0x20 + hostnameLength;
buffer.writeUInt32LE(domainOffset, offset);
offset += 4;
buffer.writeUInt16LE(hostnameLength, offset);
offset += 2;
buffer.writeUInt16LE(hostnameLength, offset);
offset += 2;
buffer.writeUInt32LE(0x20, offset);
offset += 4;
buffer.write(hostname, 0x20, hostnameLength, "ascii");
buffer.write(domain, domainOffset, domainLength, "ascii");
return buffer;
};
exports.encodeNegotiationMessage = encodeNegotiationMessage;
const decodeNegotiationMessage = (buffer) => {
let offset = 0;
const protocol = buffer.slice(0, 7).toString("ascii");
if (protocol !== "NTLMSSP" ||
buffer.readInt8(7) !== 0x00)
throw new Error("ntlmssp_header_not_found");
offset += 8;
const type = buffer.readUInt32LE(offset);
if (type !== 0x01)
throw new Error("ntlmssp_type_is_not_one");
offset += 4;
const negotiateFlags = buffer.readUInt32LE(offset);
offset += 4;
const domainLength = buffer.readUInt16LE(offset);
offset += 2;
const domainMaxLength = buffer.readUInt16LE(offset);
offset += 2;
const domainOffset = buffer.readUInt32LE(offset);
offset += 4;
const hostnameLength = buffer.readUInt16LE(offset);
offset += 2;
const hostnameMaxLength = buffer.readUInt16LE(offset);
offset += 2;
const hostnameOffset = buffer.readUInt32LE(offset);
offset += 4;
const domain = buffer.slice(domainOffset, domainOffset + domainLength).toString("ascii");
const hostname = buffer.slice(hostnameOffset, hostnameOffset + hostnameLength).toString("ascii");
return {
negotiateFlags,
domain,
hostname
};
};
exports.decodeNegotiationMessage = decodeNegotiationMessage;
const encodeChallengeMessage = (negotiateFlags) => {
let offset = 0;
const buffer = Buffer.alloc(64);
buffer.write("NTLMSSP", offset, 7, "ascii");
offset += 7;
buffer.writeUInt8(0, offset);
offset += 1;
buffer.writeUInt32LE(2, offset);
offset += 4;
buffer.writeUInt16LE(0, offset);
offset += 2;
buffer.writeUInt16LE(0, offset);
offset += 2;
buffer.writeUInt32LE(0, offset);
offset += 4;
buffer.writeUInt32LE(negotiateFlags, offset);
offset += 4;
(0, exports.generateServerChallenge)().copy(buffer, offset);
offset += 8;
buffer.fill(0, offset, offset + 8);
offset += 8;
return buffer;
};
exports.encodeChallengeMessage = encodeChallengeMessage;
const decodeChallengeMessage = (buffer) => {
let offset = 0;
const protocol = buffer.slice(0, 7).toString("ascii");
if (protocol !== "NTLMSSP" ||
buffer.readInt8(7) !== 0x00)
throw new Error("ntlmssp_header_not_found");
offset += 8;
const type = buffer.readUInt32LE(offset);
if (type !== 0x02)
throw new Error("ntlmssp_type_is_not_two");
offset += 4;
const targetNameLength = buffer.readUInt16LE(offset);
offset += 2;
const targetNameMaxLength = buffer.readUInt16LE(offset);
offset += 2;
const targetNameOffset = buffer.readUInt32LE(offset);
offset += 4;
const negotiateFlags = buffer.readUInt32LE(offset);
offset += 4;
const serverChallenge = buffer.slice(offset, offset + 8);
offset += 8;
offset += 8; // Reserved
return serverChallenge;
};
exports.decodeChallengeMessage = decodeChallengeMessage;
const encodeAuthenticationMessage = (username, hostname, domain, nonce, password) => {
hostname = hostname.toUpperCase();
domain = domain.toUpperCase();
const lmHash = Buffer.alloc(21);
createLmHash(password).copy(lmHash);
lmHash.fill(0x00, 16);
const ntHash = Buffer.alloc(21);
createNtHash(password).copy(ntHash);
ntHash.fill(0x00, 16);
const lmResponse = createResponse(lmHash, nonce);
const ntResponse = createResponse(ntHash, nonce);
const usernameLength = Buffer.byteLength(username, "ucs2");
const hostnameLength = Buffer.byteLength(hostname, "ucs2");
const domainLength = Buffer.byteLength(domain, "ucs2");
const lmResponseLength = 0x18;
const ntResponseLength = 0x18;
const domainOffset = 0x40;
const usernameOffset = domainOffset + domainLength;
const hostnameOffset = usernameOffset + usernameLength;
const lmResponseOffset = hostnameOffset + hostnameLength;
const ntResponseOffset = lmResponseOffset + lmResponseLength;
let offset = 0;
const messageLength = 64 + domainLength + usernameLength + hostnameLength + lmResponseLength + ntResponseLength;
const buffer = Buffer.alloc(messageLength);
buffer.write("NTLMSSP", offset, 7, "ascii"); // byte protocol[8];
offset += 7;
buffer.writeUInt8(0, offset);
offset++;
buffer.writeUInt8(0x03, offset); // byte type;
offset++;
buffer.fill(0x00, offset, offset + 3); // byte zero[3];
offset += 3;
buffer.writeUInt16LE(lmResponseLength, offset); // short lm_resp_len;
offset += 2;
buffer.writeUInt16LE(lmResponseLength, offset); // short lm_resp_len;
offset += 2;
buffer.writeUInt16LE(lmResponseOffset, offset); // short lm_resp_off;
offset += 2;
buffer.fill(0x00, offset, offset + 2); // byte zero[2];
offset += 2;
buffer.writeUInt16LE(ntResponseLength, offset); // short nt_resp_len;
offset += 2;
buffer.writeUInt16LE(ntResponseLength, offset); // short nt_resp_len;
offset += 2;
buffer.writeUInt16LE(ntResponseOffset, offset); // short nt_resp_off;
offset += 2;
buffer.fill(0x00, offset, offset + 2); // byte zero[2];
offset += 2;
buffer.writeUInt16LE(domainLength, offset); // short dom_len;
offset += 2;
buffer.writeUInt16LE(domainLength, offset); // short dom_len;
offset += 2;
buffer.writeUInt16LE(domainOffset, offset); // short dom_off;
offset += 2;
buffer.fill(0x00, offset, offset + 2); // byte zero[2];
offset += 2;
buffer.writeUInt16LE(usernameLength, offset); // short user_len;
offset += 2;
buffer.writeUInt16LE(usernameLength, offset); // short user_len;
offset += 2;
buffer.writeUInt16LE(usernameOffset, offset); // short user_off;
offset += 2;
buffer.fill(0x00, offset, offset + 2); // byte zero[2];
offset += 2;
buffer.writeUInt16LE(hostnameLength, offset); // short host_len;
offset += 2;
buffer.writeUInt16LE(hostnameLength, offset); // short host_len;
offset += 2;
buffer.writeUInt16LE(hostnameOffset, offset); // short host_off;
offset += 2;
buffer.fill(0x00, offset, offset + 6); // byte zero[6];
offset += 6;
buffer.writeUInt16LE(messageLength, offset); // short msg_len;
offset += 2;
buffer.fill(0x00, offset, offset + 2); // byte zero[2];
offset += 2;
const negotiateFlags = NegotiateFlag_1.default.UnicodeEncoding | NegotiateFlag_1.default.NTLMSessionSecurity | NegotiateFlag_1.default.AlwaysSign;
buffer.writeUInt32LE(negotiateFlags, offset);
offset += 4;
buffer.write(domain, domainOffset, domainLength, "ucs2");
buffer.write(username, usernameOffset, usernameLength, "ucs2");
buffer.write(hostname, hostnameOffset, hostnameLength, "ucs2");
lmResponse.copy(buffer, lmResponseOffset, 0, lmResponseLength);
ntResponse.copy(buffer, ntResponseOffset, 0, ntResponseLength);
return buffer;
};
exports.encodeAuthenticationMessage = encodeAuthenticationMessage;
const generateServerChallenge = () => {
return crypto_1.default.randomBytes(8);
};
exports.generateServerChallenge = generateServerChallenge;
const bytes2binaryArray = (buf) => {
const hex2binary = {
0: [0, 0, 0, 0],
1: [0, 0, 0, 1],
2: [0, 0, 1, 0],
3: [0, 0, 1, 1],
4: [0, 1, 0, 0],
5: [0, 1, 0, 1],
6: [0, 1, 1, 0],
7: [0, 1, 1, 1],
8: [1, 0, 0, 0],
9: [1, 0, 0, 1],
A: [1, 0, 1, 0],
B: [1, 0, 1, 1],
C: [1, 1, 0, 0],
D: [1, 1, 0, 1],
E: [1, 1, 1, 0],
F: [1, 1, 1, 1],
};
const hexString = buf.toString("hex").toUpperCase();
let array = [];
for (let i = 0; i < hexString.length; i++) {
const hexchar = hexString.charAt(i);
array = array.concat(hex2binary[hexchar]);
}
return array;
};
const binaryArray2bytes = (array) => {
const binary2hex = {
"0000": 0,
"0001": 1,
"0010": 2,
"0011": 3,
"0100": 4,
"0101": 5,
"0110": 6,
"0111": 7,
"1000": 8,
"1001": 9,
"1010": "A",
"1011": "B",
"1100": "C",
"1101": "D",
"1110": "E",
"1111": "F",
};
const bufArray = [];
for (let i = 0; i < array.length; i += 8) {
if (i + 7 > array.length)
break;
const binString1 = "" + array[i] + "" + array[i + 1] + "" + array[i + 2] + "" + array[i + 3];
const binString2 = "" +
array[i + 4] +
"" +
array[i + 5] +
"" +
array[i + 6] +
"" +
array[i + 7];
const hexchar1 = binary2hex[binString1];
const hexchar2 = binary2hex[binString2];
const buf = Buffer.from(hexchar1 + "" + hexchar2, "hex");
bufArray.push(buf);
}
return Buffer.concat(bufArray);
};
const insertZerosEvery7Bits = (buf) => {
const binaryArray = bytes2binaryArray(buf);
const newBinaryArray = [];
for (let i = 0; i < binaryArray.length; i++) {
newBinaryArray.push(binaryArray[i]);
if ((i + 1) % 7 === 0) {
newBinaryArray.push(0);
}
}
return binaryArray2bytes(newBinaryArray);
};
const createLmHash = (password) => {
password = password.toUpperCase();
const passwordBytes = Buffer.from(password, "ascii");
const passwordBytesPadded = Buffer.alloc(14);
passwordBytesPadded.fill("\0");
let sourceEnd = 14;
if (passwordBytes.length < 14)
sourceEnd = passwordBytes.length;
passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd);
const firstPart = passwordBytesPadded.slice(0, 7);
const secondPart = passwordBytesPadded.slice(7);
function encrypt(buf) {
const key = insertZerosEvery7Bits(buf);
const des = des_js_1.default.DES.create({ type: "encrypt", key });
const magicKey = Buffer.from("KGS!@#$%", "ascii");
const encrypted = des.update(magicKey);
return Buffer.from(encrypted);
}
const firstPartEncrypted = encrypt(firstPart);
const secondPartEncrypted = encrypt(secondPart);
return Buffer.concat([firstPartEncrypted, secondPartEncrypted]);
};
const createNtHash = (password) => {
const buf = Buffer.from(password, "utf16le");
const md4 = js_md4_1.default.create();
md4.update(buf);
return Buffer.from(md4.digest());
};
const createResponse = (hash, nonce) => {
const passHashPadded = Buffer.alloc(21);
passHashPadded.fill("\0");
hash.copy(passHashPadded, 0, 0, hash.length);
const resArray = [];
const des1 = des_js_1.default.DES.create({
type: "encrypt",
key: insertZerosEvery7Bits(passHashPadded.slice(0, 7)),
});
resArray.push(Buffer.from(des1.update(nonce.slice(0, 8))));
const des2 = des_js_1.default.DES.create({
type: "encrypt",
key: insertZerosEvery7Bits(passHashPadded.slice(7, 14)),
});
resArray.push(Buffer.from(des2.update(nonce.slice(0, 8))));
const des3 = des_js_1.default.DES.create({
type: "encrypt",
key: insertZerosEvery7Bits(passHashPadded.slice(14, 21)),
});
resArray.push(Buffer.from(des3.update(nonce.slice(0, 8))));
return Buffer.concat(resArray);
};