Skip to content

Commit 233323d

Browse files
committed
Unify SMTP honeypot for ports 25 and 587, update about panel
- Make smtp_honeypot.py accept --port and --proto args so it serves both SMTP (587) and MAIL (25) with full AUTH/STARTTLS/tracing support - Add honeypot_args to PROTOCOL_META, passed by monitor.py at spawn - Enable user/pass panels for MAIL protocol (AUTH credentials now captured) - Fix MAIL feed schema to show user/pass for AUTH, from/to for relays - Proto-aware SMTP diagnostic Redis keys (knock:diag:smtp vs knock:diag:mail) - Streamline about panel text, remove hardcoded site name - Optimize init_stats broadcast to skip history, add per-proto uptime - Simplify restart.sh Redis reset to pattern-match knock:* keys - Add nav visibility hidden, uptime formatting, proto color helpers
1 parent a272a67 commit 233323d

6 files changed

Lines changed: 192 additions & 79 deletions

File tree

constants.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@
3838
'MAIL': {
3939
'proto_int': 4,
4040
'color': '#00ffaa',
41-
'supports_user_panel': False,
42-
'supports_pass_panel': False,
43-
'honeypot_script': 'honeypots/smtp25_honeypot.py',
41+
'supports_user_panel': True,
42+
'supports_pass_panel': True,
43+
'honeypot_script': 'honeypots/smtp_honeypot.py',
44+
'honeypot_args': ['--port', '25', '--proto', 'MAIL'],
4445
},
4546
'FTP': {
4647
'proto_int': 5,

honeypots/smtp_honeypot.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def b64decode(s):
107107
except Exception:
108108
return ''
109109

110+
_PROTO_LABEL = "SMTP" # overridden by --proto arg
111+
110112
def emit_smtp_knock(
111113
client_ip,
112114
*,
@@ -118,7 +120,7 @@ def emit_smtp_knock(
118120
subject=None,
119121
body=None,
120122
):
121-
knock = {"type": "KNOCK", "proto": "SMTP", "ip": client_ip, "smtp_stage": stage}
123+
knock = {"type": "KNOCK", "proto": _PROTO_LABEL, "ip": client_ip, "smtp_stage": stage}
122124
if username is not None:
123125
knock["user"] = username
124126
if password is not None:
@@ -136,7 +138,7 @@ def emit_smtp_knock(
136138
def emit_smtp_diag(client_ip, session_id, **fields):
137139
payload = {
138140
"type": "SMTP_DIAG",
139-
"proto": "SMTP",
141+
"proto": _PROTO_LABEL,
140142
"ip": client_ip,
141143
"session_id": session_id,
142144
}
@@ -473,10 +475,12 @@ def emit_knock(stage, **kwargs):
473475
except:
474476
pass
475477

476-
def start_honeypot():
478+
def start_honeypot(port=587, proto_label="SMTP"):
479+
global _PROTO_LABEL
480+
_PROTO_LABEL = proto_label
477481
ensure_smtp_cert(_SMTP_HOSTNAME, SMTP_TLS_CERT_PATH, SMTP_TLS_KEY_PATH)
478-
sock = create_dualstack_tcp_listener(587, backlog=100)
479-
print(f"🚀 SMTP Honeypot Active on Port 587 (IPv4+IPv6) [{SMTP_FINGERPRINT}]. Collecting radiation...", flush=True)
482+
sock = create_dualstack_tcp_listener(port, backlog=100)
483+
print(f"🚀 {proto_label} Honeypot Active on Port {port} (IPv4+IPv6) [{SMTP_FINGERPRINT}]. Collecting radiation...", flush=True)
480484

481485
while True:
482486
client, addr = sock.accept()
@@ -488,4 +492,9 @@ def start_honeypot():
488492
threading.Thread(target=handle_connection, args=(client, client_ip), daemon=True).start()
489493

490494
if __name__ == "__main__":
491-
start_honeypot()
495+
import argparse
496+
parser = argparse.ArgumentParser()
497+
parser.add_argument("--port", type=int, default=587)
498+
parser.add_argument("--proto", default="SMTP")
499+
args = parser.parse_args()
500+
start_honeypot(port=args.port, proto_label=args.proto)

index.html

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@
199199
.d-nav {
200200
display: flex; flex-wrap: wrap; gap: 16px 26px; margin-bottom: 10px;
201201
border-bottom: 1px solid #222; padding-bottom: 10px;
202+
visibility: hidden;
202203
}
203204
.d-nav-item {
204205
color: var(--ui-grey-dim); font-size: 1.6em; cursor: pointer;
@@ -480,15 +481,15 @@
480481
#m-viewport::-webkit-scrollbar { display: none !important; }
481482

482483
.m-footer { flex: 0 0 auto; background: #0a0a0a; border-top: 1px solid #222; padding-bottom: env(safe-area-inset-bottom); }
483-
.m-dots { display: flex; justify-content: center; gap: 15px; padding: 12px 0; }
484+
.m-dots { display: flex; justify-content: center; gap: 15px; padding: 12px 0; visibility: hidden; }
484485
.dot { width: 10px; height: 10px; border-radius: 50%; background: #222; transition: 0.3s; cursor: pointer; }
485486
.dot.active { background: var(--neon-green); transform: scale(1.4); box-shadow: 0 0 8px var(--neon-green); }
486487

487488
.m-nav-container { position: relative; border-top: 2px solid var(--neon-green); background: #000; }
488489
.m-nav-container::before, .m-nav-container::after { content: ''; position: absolute; top: 0; bottom: 0; width: 30px; z-index: 10; pointer-events: none; }
489490
.m-nav-container::before { left: 0; background: linear-gradient(to right, #000 0%, transparent 100%); }
490491
.m-nav-container::after { right: 0; background: linear-gradient(to left, #000 0%, transparent 100%); }
491-
.m-nav { display: flex; gap: 40px; padding: 15px 30px; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
492+
.m-nav { display: flex; gap: 40px; padding: 15px 30px; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; visibility: hidden; }
492493
.m-nav::-webkit-scrollbar { display: none; }
493494
.nav-item { color: var(--ui-grey-dim); font-size: 1.8em; display: flex; flex-direction: column; align-items: center; cursor: pointer; transition: 0.2s; flex-shrink: 0; }
494495
.nav-item span { font-size: 0.45em; margin-top: 4px; font-weight: bold; }
@@ -624,12 +625,9 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
624625
<div class="d-box" id="d-box-trivia"><h2 style="display:flex;justify-content:space-between;align-items:center;">TRIVIA<span class="proto-cycle-btn desktop-readonly" style="vertical-align:initial;">ALL</span></h2><div id="d-trivia" class="d-content"></div></div>
625626
<div class="d-box" id="d-box-jokes"><h2>KNOCK-KNOCK JOKES</h2><div id="d-jokes" class="d-content" style="color:var(--neon-green); line-height:1.6;"></div></div>
626627
<div class="d-box"><h2>ABOUT</h2><div class="d-content" style="color:var(--neon-green); line-height:1.6;">
627-
<div style="font-size:1.4em; font-weight:bold; margin-bottom:20px;">KNOCK-KNOCK.NET</div>
628-
<p>Set up an unprotected server on the net, and the bots start swarming!</p><p> This site shows bots attempting (unsuccessfully) to break into an ordinary internet server.</p>
629-
<p>This constant chatter of bots knocking on the doors of machines on the net has been referred to as <em>"the background radiation of the Internet".</em></p>
630-
<p>Knock-knock.net is a visualization of this bot traffic.
631-
It shows the bot activity in real-time, and provides historic stats of the bot attacks over time: where they are coming from, the most common usernames and passwords attempted, the worst offending ISPs, and in some cases, why the password or username was chosen.</p>
632-
<p>Have fun! Send questions or comments to:<br><a class="contact-email" href="#" style="color:var(--data-blue)"></a></p>
628+
<p>Set up an unprotected server on the net, and the bots start swarming! This site shows their break-in attempts in real-time: where they come from, what credentials they try, and why.</p>
629+
<p>Questions and comments to: <a class="contact-email" href="#" style="color:var(--data-blue)"></a></p>
630+
<div id="d-about-server-info" style="color:var(--ui-grey-dim); font-size:0.9em; margin-top:15px; margin-bottom:15px;"></div>
633631
<div style="text-align:center; margin-top:30px;">
634632
<a href="https://www.abuseipdb.com/user/270949" title="AbuseIPDB is an IP address blacklist for webmasters and sysadmins to report IP addresses engaging in abusive behavior on their networks" target="_blank" rel="noopener">
635633
<img src="https://www.abuseipdb.com/contributor/270949.svg" alt="AbuseIPDB Contributor Badge" style="width:200px; border-radius:5px; border:1px solid #333; padding:8px; background:var(--data-blue); box-shadow:0 0 8px rgba(0,251,255,0.3);">
@@ -672,12 +670,9 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
672670
<div class="m-pane" id="m-pane-trivia"><h2 style="display:flex;justify-content:space-between;align-items:center;">TRIVIA<span class="proto-cycle-btn" onclick="cycleProto(event)" style="vertical-align:initial;">⟳ ALL</span></h2><div id="m-trivia" class="m-content"></div></div>
673671
<div class="m-pane" id="m-pane-jokes"><h2>KNOCK-KNOCK JOKES</h2><div id="m-jokes" class="m-content" style="color:var(--neon-green); line-height:1.6;"></div></div>
674672
<div class="m-pane" id="m-pane-about"><h2>ABOUT</h2><div class="m-content" style="color:var(--neon-green); line-height:1.6;">
675-
<div style="font-size:1.4em; font-weight:bold; margin-bottom:20px;">KNOCK-KNOCK.NET</div>
676-
<p>Set up an unprotected server on the net, and the bots start swarming!</p><p> This site shows bots attempting (unsuccessfully) to break into an ordinary internet server.</p>
677-
<p>This constant chatter of bots knocking on the doors of machines on the net has been referred to as <em>"the background radiation of the Internet".</em></p>
678-
<p>Knock-knock.net is a visualization of this bot traffic.
679-
It shows the bot activity in real-time, and provides historic stats of the bot attacks over time: where they are coming from, the most common usernames and passwords attempted, the worst offending ISPs, and in some cases, why the password or username was chosen.</p>
680-
<p>Have fun! Send questions or comments to:<br><a class="contact-email" href="#" style="color:var(--data-blue)"></a></p>
673+
<p>Set up an unprotected server on the net, and the bots start swarming! This site shows their break-in attempts in real-time: where they come from, what credentials they try, and why.</p>
674+
<p>Questions and comments to: <a class="contact-email" href="#" style="color:var(--data-blue)"></a></p>
675+
<div id="m-about-server-info" style="color:var(--ui-grey-dim); font-size:0.9em; margin-top:15px; margin-bottom:15px;"></div>
681676
<div style="text-align:center; margin-top:30px;">
682677
<a href="https://www.abuseipdb.com/user/270949" title="AbuseIPDB is an IP address blacklist for webmasters and sysadmins to report IP addresses engaging in abusive behavior on their networks" target="_blank" rel="noopener">
683678
<img src="https://www.abuseipdb.com/contributor/270949.svg" alt="AbuseIPDB Contributor Badge" style="width:275px; border-radius:5px; border:1px solid #333; padding:8px; background:var(--data-blue); box-shadow:0 0 8px rgba(0,251,255,0.3);">
@@ -891,15 +886,58 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
891886
let showPasswordPanel = true;
892887
const lastKnockTimeByProto = {};
893888

889+
function formatUptimeCompact(minutes) {
890+
const m = Math.floor(minutes);
891+
if (m < 60) return `${m}m`;
892+
const d = Math.floor(m / 1440);
893+
const h = Math.floor((m % 1440) / 60);
894+
const rm = m % 60;
895+
return (d > 0 ? `${d}d ` : '') + (h > 0 ? `${h}h ` : '') + `${rm}m`;
896+
}
897+
898+
function colorProtoList(protocols) {
899+
return protocols.map(p => {
900+
const c = PROTO_COLORS[p] || PROTO_COLORS.ALL;
901+
return `<span style="color:${c}">${escapeHtml(p)}</span>`;
902+
}).join('<span style="color:var(--neon-green)">, </span>');
903+
}
904+
905+
function updateAboutServerInfo() {
906+
const L = 'color:var(--ui-label)';
907+
const G = 'color:var(--neon-green)';
908+
const LW = 'display:inline-block;width:6em;flex-shrink:0;';
909+
const ROW = 'display:flex;';
910+
const lines = [];
911+
lines.push(`<div style="${ROW}"><span style="${L};${LW}">Uptime:</span><span style="${G}">${formatUptimeCompact(uptimeMinutesCache)}</span></div>`);
912+
lines.push(`<div style="${ROW}"><span style="${L};${LW}">Monitoring:</span><span style="${G}">${TRACKED_PROTOCOLS.map(escapeHtml).join(', ')}</span></div>`);
913+
if (isFiltered) {
914+
lines.push(`<div style="${ROW}"><span style="${L};${LW}">Viewing:</span><span style="${G}">${activeProtocols.map(escapeHtml).join(', ')} only</span></div>`);
915+
}
916+
const html = lines.join('');
917+
['d-about-server-info', 'm-about-server-info'].forEach(id => {
918+
const el = document.getElementById(id);
919+
if (el) el.innerHTML = html;
920+
});
921+
}
922+
894923
function getFilteredTotal() {
895924
let sum = 0;
896925
activeProtocols.forEach(p => { sum += Number(protoBreakdownCache?.[p]?.count || 0); });
897926
return sum;
898927
}
899928

900929
function getFilteredKpm() {
901-
const mins = Number(uptimeMinutesCache) || 0;
902-
return mins > 0 ? (getFilteredTotal() / mins).toFixed(2) : '0.00';
930+
if (activeProtocols.length === TRACKED_PROTOCOLS.length) {
931+
const mins = Number(uptimeMinutesCache) || 0;
932+
return mins > 0 ? (getFilteredTotal() / mins).toFixed(2) : '0.00';
933+
}
934+
let totalKpm = 0;
935+
activeProtocols.forEach(p => {
936+
const uptime = Number(protoBreakdownCache?.[p]?.uptime || 0);
937+
const count = Number(protoBreakdownCache?.[p]?.count || 0);
938+
if (uptime > 0) totalKpm += count / uptime;
939+
});
940+
return totalKpm.toFixed(2);
903941
}
904942

905943
function getFilteredLastKnockTime() {
@@ -1410,7 +1448,9 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
14101448
if (totalGlobal !== null && totalGlobal > 0) {
14111449
pct = Number(((count * 100) / totalGlobal).toFixed(2));
14121450
}
1413-
out[proto] = { count, pct };
1451+
const uptimeRaw = Number(row.uptime);
1452+
const uptime = Number.isFinite(uptimeRaw) ? Math.max(0, Math.floor(uptimeRaw)) : 0;
1453+
out[proto] = { count, pct, uptime };
14141454
});
14151455
return out;
14161456
}
@@ -1419,27 +1459,32 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
14191459
const proto = (d?.proto || '').toUpperCase();
14201460
if (!TRACKED_PROTOCOLS.includes(proto)) return;
14211461
if (!protoBreakdownCache[proto]) {
1422-
protoBreakdownCache[proto] = { count: 0, pct: 0 };
1462+
protoBreakdownCache[proto] = { count: 0, pct: 0, uptime: 0 };
14231463
}
14241464
protoBreakdownCache[proto].count += 1;
1465+
const protoUptime = Number(d?.proto_uptime);
1466+
if (Number.isFinite(protoUptime) && protoUptime > 0) {
1467+
protoBreakdownCache[proto].uptime = protoUptime;
1468+
}
14251469
const total = Number(d?.total_global);
14261470
if (Number.isFinite(total) && total > 0) {
14271471
TRACKED_PROTOCOLS.forEach(name => {
14281472
const count = Number(protoBreakdownCache[name]?.count || 0);
1473+
const uptime = Number(protoBreakdownCache[name]?.uptime || 0);
14291474
protoBreakdownCache[name] = {
1430-
count,
1475+
count, uptime,
14311476
pct: Number(((count * 100) / total).toFixed(2))
14321477
};
14331478
});
14341479
}
14351480
}
14361481

14371482
function renderProtoStats() {
1438-
const uptimeMinutes = Number(uptimeMinutesCache) || 0;
14391483
const rows = TRACKED_PROTOCOLS.map(proto => {
14401484
const count = Number(protoBreakdownCache?.[proto]?.count || 0);
14411485
const pct = Number(protoBreakdownCache?.[proto]?.pct || 0);
1442-
const kpm = uptimeMinutes > 0 ? (count / uptimeMinutes) : 0;
1486+
const protoUptime = Number(protoBreakdownCache?.[proto]?.uptime || 0);
1487+
const kpm = protoUptime > 0 ? (count / protoUptime) : 0;
14431488
return { proto, count, pct, kpm };
14441489
})
14451490
.sort((a, b) => (b.kpm - a.kpm) || (b.count - a.count) || a.proto.localeCompare(b.proto));
@@ -1458,7 +1503,7 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
14581503
</div>
14591504
`).join('');
14601505

1461-
const footer = `<div style="margin-top:8px;color:var(--ui-grey-dim);font-size:0.9em;">Uptime basis: ${uptimeMinutes.toLocaleString()} min</div>`;
1506+
const footer = '';
14621507
const html = header + body + footer;
14631508
['d-proto-stats', 'm-proto-stats'].forEach(id => {
14641509
const el = document.getElementById(id);
@@ -2460,8 +2505,8 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
24602505
},
24612506
{
24622507
name: 'mail_envelope',
2463-
match: (d, proto) => proto === 'MAIL',
2464-
render: d => renderFromToBlock(d, 'mail_from', 'mail_to', true),
2508+
match: (d, proto) => proto === 'MAIL' && SMTP_MESSAGE_STAGES.has(String(d.smtp_stage || '')),
2509+
render: d => renderFromToBlock(d, 'smtp_mail_from', 'smtp_rcpt_to', true),
24652510
},
24662511
{
24672512
name: 'rdp_username',
@@ -2763,14 +2808,14 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
27632808
const d = msg.data;
27642809
if (msg.type === 'new_knock') {
27652810
knockCount++;
2811+
const knockProto = (d.proto || 'SSH').toUpperCase();
27662812
debugLog(`Knock #${knockCount} from ${d.iso || '??'}`);
2767-
playClick();
2813+
if (activeProtocols.includes(knockProto)) playClick();
27682814
if (showProtoChip) {
27692815
pulseProtoChip(d.proto, DESKTOP_LATEST_PROTO_CHIP);
27702816
pulseProtoChip(d.proto, MOBILE_LATEST_PROTO_CHIP);
27712817
}
27722818
if (knockMatchesFilter(d)) pulseGlobeAtmosphere();
2773-
const knockProto = (d.proto || 'SSH').toUpperCase();
27742819
const now = Math.floor(Date.now() / 1000);
27752820
lastKnockTime = now;
27762821
lastKnockTimeByProto[knockProto] = now;
@@ -2796,8 +2841,8 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
27962841
if (hasUserField(d)) incrementAndSort('user', d.user);
27972842
incrementAndSort('ip', d.ip);
27982843
}
2799-
refreshStatsAndTrivia();
28002844
if (knockMatchesFilter(d)) {
2845+
refreshStatsAndTrivia();
28012846
updateGlobeLocation(d);
28022847
rotateGlobeToLocation(d.lat, d.lng);
28032848
}
@@ -2818,16 +2863,11 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
28182863
if (d.proto_stats) protoStatsCache = d.proto_stats;
28192864
protoBreakdownCache = normalizeProtoBreakdown(d.proto_breakdown || {}, d.total || 0);
28202865

2821-
// Seed per-protocol last knock times from history
2822-
const ph = d.proto_histories || {};
2866+
// Seed per-protocol last knock times from backend
2867+
const plt = d.proto_last_times || {};
28232868
activeProtocols.forEach(p => {
2824-
const first = (ph[p.toLowerCase()] || [])[0];
2825-
if (first?.timestamp) {
2826-
const ts = typeof first.timestamp === 'string'
2827-
? Math.floor(new Date(first.timestamp).getTime() / 1000)
2828-
: Number(first.timestamp);
2829-
if (Number.isFinite(ts)) lastKnockTimeByProto[p] = ts;
2830-
}
2869+
const t = plt[p.toLowerCase()];
2870+
if (t) lastKnockTimeByProto[p] = t;
28312871
});
28322872

28332873
// Update header stats (filtered or global)
@@ -2849,7 +2889,11 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
28492889
}
28502890
applyProtoButtons(getCurrentMode());
28512891
applyProtocolPanelVisibility();
2892+
document.querySelector('.d-nav').style.visibility = 'visible';
2893+
document.querySelector('.m-nav').style.visibility = 'visible';
2894+
document.querySelector('.m-dots').style.visibility = 'visible';
28522895
renderProtoStats();
2896+
updateAboutServerInfo();
28532897
// Refresh Heat extrusion colors/heights without re-setting image or polygon data
28542898
refreshHeatGlobe(getActiveLeaderboards().loc);
28552899

@@ -2876,8 +2920,8 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
28762920
} else {
28772921
hideGlobeDot();
28782922
}
2923+
refreshStatsAndTrivia();
28792924
}
2880-
refreshStatsAndTrivia();
28812925
}
28822926
};
28832927
}

0 commit comments

Comments
 (0)