v1.2.0: bring up to par with sclient.go; add --servername, allow pipes, use - for stdin

This commit is contained in:
AJ ONeal 2018-09-03 15:29:19 -06:00
parent f14708495a
commit 1287ccc1ce
4 changed files with 104 additions and 22 deletions

View File

@ -51,9 +51,12 @@ Usage
===== =====
```bash ```bash
sclient <remote> <local> [-k | --insecure] sclient [flags] <remote> <local>
``` ```
* flags
* -k, --insecure ignore invalid TLS (SSL/HTTPS) certificates
* --servername <string> spoof SNI (to disable use IP as &lt;remote&gt; and do not use this option)
* remote * remote
* must have servername (i.e. example.com) * must have servername (i.e. example.com)
* port is optional (default is 443) * port is optional (default is 443)
@ -79,5 +82,31 @@ sclient telebit.cloud:443 localhost:3000
Ignore a bad TLS/SSL/HTTPS certificate and connect anyway. Ignore a bad TLS/SSL/HTTPS certificate and connect anyway.
```bash ```bash
sclient badtls.telebit.cloud:443 localhost:3000 -k sclient -k badtls.telebit.cloud:443 localhost:3000
```
Reading from stdin
```bash
sclient telebit.cloud:443 -
```
```bash
sclient telebit.cloud:443 - </path/to/file
```
Piping
```bash
printf "GET / HTTP/1.1\r\nHost: telebit.cloud\r\n\r\n" | sclient telebit.cloud:443
```
Testing for security vulnerabilities on the remote:
```bash
sclient --servername "Robert'); DROP TABLE Students;" -k example.com localhost:3000
```
```bash
sclient --servername "../../../.hidden/private.txt" -k example.com localhost:3000
``` ```

View File

@ -1,30 +1,68 @@
'use strict'; 'use strict';
var pkg = require('../package.json'); var pkg = require('../package.json');
var remote = (process.argv[2]||'').split(':'); var remote;
var local = (process.argv[3]||'').split(':'); var local;
var isPiped = !process.stdin.isTTY;
var localAddress; var localAddress;
var localPort; var localPort;
var rejectUnauthorized; var rejectUnauthorized;
var servername;
function usage() { function usage() {
console.info(""); console.info("");
console.info("sclient.js v" + pkg.version); console.info("sclient.js v" + pkg.version);
console.info("Usage: sclient <servername:port> <port> [-k | --insecure]"); console.info("Usage: sclient [--servername <string>] [-k | --insecure] <servername:port> <port>");
console.info(" ex: sclient whatever.com 3000"); console.info(" ex: sclient whatever.com 3000");
console.info(" (whatever.com:443 localhost:3000)"); console.info(" (whatever.com:443 localhost:3000)");
console.info(" ex: sclient whatever.com:4080 0.0.0.0:3000"); console.info(" ex: sclient whatever.com:4080 0.0.0.0:3000");
console.info(""); console.info("");
process.exit(0);
} }
function parseFlags() {
process.argv.some(function (arg, i) {
if (/^-k|--?insecure$/.test(arg)) {
rejectUnauthorized = false;
process.argv.splice(i, 1);
return true;
}
});
process.argv.some(function (arg, i) {
if (/^--?servername$/.test(arg)) {
servername = process.argv[i + 1];
if (!servername || /^-/.test(servername)) {
usage();
process.exit(2);
}
process.argv.splice(i, 2);
return true;
}
});
}
parseFlags();
remote = (process.argv[2]||'').split(':');
local = (process.argv[3]||'').split(':');
// arg 0 is node
// arg 1 is sclient
// arg 2 is remote
// arg 3 is local
if (4 !== process.argv.length) {
if (isPiped) {
local = ['|'];
} else {
usage();
process.exit(1);
}
}
// Check for the first argument (what to connect to)
if (!remote[0]) { if (!remote[0]) {
usage(); usage();
return; return;
} }
if (!remote[1]) {
remote[1] = 443;
}
if (!local[0]) { if (!local[0]) {
usage(); usage();
@ -34,24 +72,23 @@ if (local[0] === String(parseInt(local[0], 10))) {
localPort = parseInt(local[0], 10); localPort = parseInt(local[0], 10);
localAddress = 'localhost'; localAddress = 'localhost';
} else { } else {
localAddress = local[0]; localAddress = local[0]; // potentially '-' or '|'
localPort = parseInt(local[1], 10); localPort = parseInt(local[1], 10);
} }
if (!localPort) { if ('-' === localAddress || '|' === localAddress) {
// no need for port
} else if (!localPort) {
usage(); usage();
return; return;
} }
if (/^-k|--insecure$/.test(process.argv[4])) {
rejectUnauthorized = false;
}
var opts = { var opts = {
remoteAddr: remote[0] remoteAddr: remote[0]
, remotePort: remote[1] , remotePort: remote[1]
, localAddress: localAddress , localAddress: localAddress
, localPort: localPort , localPort: localPort
, rejectUnauthorized: rejectUnauthorized , rejectUnauthorized: rejectUnauthorized
, servername: servername
}; };
require('../')(opts); require('../')(opts);

View File

@ -4,7 +4,7 @@ var net = require('net');
var tls = require('tls'); var tls = require('tls');
function listenForConns(opts) { function listenForConns(opts) {
var server = net.createServer(function (c) { function pipeConn(c, out) {
var sclient = tls.connect({ var sclient = tls.connect({
servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort
, rejectUnauthorized: opts.rejectUnauthorized , rejectUnauthorized: opts.rejectUnauthorized
@ -12,7 +12,7 @@ function listenForConns(opts) {
console.info('[connect] ' + sclient.localAddress.replace('::1', 'localhost') + ":" + sclient.localPort console.info('[connect] ' + sclient.localAddress.replace('::1', 'localhost') + ":" + sclient.localPort
+ " => " + opts.remoteAddr + ":" + opts.remotePort); + " => " + opts.remoteAddr + ":" + opts.remotePort);
c.pipe(sclient); c.pipe(sclient);
sclient.pipe(c); sclient.pipe(out || c);
}); });
sclient.on('error', function (err) { sclient.on('error', function (err) {
console.error('[error] (remote) ' + err.toString()); console.error('[error] (remote) ' + err.toString());
@ -20,7 +20,19 @@ function listenForConns(opts) {
c.on('error', function (err) { c.on('error', function (err) {
console.error('[error] (local) ' + err.toString()); console.error('[error] (local) ' + err.toString());
}); });
if (out) {
out.on('error', function (err) {
console.error('[error] (local) ' + err.toString());
}); });
}
}
if ('-' === opts.localAddress || '|' === opts.localAddress) {
pipeConn(process.stdin, process.stdout);
return;
}
var server = net.createServer(pipeConn);
server.on('error', function (err) { server.on('error', function (err) {
console.error('[error] ' + err.toString()); console.error('[error] ' + err.toString());
}); });
@ -35,10 +47,14 @@ function listenForConns(opts) {
function testConn(opts) { function testConn(opts) {
// Test connection first // Test connection first
var tlsSock = tls.connect({ var tlsOpts = {
servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort host: opts.remoteAddr, port: opts.remotePort
, rejectUnauthorized: opts.rejectUnauthorized , rejectUnauthorized: opts.rejectUnauthorized
}, function () { };
if (opts.servername) {
tlsOpts.servername = opts.servername;
}
var tlsSock = tls.connect(tlsOpts, function () {
tlsSock.end(); tlsSock.end();
listenForConns(opts); listenForConns(opts);
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "sclient", "name": "sclient",
"version": "1.0.4", "version": "1.2.0",
"description": "Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. Also ideal for multiplexing a single port with multiple protocols using SNI.", "description": "Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. Also ideal for multiplexing a single port with multiple protocols using SNI.",
"main": "index.js", "main": "index.js",
"homepage": "https://git.coolaj86.com/coolaj86/sclient.js", "homepage": "https://git.coolaj86.com/coolaj86/sclient.js",