Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
persistent-shell
Advanced tools
Wrapper class for ssh2 client.shell command.
The package allows for user/program/interface to run commands without disconnecting each time.
npm install persistent-shell
this.host
Is the host object passed to the constructor.
this.commands
(Optional) is array of commands.
Commands are set using host.commands = [commands]
or this.runCommand([commands])
.
Instance = new persistent-shell(host)
requires the host object defined above.
this.connect(callback)
Connects using host.server properties running the callback when finished. Callback is optional.
this.runCommand(command/s)
takes a command or an array of commands. Runs command or first command in array.
callback = function(sessionText){}
Runs after everything has closed allowing you to process the full session.
this.on("pipe",function(writeStream){})
Allows you to bind a write stream to the shell stream.
this.on("unpipe", function(writeStream){})
Runs when a pipe is removed.
this.on("data", function(data){})
Runs every time data is received from the host.
this.on("commandProcessing", function(response){})
Runs with each data event before a prompt is detected.
this.on("commandComplete", function(response){})
Runs when a prompt is detected after a command.
this.on("lastCommand", function(message){})
Indicates there are no commands in the commands array.
this.on("end", function (sessionText){})
Runs when the stream/connection is being closed.
this.on("msg", function(message){})
Output a message but with no carrage return.
this.on("info", function(message){})
Emits msg with carrage return. Host.onInfo
is not available modify msg instead.
this.on("error", function(err, type, close = false, callback){})
Runs when an error occures.
this.on("keyboard-interactive", function(name, instructions, instructionsLang, prompts, finish){})
Keyboard-interactive authentication requires host.server.tryKeyboard to be set.
Persistent-shell expects an object with the following structure to be passed to its constructor:
Note: Any property or event handler with a default value does not need to be added to your host object unless you want to change it.
host = {
server: {
host: "IP Address",
port: "external port number",
userName: "user name",
password: "user password",
passPhrase: "privateKeyPassphrase",
privateKey: require('fs').readFileSync('/path/to/private/key/id_rsa')
//Optional: ssh2.connect config parameters
//See https://github.com/mscdex/ssh2#client-methods
},
commands: [],
standardPrompt: ">$%#",
passwordPrompt: ":",
passphrasePrompt: ":",
showBanner: false,
window: false, //https://github.com/mscdex/ssh2#pseudo-tty-settings use {cols:200}
enter: "\n",
streamEncoding: "utf8",
asciiFilter: "[^\r\n\x20-\x7e]",
disableColorFilter: false,
textColorFilter: "(\[{1}[0-9;]+m{1})",
msg: function( message ) { process.stdout.write(message)},
verbose: false,
debug: false,
connectedMessage: "Connected",
readyMessage: "Ready",
closedMessage: "Closed",
callback: function( sessionText ){},
onFirstPrompt: function() {},
onData: function( data ) {},
onPipe: function( writable ){},
onUnpipe: function( writable ) {},
onCommandProcessing: function( response ) {},
onCommandComplete: function( response ) {},
onLastCommand: function( command ) {},
onEnd: function( sessionText ) {},
onError: function( err, type, close = false, callback ) {},
onKeyboardInteractive: function(name, instructions, instructionsLang, prompts, finish){}
};
this
keyword is available within host event handlers to give access to persistent-shell functions and properties.this.host
or host variable passed into a function provides access to all the host config, some instance
variables.Persistent connection using both batch commands on connection and user input via the terminal
var persistent-shell = require (persistent-shell),
commands = ["cd /home", "la", "cd user", "la", "ifconfig"],
host = {
server: {
host: "192.168.0.1",
port: "22",
userName: "user",
password: "password"
},
//Commands can be set here or when using .runCommand(command/s).
commands: commands,
//First prompt detected and the stream is ready.
onFirstPrompt: function(){
this.stdin = process.openStdin();
var self = this;
//Receive stdin data
this.stdin.addListener("data", function(data){
var command = data.toString().trim();
if (command == "exit"){
self.stdin.end();
self.exit();
}else {
self.runCommand(command);
}
})
}
},
session = new persistentShell(host);
//Handle ctrl-c to terminate the running command on the host
process.on('SIGINT', function() {session.runCommand('\x03')});
//Make connection
session.connect();
Error: Unable to parse private key while generating public key (expected sequence)
is caused by the pass phrase
being incorrect. This confused me because it doesn't indicate the pass phrase was the problem but it does indicate
that it could not decrypt the private key.host.debug = true
When host.verbose = true
outputs each commands response.
When host.debug = true
outputs each process step.
host.server.tryKeyboard
and instance.on ("keayboard-interactive", function...)
or host.onKeyboardInteractive()
must be defined.Default Cyphers and Keys used in the initial ssh connection can be redefined by setting the ssh2.connect.algorithms through the host.server.algorithms option.
As with this property all ssh2.connect properties
are set in the host.server
object.
Example:
var host = {
server: {
host: "<host IP>",
port: "22",
userName: "<username>",
password: "<password>",
hashMethod: "md5", //optional "md5" or "sha1" default is "md5"
//other ssh2.connect options
algorithms: {
kex: [
'diffie-hellman-group1-sha1',
'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521',
'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group14-sha1'],
cipher: [
'aes128-ctr',
'aes192-ctr',
'aes256-ctr',
'aes128-gcm',
'aes128-gcm@openssh.com',
'aes256-gcm',
'aes256-gcm@openssh.com',
'aes256-cbc'
]
}
},
......
}
At connection time the hash of the server’s public key can be compared with the hash the client had previously recorded for that server. This stops "man in the middle" attacks where you are redirected to a different server as you connect to the server you expected to. This hash only changes with a reinstall of SSH, a key change on the server or a load balancer is now in place.
Note: Fingerprint check doesn't work the same way for tunnelling. The first host will validate using this method but the subsequent connections would have to be handled by your commands. Only the first host uses the SSH2 connection method that does the validation.
To use fingerprint validation you first need the server hash string which can be obtained using persistent-shell as follows:
Example:
//Define the hostValidation function in the host.server config.
//hashKey needs to be defined at the top level if you want to access the server hash at run time
var serverHash, host;
//don't set expectedHash if you want to know the server hash
var expectedHash
expectedHash = "85:19:8a:fb:60:4b:94:13:5c:ea:fe:3b:99:c7:a5:4e";
host = {
server: {
//other normal connection params,
hashMethod: "md5", //"md5" or "sha1"
//hostVerifier function must be defined and return true for match or false for failure.
hostVerifier: function(hashedKey) {
var recievedHash,
expectedHash = expectedHash + "".replace(/[:]/g, "").toLowerCase(),
recievedHash = hashedKey + "".replace(/[:]/g, "").toLowerCase();
if (expectedHash === "") {
//No expected hash so save what was received from the host (hashedKey)
//serverHash needs to be defined before host object
serverHash = hashedKey;
console.log("Server hash: " + serverHash);
return true;
} else if (recievedHash === expectedHash) {
console.log("Hash values matched");
return true;
}
//Output the failed comparison to the console if you want to see what went wrong
console.log("Hash values: Server = " + recievedHash + " <> Client = " + expectedHash);
return false;
},
},
//Other settings
};
var persistent-shell = require ('persistent-shell'),
session = new persistent-shell(host);
session.connect();
Note: host.server.hashMethod only supports md5 or sha1 according to the current SSH2 documentation anything else may produce undesired results.
Keyboard-interactive authentication is available when both host.server.tryKeyboard is set to true and the event handler keyboard-interactive is defined as below.
The keyboard-interactive event handler can only be used on the first connection.
Defining the event handler:
//this is required
host.server.tryKeyboard = true;
var persistent-shell = require ('../lib/persistent-shell');
var session = new persistent-shell(host);
//Add the keyboard-interactive handler
//The event function must call finish() with an array of responses in the same order as prompts received
// in the prompts array
session.on ('keyboard-interactive', function(name, instructions, instructionsLang, prompts, finish){
if (this.host.debug) {this.emit('msg', this.host.server.host + ": Keyboard-interactive");}
if (this.host.verbose){
this.emit('msg', "name: " + name);
this.emit('msg', "instructions: " + instructions);
var str = JSON.stringify(prompts, null, 4);
this.emit('msg', "Prompts object: " + str);
}
//The example presumes only the password is required
finish([this.host.server.password] );
});
session.connect();
Or
host = {
...,
onKeyboardInteractive: function(name, instructions, instructionsLang, prompts, finish){
if (this.host.debug) {this.emit('msg', this.host.server.host + ": Keyboard-interactive");}
if (this.host.verbose){
this.emit('msg', "name: " + name);
this.emit('msg', "instructions: " + instructions);
var str = JSON.stringify(prompts, null, 4);
this.emit('msg', "Prompts object: " + str);
}
//The example presumes only the password is required
finish([this.host.server.password] );
},
...
}
FAQs
A wrapper class for ssh2 to run a persistent shell session
The npm package persistent-shell receives a total of 1 weekly downloads. As such, persistent-shell popularity was classified as not popular.
We found that persistent-shell demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.