This repository has been archived by the owner on Jan 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first commit. only 'none' auth - secure telnet :)
- Loading branch information
0 parents
commit 6384055
Showing
3 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; | ||
}, | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
|