honeyweb-core 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blocked-ips.json +0 -0
- package/index.js +149 -58
- package/package.json +1 -1
package/blocked-ips.json
ADDED
|
File without changes
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// honeyweb-core/index.js
|
|
1
|
+
// honeyweb-core/index.js (V2 - Full Suite)
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const cheerio = require('cheerio');
|
|
@@ -7,100 +7,191 @@ const { GoogleGenerativeAI } = require("@google/generative-ai");
|
|
|
7
7
|
const DB_FILE = path.join(__dirname, 'blocked-ips.json');
|
|
8
8
|
|
|
9
9
|
// ------------------------------------------------------
|
|
10
|
-
// CONFIGURATION
|
|
10
|
+
// 1. CONFIGURATION
|
|
11
11
|
// ------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
const GOOGLE_API_KEY = process.env.HONEYWEB_API_KEY;
|
|
13
|
+
|
|
14
|
+
if (!GOOGLE_API_KEY) {
|
|
15
|
+
console.error("⚠️ [HoneyWeb] Error: HONEYWEB_API_KEY is missing from environment variables.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const TRAP_PATHS = ['/admin-backup-v2', '/wp-login-hidden', '/db-dump-2024', '/auth/root-access'];
|
|
19
|
+
|
|
20
|
+
// Brute Force Config
|
|
21
|
+
const RATE_LIMIT_WINDOW = 10000; // 10s
|
|
22
|
+
const MAX_REQUESTS = 50;
|
|
23
|
+
const trafficMap = new Map();
|
|
24
|
+
|
|
25
|
+
// NEW: SQL Injection & XSS Patterns (The WAF)
|
|
26
|
+
const MALICIOUS_PATTERNS = [
|
|
27
|
+
/(\%27)|(\')|(\-\-)|(\%23)|(#)/i, // SQLi: ' -- #
|
|
28
|
+
/((\%3D)|(=))[^\n]*((\%27)|(\')|(\-\-)|(\%3B)|(;))/i, // SQLi: = ' ;
|
|
29
|
+
/\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/i, // SQLi: ' OR
|
|
30
|
+
/(<script>)/i, // XSS
|
|
31
|
+
/(alert\()/i // XSS
|
|
21
32
|
];
|
|
22
33
|
|
|
34
|
+
// Ensure DB exists
|
|
23
35
|
if (!fs.existsSync(DB_FILE)) { fs.writeJsonSync(DB_FILE, []); }
|
|
24
36
|
|
|
25
37
|
// ------------------------------------------------------
|
|
26
|
-
// AI
|
|
38
|
+
// 2. AI INTELLIGENCE
|
|
27
39
|
// ------------------------------------------------------
|
|
28
|
-
async function generateThreatReport(ip, userAgent,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
console.log("⚠️ [HoneyWeb] No Google API Key found. Skipping AI Report.");
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.log("🤖 [HoneyWeb] Asking Gemini to analyze the attack...");
|
|
36
|
-
|
|
40
|
+
async function generateThreatReport(ip, userAgent, type, details) {
|
|
41
|
+
if (!GOOGLE_API_KEY || GOOGLE_API_KEY.includes('YOUR_KEY')) return;
|
|
42
|
+
console.log(`🤖 [HoneyWeb] Asking Gemini to analyze: ${type}...`);
|
|
37
43
|
try {
|
|
38
44
|
const genAI = new GoogleGenerativeAI(GOOGLE_API_KEY);
|
|
39
|
-
|
|
40
45
|
const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Attack Details:
|
|
46
|
-
- IP Address: ${ip}
|
|
47
|
-
- User-Agent: ${userAgent}
|
|
48
|
-
- Trap Link Accessed: ${trapPath}
|
|
49
|
-
|
|
50
|
-
Please provide a 1-sentence summary of what kind of threat this is (e.g., reconnaissance, targeted scan) and what they were likely looking for.
|
|
51
|
-
`;
|
|
52
|
-
|
|
46
|
+
const prompt = `Cybersecurity Alert. Analyze this blocked attack.
|
|
47
|
+
Type: ${type} | IP: ${ip} | UA: ${userAgent} | Detail: ${details}
|
|
48
|
+
Respond in 1 sentence: What is the attacker trying to exploit?`;
|
|
49
|
+
|
|
53
50
|
const result = await model.generateContent(prompt);
|
|
54
|
-
const report = result.response.text();
|
|
55
|
-
|
|
56
51
|
console.log("\n📝 [HoneyWeb INTELLIGENCE REPORT]");
|
|
57
52
|
console.log("---------------------------------------------------");
|
|
58
|
-
console.log(
|
|
53
|
+
console.log(result.response.text().trim());
|
|
59
54
|
console.log("---------------------------------------------------\n");
|
|
55
|
+
} catch (e) { console.error("❌ Gemini Error:", e.message); }
|
|
56
|
+
}
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
// ------------------------------------------------------
|
|
59
|
+
// 3. HELPER: BAN SYSTEM
|
|
60
|
+
// ------------------------------------------------------
|
|
61
|
+
async function banIP(ip, reason) {
|
|
62
|
+
let list = [];
|
|
63
|
+
try { list = await fs.readJson(DB_FILE); } catch (e) {}
|
|
64
|
+
|
|
65
|
+
// Check if IP is already in the list (simple check)
|
|
66
|
+
const exists = list.some(entry => entry.ip === ip);
|
|
67
|
+
|
|
68
|
+
if (!exists) {
|
|
69
|
+
list.push({ ip, reason, date: new Date().toISOString() });
|
|
70
|
+
await fs.writeJson(DB_FILE, list);
|
|
71
|
+
console.log(`🚫 [HoneyWeb] BANNED IP: ${ip} | Reason: ${reason}`);
|
|
72
|
+
return true;
|
|
64
73
|
}
|
|
74
|
+
return false;
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
// ------------------------------------------------------
|
|
68
|
-
// MIDDLEWARE
|
|
78
|
+
// 4. MAIN MIDDLEWARE
|
|
69
79
|
// ------------------------------------------------------
|
|
70
80
|
function honeyWeb() {
|
|
71
81
|
return async (req, res, next) => {
|
|
72
82
|
const clientIP = req.ip || req.connection.remoteAddress;
|
|
83
|
+
const userAgent = req.headers['user-agent'] || 'Unknown';
|
|
84
|
+
|
|
85
|
+
// A. STATUS DASHBOARD
|
|
86
|
+
if (req.path === '/honeyweb-status') {
|
|
87
|
+
// Insert your own secret key here
|
|
88
|
+
// Usage: http://localhost:3000/honeyweb-status?key=admin123
|
|
89
|
+
if (req.query.key !== 'admin123') {
|
|
90
|
+
return res.status(404).send('Not Found'); // Pretend it doesn't exist
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const list = await fs.readJson(DB_FILE);
|
|
94
|
+
return res.send(`
|
|
95
|
+
<html><body style="font-family:monospace; background:#222; color:#0f0; padding:20px;">
|
|
96
|
+
<h1>🛡️ HoneyWeb Active Defense Status</h1>
|
|
97
|
+
<p>Total Banned IPs: ${list.length}</p>
|
|
98
|
+
<table border="1" style="border-collapse:collapse; width:100%; color:#fff;">
|
|
99
|
+
<tr><th>IP</th><th>Reason</th><th>Time</th></tr>
|
|
100
|
+
${list.map(i => `<tr><td>${i.ip}</td><td>${i.reason}</td><td>${i.date}</td></tr>`).join('')}
|
|
101
|
+
</table></body></html>
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
73
104
|
|
|
74
|
-
//
|
|
75
|
-
let
|
|
76
|
-
try {
|
|
105
|
+
// B. CHECK BLOCKLIST
|
|
106
|
+
let bannedList = [];
|
|
107
|
+
try { bannedList = await fs.readJson(DB_FILE); } catch (e) {}
|
|
108
|
+
if (bannedList.some(entry => entry.ip === clientIP)) {
|
|
109
|
+
return res.status(403).send('<h1>403 Forbidden</h1><p>Your IP is flagged by HoneyWeb.</p>');
|
|
110
|
+
}
|
|
77
111
|
|
|
78
|
-
|
|
79
|
-
|
|
112
|
+
// C. ANTI-BRUTE FORCE (Rate Limit)
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
if (!trafficMap.has(clientIP)) trafficMap.set(clientIP, { count: 1, start: now });
|
|
115
|
+
else {
|
|
116
|
+
const data = trafficMap.get(clientIP);
|
|
117
|
+
if (now - data.start > RATE_LIMIT_WINDOW) trafficMap.set(clientIP, { count: 1, start: now });
|
|
118
|
+
else {
|
|
119
|
+
data.count++;
|
|
120
|
+
if (data.count > MAX_REQUESTS) {
|
|
121
|
+
if (await banIP(clientIP, "Brute Force (Rate Limit Exceeded)")) {
|
|
122
|
+
generateThreatReport(clientIP, userAgent, "Volumetric Attack", `> ${MAX_REQUESTS} reqs/10s`);
|
|
123
|
+
}
|
|
124
|
+
return res.status(429).send('Too Many Requests');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
80
127
|
}
|
|
81
128
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
129
|
+
// D. BAIT PARAMETER ANALYSIS (Smarter WAF)
|
|
130
|
+
|
|
131
|
+
const checkPayload = (str) => {
|
|
132
|
+
if (!str) return false;
|
|
133
|
+
return MALICIOUS_PATTERNS.some(regex => regex.test(str));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Scan URL Query Params (?id=1 OR 1=1)
|
|
137
|
+
if (checkPayload(req.originalUrl)) {
|
|
138
|
+
if (await banIP(clientIP, "Malicious URL Payload")) {
|
|
139
|
+
generateThreatReport(clientIP, userAgent, "Injection Attack", `Dirty URL: ${req.originalUrl}`);
|
|
89
140
|
}
|
|
141
|
+
return res.status(403).send('Forbidden');
|
|
142
|
+
}
|
|
90
143
|
|
|
91
|
-
|
|
92
|
-
|
|
144
|
+
// Scan specific "Login" fields only (Reduces false positives on other forms)
|
|
145
|
+
if (req.method === 'POST' && req.body) {
|
|
146
|
+
const riskyInputs = [req.body.username, req.body.password, req.body.search];
|
|
147
|
+
if (riskyInputs.some(input => checkPayload(input))) {
|
|
148
|
+
if (await banIP(clientIP, "Malicious Form Data")) {
|
|
149
|
+
generateThreatReport(clientIP, userAgent, "SQL Injection", `Payload in form: ${JSON.stringify(req.body)}`);
|
|
150
|
+
}
|
|
151
|
+
return res.status(403).send('Forbidden');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
93
154
|
|
|
155
|
+
// E. HONEYPOT TRAP
|
|
156
|
+
if (TRAP_PATHS.includes(req.path)) {
|
|
157
|
+
if (await banIP(clientIP, "Trap Triggered")) {
|
|
158
|
+
generateThreatReport(clientIP, userAgent, "Reconnaissance", `Accessed trap: ${req.path}`);
|
|
159
|
+
}
|
|
94
160
|
return res.status(403).send('<h1>403 Forbidden</h1>');
|
|
95
161
|
}
|
|
96
162
|
|
|
97
|
-
//
|
|
163
|
+
// F. INJECTION
|
|
98
164
|
const originalSend = res.send;
|
|
99
165
|
res.send = function (body) {
|
|
100
|
-
if (typeof body === 'string' && (body.includes('<
|
|
166
|
+
if (typeof body === 'string' && (body.includes('<body') || body.includes('<html'))) {
|
|
101
167
|
const $ = cheerio.load(body);
|
|
168
|
+
|
|
169
|
+
// Select a random trap URL
|
|
102
170
|
const trapUrl = TRAP_PATHS[Math.floor(Math.random() * TRAP_PATHS.length)];
|
|
103
|
-
|
|
171
|
+
|
|
172
|
+
// Inject CSS that mimics standard Accessibility Hiding (Bootstrap/Tailwind style)
|
|
173
|
+
// This reduces the element to a 1x1 pixel clip that is technically "visible" but unclickable.
|
|
174
|
+
const cssCamouflage = `
|
|
175
|
+
<style>
|
|
176
|
+
.sr-only-utility {
|
|
177
|
+
position: absolute;
|
|
178
|
+
width: 1px;
|
|
179
|
+
height: 1px;
|
|
180
|
+
padding: 0;
|
|
181
|
+
margin: -1px;
|
|
182
|
+
overflow: hidden;
|
|
183
|
+
clip: rect(0, 0, 0, 0);
|
|
184
|
+
white-space: nowrap;
|
|
185
|
+
border: 0;
|
|
186
|
+
}
|
|
187
|
+
</style>
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
// Inject the link with a boring name ("Skip to content", "Toolbar")
|
|
191
|
+
// A bot sees this as a valid navigation aid.
|
|
192
|
+
$('head').append(cssCamouflage);
|
|
193
|
+
$('body').prepend(`<a href="${trapUrl}" class="sr-only-utility">Skip to Content</a>`);
|
|
194
|
+
|
|
104
195
|
body = $.html();
|
|
105
196
|
}
|
|
106
197
|
originalSend.call(this, body);
|