Skip to content
This repository has been archived by the owner on Jan 29, 2023. It is now read-only.

Commit

Permalink
first commit. only 'none' auth - secure telnet :)
Browse files Browse the repository at this point in the history
  • Loading branch information
danopia committed Apr 15, 2013
0 parents commit 6384055
Show file tree
Hide file tree
Showing 3 changed files with 368 additions and 0 deletions.
72 changes: 72 additions & 0 deletions packetreader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
var PacketReader = module.exports = function (buffer, macLen, deciph, macC, seqC) {
var d1 = deciph ? deciph.update(buffer.slice(0, 5)) : buffer;
var pktLen = d1.readUInt32BE(0),
padLen = d1.readUInt8(4),
payLen = pktLen - padLen,
macIdx = 4 + pktLen;

this.totLen = 4 + pktLen + macLen;
this.orig = buffer.slice(5, 4 + payLen);
this.mac = buffer.slice(macIdx, macIdx + macLen);

this.payload = deciph ? deciph.update(this.orig) : this.orig;
if (buffer.slice(4 + payLen, 4 + payLen + padLen).length != padLen) {
console.log(d1);
};
if (deciph) this.padding = deciph.update(buffer.slice(4 + payLen, 4 + payLen + padLen));

if (macLen) {
var asdff = new Buffer(4);
asdff.writeUInt32BE(seqC, 0);
var mac = require('crypto').createHmac('md5', macC.slice(0, 16)); // TODO: net::ssh key_expander.rb
mac.write(Buffer.concat([asdff,d1,this.payload,this.padding]))
mac = new Buffer(mac.digest());
if (mac.toString() != this.mac.toString()) console.log('SECURITY ERROR: MAC hash from client is incorrect');
};

this.idx = 1;
};

PacketReader.prototype = {
getType: function () {
return this.payload.readUInt8(0);
},

readUInt8: function () {
return this.payload.readUInt8((this.idx += 1) - 1);
},

readUInt32: function () {
return this.payload.readUInt32BE((this.idx += 4) - 4);
},

readBool: function () {
return this.readUInt8() > 0;
},

readBuffer: function (len) {
if (!len) len = this.readUInt32();
return this.payload.slice(this.idx, this.idx += len);
},

readString: function (len) {
if (!len) len = this.readUInt32();
return this.payload.toString('utf8', this.idx, this.idx += len);
},

readList: function () {
var str = this.readString();
return (str.length ? str.split(',') : []);
},

readMpint: function () {
var buff = this.readBuffer();
if (buff[0] & 0x80) {
console.log('Really, fuck twos complement.', buff);
} else {
if (buff[0] == 0) buff = buff.slice(1);
return buff;
};
},
};

65 changes: 65 additions & 0 deletions packetwriter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
var toTwos = function (buff) {
var neg = buff.neg;

while (buff.length > 2 && buff[0] === 0 && buff[1] === 0)
buff = buff.slice(1);

if (buff.length > 1 && buff[0] & 0x80 == 0)
buff = buff.slice(1);
if (buff[0] & 0x80)
buff = Buffer.concat([new Buffer([0]), buff]);

if (buff[0] == 0 && buff.length == 1)
buff = new Buffer(0);
else
buff = new Buffer(buff);

if (neg) {
var i;
for (i = 0; i < buff.length; i++)
buff[i] = ~buff[i];

if (buff[buff.length - 1] == 255) console.log('Fuck twos complement.', buff);
buff[buff.length - 1]++;
};

return buff;
};

module.exports = function (data) {
var len = 0, i, j;

for (i = 0; i < data.length; i++) {
j = data[i];
if (j.byte !== undefined || j === true || j === false) len += 1;
else if (j.uint32 !== undefined) len += 4;
else if (j.raw) len += j.raw.length;
else if (Array.isArray(j)) len += 4 + j.join(',').length;
else if (j.length !== undefined) len += 4 + j.length;
else if (j.mpint) len += 4 + toTwos(j.mpint).length;
else console.log('What size is', j);
};

var payload = new Buffer(len),
idx = 0;

for (i = 0; i < data.length; i++) {
j = data[i];
if (j === true || j === false) j = {byte: (j ? 1 : 0)};
else if (Array.isArray(j)) j = j.join(',');
else if (j.mpint) j = toTwos(j.mpint);

if (j.byte !== undefined) payload.writeUInt8(j.byte, (idx += 1) - 1);
else if (j.uint32 !== undefined) payload.writeUInt32BE(j.uint32, (idx += 4) - 4);
else if (j.raw) j.raw.copy(payload, (idx += j.raw.length) - j.raw.length);
else if (j.length !== undefined) {
if (!Buffer.isBuffer(j)) j = new Buffer(j);
payload.writeUInt32BE(j.length, idx);
j.copy(payload, (idx += 4 + j.length) - j.length);
};
};

//console.log(data, payload, payload.toString());
return payload;
};

231 changes: 231 additions & 0 deletions sshd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
var net = require('net'),
fs = require('fs'),
crypto = require('crypto'),
PacketReader = require('./packetreader'),
composePacket = require('./packetwriter');

var hostkey = fs.readFileSync('/home/danopia/hostkey').toString();
var hostPub = new Buffer(fs.readFileSync('/etc/ssh/ssh_host_rsa_key.pub').toString().split(' ')[1], 'base64');

function signBuffer(buffer) {
var signer = crypto.createSign('RSA-SHA1');
signer.write(buffer);
var signature = signer.sign(hostkey);
return composePacket(['ssh-rsa', signature]);
}

require('net').createServer(function (conn) {
var macLen = 0, seqS = 0, seqC = 0, kex, dh, hashIn = [], keyson = false, session, cookie, deciph, cipher, macS, macC, user, proc;

var sendPay = function (payload) {
var padLen = (16-((5 + payload.length)%16))+16;
var buffer = new Buffer(5 + payload.length + padLen);

buffer.writeUInt32BE(payload.length + 1 + padLen, 0);
buffer.writeUInt8(padLen, 4);
payload.copy(buffer, 5);
buffer.fill(0, 5 + payload.length);

if (macLen) {
var asdff = new Buffer(4);
asdff.writeUInt32BE(seqS, 0);
var mac = crypto.createHmac('md5', macS.slice(0, 16)); // TODO: net::ssh key_expander.rb
mac.write(Buffer.concat([asdff,buffer]))
mac = new Buffer(mac.digest());
};

console.log('>> Type', payload[0], '-', payload.length, 'bytes');
if (cipher) buffer = cipher.update(buffer);
if (macLen) buffer = Buffer.concat([buffer, mac]);
conn.write(buffer);

seqS += 1;
};

var sendPayload = function (ast) {
sendPay(composePacket(ast));
};

var getPacket = function (packet) {
var type = packet.getType();
console.log('<< Type', type, '-', packet.payload.length, 'bytes');
switch (type) {
case 1: // disconnect
var code = packet.readUInt32(),
msg = packet.readString();
console.log('Client disconnected:', msg, '('+code+')');
break;

case 20: // kexinit
hashIn.push(packet.payload);
hashIn.push(composePacket([{byte: 20}, {raw: cookie}, ['diffie-hellman-group-exchange-sha256'], ['ssh-rsa'], ['aes256-ctr'], ['aes256-ctr'], ['hmac-md5'], ['hmac-md5'], ['none'], ['none'], [], [], false, {uint32: 0}]));
hashIn.push(hostPub);

kex = {
cookie: packet.readBuffer(16),
kexAlgs: packet.readList(),
hostKeyAlgs: packet.readList(),
encAlgs: [packet.readList(), packet.readList()],
macAlgs: [packet.readList(), packet.readList()],
cprAlgs: [packet.readList(), packet.readList()],
langs: [packet.readList(), packet.readList()],
firstKexFollows: packet.readBool()};
break;

case 34: // SSH_MSG_KEX_DH_GEX_REQUEST
var dhflags = {
min: packet.readUInt32(),
n: packet.readUInt32(),
max: packet.readUInt32()};
hashIn.push({uint32: dhflags.min});
hashIn.push({uint32: dhflags.n});
hashIn.push({uint32: dhflags.max});
dh = require('crypto').getDiffieHellman('modp2');

// SSH_MSG_KEX_DH_GEX_GROUP
hashIn.push({mpint: dh.getPrime()});
hashIn.push({mpint: new Buffer([2])});
sendPay(composePacket([{byte: 31}, {mpint: dh.getPrime()}, {mpint: new Buffer([2])}]));
dh.generateKeys();
break;

case 32: // SSH_MSG_KEX_DH_GEX_INIT
var e = packet.readMpint();
dh.secret = dh.computeSecret(e);

hashIn.push({mpint: e});
hashIn.push({mpint: dh.getPublicKey()});
hashIn.push({mpint: dh.secret});

var sha = require('crypto').createHash('sha256');
sha.write(composePacket(hashIn));
session = sha.digest();
sendPayload([{byte: 33}, hostPub, {mpint: dh.getPublicKey()}, signBuffer(session)]);
break;

case 21: // SSH_MSG_NEWKEYS okay bro, keys are good, let's goooo
sendPayload([{byte: 21}]);
keyson = true;

var keyize = function (salt) {
// TODO: dh.secret might need ot be encoded for SSH
var sha = crypto.createHash('SHA256');
sha.write(Buffer.concat([composePacket([{mpint: dh.secret}]), new Buffer(session), new Buffer(salt), new Buffer(session)]));
return sha;
};

//console.log(keyize('C').digest('hex'));
deciph = crypto.createDecipheriv('aes-256-ctr', keyize('C').digest(), keyize('A').digest().slice(0,16));
cipher = crypto.createCipheriv ('aes-256-ctr', keyize('D').digest(), keyize('B').digest().slice(0,16));

macC = keyize('E').digest();
macS = keyize('F').digest();
macLen = 16;
break;

case 5: // SSH_MSG_SERVICE_REQUEST
var service = packet.readString();
console.log('Client requested', service);
if (service == 'ssh-userauth') {
sendPayload([{byte: 6}, service]); // SSH_MSG_SERVICE_ACCEPT
} else {
sendPayload([{byte: 1}, {byte: 0}, 'wtf dude']);
}
break;

case 50: // SSH_MSG_USERAUTH_REQUEST
user = packet.readString();
var service = packet.readString();
var method = packet.readString(); // plus more
console.log(user, service, method);
sendPayload([{byte: 52}]); // SSH_MSG_USERAUTH_SUCCESS
break;

case 90: // SSH_MSG_CHANNEL_OPEN
var channel = {
type: packet.readString(),
sender: packet.readUInt32(),
initSize: packet.readUInt32(),
maxSize: packet.readUInt32()}; // plus more
console.log(channel);

sendPayload([{byte: 91}, {uint32: channel.sender}, {uint32: channel.sender}, {uint32: channel.initSize}, {uint32: channel.maxSize}]); // SSH_MSG_CHANNEL_OPEN_CONFIRMATION
break;

case 98: // SSH_MSG_CHANNEL_REQUEST
var recip = packet.readUInt32();
var type = packet.readString();
var wantReply = packet.readBool();
// plus more
if (type == 'env') {
console.log('Environment:', packet.readString(), '=', packet.readString());
} else if (type == 'exec') {
console.log('Client wants to exec', packet.readString(), wantReply);
sendPayload([{byte: 99}, {uint32: recip}]); // SSH_MSG_CHANNEL_SUCCESS
} else if (type == 'pty-req') {
var pty = {
term: packet.readString(),
widthC: packet.readUInt32(),
heightC: packet.readUInt32(),
widthP: packet.readUInt32(),
heightP: packet.readUInt32(),
modes: packet.readString()};

console.log(wantReply, pty);
sendPayload([{byte: 99}, {uint32: recip}]); // SSH_MSG_CHANNEL_SUCCESS
} else if (type == 'shell') {
console.log('Client warms up their shell');
sendPayload([{byte: 99}, {uint32: recip}]); // SSH_MSG_CHANNEL_SUCCESS
} else {
console.log('Requested', type, 'for', recip, '... but idk');
if (wantReply)
sendPayload([{byte: 98}, {uint32: recip}]); // SSH_MSG_CHANNEL_FAILURE
};
break;

case 94: // SSH_MSG_CHANNEL_DATA
var chan = packet.readUInt32();
var data = packet.readString();
console.log(chan, data);
if (data == '\u0004') {
sendPayload([{byte: 94}, {uint32: chan}, 'Hit q to exit\r\n']);
} else if (data == 'q') {
sendPayload([{byte: 1}, {uint32: 0}, 'Bye!']);
} else {
sendPayload([{byte: 94}, {uint32: chan}, 'You hit ' + data + '\r\n']);
}
break;

default:
console.log('Unimpl packet', type, packet.payload, packet.payload.toString());
process.exit();
};
};

console.log('New connection');
conn.on('data', function (data) {
if (data.toString('utf-8', 0, 4) === 'SSH-') {
var eof = data.toString().indexOf('\n');
console.log('Client header:', data.toString('utf-8', 8, eof-1));
hashIn.push(data.toString('utf8', 0, eof-1))
hashIn.push('SSH-2.0-sshd.js_0.0.1 Experimental, low-security SSHd implemented in NodeJS');
data = data.slice(eof + 1);
};

while (data.length >= 4) {
var packet = new PacketReader(data, macLen, deciph, macC, seqC);
getPacket(packet);
seqC += 1;
data = data.slice(packet.totLen);
};
});

crypto.randomBytes(16, function (err, rand) {
conn.write('SSH-2.0-sshd.js_0.0.1 Experimental, low-security SSHd implemented in NodeJS\r\n');

cookie = rand;
sendPay(composePacket([{byte: 20}, {raw: cookie}, ['diffie-hellman-group-exchange-sha256'], ['ssh-rsa'], ['aes256-ctr'], ['aes256-ctr'], ['hmac-md5'], ['hmac-md5'], ['none'], ['none'], [], [], false, {uint32: 0}]));
});

}).listen(22);

0 comments on commit 6384055

Please sign in to comment.