Unlink scripts submodule and commit contents as normal directory

This commit is contained in:
Kyle Speight committed 2026-06-14 03:42:18 -07:00
1 parent 4f7dea111f
commit 6488b3ea85
48 files changed
+2876 -1

No files matched your search

+271
View File
@@ -0,0 +1,271 @@
const { FLAGS, createFlagCollection } = require('../flags.js');
const logIpToFile = require('../logIpToFile.js');
const tailFile = require('../services/tailFile.js');
const logger = require('../logger.js');
const resolvePath = require('../pathResolver.js');
const { COWRIE_LOG_FILE, SERVER_ID } = require('../../config.js').MAIN;
const LOG_FILE = resolvePath(COWRIE_LOG_FILE);
const REPORT_DELAY = SERVER_ID === 'development' ? 30 * 1000 : 10 * 60 * 1000;
const CREDS_LIMIT = 885;
const ipBuffers = new Map();
const extractSessionData = sessions => {
const commands = [];
const flags = createFlagCollection();
const credsSet = new Set();
const fingerprints = new Set();
const uploads = new Set();
const tunnels = new Set();
let dpt = null, proto = null, sshVersion = null, timestamp = null;
const downloadUrls = new Set();
for (const s of sessions) {
dpt ??= s.dpt;
proto ??= s.proto;
sshVersion ??= s.sshVersion;
timestamp ??= s.timestamp;
s.credentials?.forEach((_, cred) => credsSet.add(cred));
commands.push(...s.commands);
if (s.fingerprint) fingerprints.add(s.fingerprint);
if (s.uploads) s.uploads.forEach(f => uploads.add(f));
if (s.tunnels) s.tunnels.forEach(t => tunnels.add(t));
if (s.download?.url) {
flags.add(FLAGS.WEB_APP_ATTACK);
downloadUrls.add(s.download.url);
}
}
const creds = [...credsSet];
const loginAttempts = creds.length;
const cmdCount = commands.length;
if (loginAttempts >= 2) flags.add(FLAGS.BRUTE_FORCE);
if (proto === 'ssh') flags.add(FLAGS.SSH);
if (proto === 'telnet') flags.add(FLAGS.TELNET);
if (cmdCount) flags.add(FLAGS.EMAIL);
if (!loginAttempts && !cmdCount) flags.add(FLAGS.PORT_SCAN);
return {
dpt, proto, sshVersion, timestamp, creds, commands,
categories: flags.toString(),
downloadUrls: [...downloadUrls],
fingerprints: [...fingerprints],
uploads: [...uploads],
tunnels: [...tunnels],
};
};
const buildComment = ({ serverId, dpt, proto, creds, commands, sshVersion, downloadUrls, fingerprints, uploads, tunnels }, full = false) => {
const loginAttempts = creds.length;
const cmdCount = commands.length;
const lines = [];
lines.push(`Honeypot ${serverId ? `[${serverId}]` : 'hit'}: ${loginAttempts ? 'Brute-force attack' : 'Unauthorized connection attempt'} detected on ${dpt}/${proto.toUpperCase()}`);
if (loginAttempts === 1) {
lines.push(`• Credential used: ${creds[0]}`);
} else if (loginAttempts > 1) {
let joined = creds.join(', ');
if (!full && joined.length > CREDS_LIMIT) joined = joined.slice(0, CREDS_LIMIT).replace(/,[^,]*$/, '') + '...';
lines.push(`• Credentials: ${joined}`);
}
if (loginAttempts) lines.push(`• Number of login attempts: ${loginAttempts}`);
if (cmdCount) lines.push(`• ${cmdCount} command(s) were executed during the session`);
if (sshVersion) lines.push(`• Client: ${sshVersion}`);
if (downloadUrls.length) lines.push(`• Suspicious file URLs: ${downloadUrls.join(', ')}`);
if (fingerprints.length) lines.push(`• SSH key fingerprints: ${fingerprints.join(', ')}`);
if (uploads.length) lines.push(`• Uploaded files: ${uploads.join(', ')}`);
if (tunnels.length) lines.push(`• TCP tunnels: ${tunnels.join(', ')}`);
return lines.join('\n');
};
const flushBuffer = async (srcIp, reportIp) => {
const buffer = ipBuffers.get(srcIp);
if (!buffer) return;
clearTimeout(buffer.timer);
ipBuffers.delete(srcIp);
const sessions = buffer.sessions || [];
if (!sessions.length) return;
const {
dpt, proto, sshVersion, timestamp, creds, commands,
categories, downloadUrls, fingerprints, uploads, tunnels,
} = extractSessionData(sessions);
if (!srcIp || !dpt || !proto) {
return logger.warn(`COWRIE -> Incomplete data for ${srcIp}, discarded`);
}
const shortComment = buildComment({ serverId: SERVER_ID, dpt, proto, creds, commands, sshVersion, downloadUrls, fingerprints, uploads, tunnels }, false);
const [, ...restLines] = shortComment.split('\n');
const rest = restLines.length ? `\n${restLines.join('\n')}` : '';
await reportIp('COWRIE', { srcIp, dpt, proto, timestamp }, categories, shortComment);
if (restLines.length) await logger.info(rest);
await logger.webhook(`### Cowrie: ${srcIp} on ${dpt}/${proto}${rest}`);
logIpToFile(srcIp, { honeypot: 'COWRIE', comment: buildComment({ serverId: SERVER_ID, dpt, proto, creds, commands, sshVersion, downloadUrls, fingerprints, uploads, tunnels }, true) });
};
const processCowrieLogLine = async (entry, reportIp) => {
const ip = entry?.src_ip;
const sessionId = entry?.session;
const { eventid } = entry;
if (!ip || !eventid || !sessionId) return logger.warn('COWRIE -> Skipped: missing src_ip, eventid or sessionId');
let buffer = ipBuffers.get(ip);
if (!buffer) {
buffer = {
sessions: [],
timer: setTimeout(() => flushBuffer(ip, reportIp), REPORT_DELAY),
lastSeen: Date.now(),
};
ipBuffers.set(ip, buffer);
} else {
buffer.lastSeen = Date.now();
}
let session = buffer.sessions.find(s => s.sessionId === sessionId);
if (!session && eventid !== 'cowrie.session.closed') {
session = {
sessionId,
srcIp: ip,
dpt: null,
proto: null,
timestamp: null,
credentials: new Map(),
commands: [],
sshVersion: null,
download: null,
fingerprint: null,
uploads: [],
tunnels: [],
};
buffer.sessions.push(session);
}
switch (eventid) {
case 'cowrie.session.connect':
if (session) {
session.dpt = entry.dst_port;
session.proto = entry.protocol;
session.timestamp = entry.timestamp;
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: Connect`);
}
break;
case 'cowrie.login.success':
case 'cowrie.login.failed':
if (session && (entry.username || entry.password)) {
session.credentials.set(`${entry.username}:${entry.password}`, true);
const status = eventid === 'cowrie.login.success' ? 'Connected' : 'Failed login';
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: ${status} » ${entry.username}:${entry.password}`);
}
break;
case 'cowrie.client.version':
if (session) {
session.sshVersion = entry.version;
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: SSH version » ${entry.version}`);
}
break;
case 'cowrie.command.input':
if (session && entry.input) {
session.commands.push(entry.input);
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: $ ${entry.input}`);
}
break;
case 'cowrie.session.file_download':
if (session && entry.url) {
session.commands.push(`[download] ${entry.url}`);
session.download = { url: entry.url, outfile: entry.outfile };
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: File download » ${entry.url}`);
}
break;
case 'cowrie.client.fingerprint':
if (session && entry.fingerprint) {
session.fingerprint = entry.fingerprint;
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: SSH key fingerprint » ${entry.fingerprint}`);
}
break;
case 'cowrie.session.file_upload':
if (session && entry.filename) {
session.uploads.push(entry.filename);
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: File upload » ${entry.filename}`);
}
break;
case 'cowrie.direct-tcpip.request':
if (session && entry.dst_ip && entry.dst_port) {
const tunnel = `${entry.dst_ip}:${entry.dst_port}`;
session.tunnels.push(tunnel);
logger.info(`COWRIE -> ${ip}/${session.proto}/${session.dpt}: TCP tunnel request » ${tunnel}`);
}
break;
case 'cowrie.session.closed':
logger.info(`COWRIE -> ${ip}/${session?.proto ?? 'unknown'}/${session?.dpt ?? '-'}: Session ${sessionId} closed`);
break;
}
};
module.exports = reportIp => {
tailFile(LOG_FILE, async line => {
if (!line.length) return;
let entry;
try {
entry = JSON.parse(line);
} catch (err) {
return logger.error(`COWRIE -> JSON parse error: ${err.message}\nFaulty line: ${JSON.stringify(line)}`);
}
try {
await processCowrieLogLine(entry, reportIp);
} catch (err) {
logger.error(err);
}
}, { label: 'COWRIE' }).catch(err => logger.error(err));
// Clean buffer
const cleanupInterval = setInterval(() => {
const now = Date.now();
for (const [ip, buffer] of ipBuffers.entries()) {
if (now - buffer.lastSeen > 30 * 60 * 1000) {
clearTimeout(buffer.timer);
ipBuffers.delete(ip);
logger.warn(`COWRIE -> Cleaned up stale session buffer for ${ip}`);
}
}
}, 15 * 60 * 1000);
logger.success('🛡️ COWRIE » Watcher initialized');
return {
flush: async () => {
for (const ip of ipBuffers.keys()) {
await flushBuffer(ip, reportIp);
}
},
cleanup: () => {
clearInterval(cleanupInterval);
for (const buffer of ipBuffers.values()) {
clearTimeout(buffer.timer);
}
ipBuffers.clear();
},
};
};