Skip to content

Instantly share code, notes, and snippets.

@mwarner1
Last active February 19, 2019 21:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwarner1/4e6c9a06638302328e54aa7aca1c5477 to your computer and use it in GitHub Desktop.
Save mwarner1/4e6c9a06638302328e54aa7aca1c5477 to your computer and use it in GitHub Desktop.
Null shared secret allows brute force success with tacacs-plus library
'use strict';
const net = require('net');
const crypto = require('crypto');
const tacacs = require('tacacs-plus');
const AuthResult = Object.freeze({
Success: 'Success',
Failure: 'Failure'
});
const checkCredentials = (username, password) => {
return new Promise((resolve, reject) => {
let authResult = AuthResult.Failure;
let authMessage = null;
const shared_secret = null; // THIS SEEMS TO BE THE TRIGGER
const client = net.connect({port: 49, host: '192.168.30.77'}, function () {
console.log('Client connected!');
// now that we've connected, send the first auth packet
const sessionIdBytes = crypto.randomBytes(4);
const sessionId = Math.abs(sessionIdBytes.readInt32BE(0));
// create the auth start body
const authStart = tacacs.createAuthStart({
action: tacacs.TAC_PLUS_AUTHEN_LOGIN,
privLvl: tacacs.TAC_PLUS_PRIV_LVL_USER,
authenType: tacacs.TAC_PLUS_AUTHEN_TYPE_ASCII,
authenService: tacacs.TAC_PLUS_AUTHEN_SVC_LOGIN,
user: '',
port: '',
remAddr: '',
data: null
});
const version = tacacs.createVersion(tacacs.TAC_PLUS_MAJOR_VER, tacacs.TAC_PLUS_MINOR_VER_DEFAULT);
const sequenceNumber = 1;
const encryptedAuthStart = tacacs.encodeByteData(sessionId, shared_secret, version, sequenceNumber, authStart);
// create the tacacs+ header
const headerOptions = {
majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
type: tacacs.TAC_PLUS_AUTHEN,
sequenceNumber: sequenceNumber,
flags: tacacs.TAC_PLUS_SINGLE_CONNECT_FLAG, // setting this to zero assumes encryption is being used -- | tacacs.TAC_PLUS_UNENCRYPTED_FLAG
sessionId: sessionId,
length: authStart.length
};
const header = tacacs.createHeader(headerOptions);
const packetToSend = Buffer.concat([header, encryptedAuthStart]);
// send the auth start packet to the server
client.write(packetToSend);
});
client.on('error', function (err) {
console.log(err);
reject(err);
});
client.on('close', function (had_err) {
if (had_err) {
reject(had_err);
}
resolve({status: authResult, msg: authMessage});
});
client.on('data', function (data) {
if (data) {
// decode response
const resp = tacacs.decodePacket({packet: data, key: shared_secret});
if (resp) {
if (resp.data.status === tacacs.TAC_PLUS_AUTHEN_STATUS_ERROR) {
client.end();
authMessage = "Authentication error";
} else if (resp.data.status === tacacs.TAC_PLUS_AUTHEN_STATUS_FAIL) {
client.end();
authMessage = "Authentication Failed";
} else if (resp.data.status === tacacs.TAC_PLUS_AUTHEN_STATUS_GETUSER) {
const newSeq = resp.header.sequenceNumber + 1;
const tRespOptions = {
flags: 0x00,
userMessage: username,
data: null
};
const tContinue = tacacs.createAuthContinue(tRespOptions);
const encryptedContinue = tacacs.encodeByteData(resp.header.sessionId, shared_secret, resp.header.versionByte, newSeq, tContinue);
const tRespHeader = {
majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
type: tacacs.TAC_PLUS_AUTHEN,
sequenceNumber: newSeq,
flags: resp.header.flags,
sessionId: resp.header.sessionId,
length: encryptedContinue.length
};
const header = tacacs.createHeader(tRespHeader);
const packetToSend = Buffer.concat([header, encryptedContinue]);
client.write(packetToSend);
} else if (resp.data.status === tacacs.TAC_PLUS_AUTHEN_STATUS_GETPASS) {
const newSeq = resp.header.sequenceNumber + 1;
const tRespOptions = {
flags: 0x00,
userMessage: password,
data: null
};
const tContinue = tacacs.createAuthContinue(tRespOptions);
const encryptedContinue = tacacs.encodeByteData(resp.header.sessionId, shared_secret, resp.header.versionByte, newSeq, tContinue);
const tRespHeader = {
majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
type: tacacs.TAC_PLUS_AUTHEN,
sequenceNumber: newSeq,
flags: resp.header.flags,
sessionId: resp.header.sessionId,
length: encryptedContinue.length
};
const header = tacacs.createHeader(tRespHeader);
const packetToSend = Buffer.concat([header, encryptedContinue]);
client.write(packetToSend);
} else if (resp.data.status === tacacs.TAC_PLUS_AUTHEN_STATUS_PASS) {
client.end();
authMessage = "Success";
authResult = AuthResult.Success;
} else {
const newSeq = resp.header.sequenceNumber + 1;
const tRespOptions = {
flags: tacacs.TAC_PLUS_CONTINUE_FLAG_ABORT,
userMessage: null,
data: null
};
const tContinue = tacacs.createAuthContinue(tRespOptions);
const encryptedContinue = tacacs.encodeByteData(resp.header.sessionId, shared_secret, resp.header.versionByte, newSeq, tContinue);
const tRespHeader = {
majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
type: tacacs.TAC_PLUS_AUTHEN,
sequenceNumber: newSeq,
flags: resp.header.flags,
sessionId: resp.header.sessionId,
length: encryptedContinue.length
};
const header = tacacs.createHeader(tRespHeader);
const packetToSend = Buffer.concat([header, encryptedContinue]);
client.write(packetToSend);
client.end();
authMessage = "General failure";
}
}
} else {
reject('Client: No data!');
}
});
});
};
const doMain = async () => {
let promises = [];
for (let x = 0; x < 20; x++) {
promises.push(checkCredentials('admin', 'badpass').then(result => {
console.log(result);
}));
}
await Promise.all(promises);
};
doMain();
@mwarner1
Copy link
Author

Output shows two successes. With a null shared_secret, the code is not actually talking to tac_plus

node index.js
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
Client connected!
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'Authentication Failed' }
{ status: 'Failure', msg: null }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: null }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Failure', msg: null }
{ status: 'Failure', msg: 'Authentication Failed' }
{ status: 'Failure', msg: null }
{ status: 'Failure', msg: 'General failure' }
{ status: 'Success', msg: 'Success' }
{ status: 'Success', msg: 'Success' }
{ status: 'Failure', msg: 'Authentication error' }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment