@panguard-ai/panguard-trap 0.1.0

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.
Files changed (69) hide show
  1. package/dist/cli/index.d.ts +45 -0
  2. package/dist/cli/index.d.ts.map +1 -0
  3. package/dist/cli/index.js +298 -0
  4. package/dist/cli/index.js.map +1 -0
  5. package/dist/index.d.ts +23 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +26 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/intel/index.d.ts +38 -0
  10. package/dist/intel/index.d.ts.map +1 -0
  11. package/dist/intel/index.js +157 -0
  12. package/dist/intel/index.js.map +1 -0
  13. package/dist/profiler/attacker-profiler.d.ts +68 -0
  14. package/dist/profiler/attacker-profiler.d.ts.map +1 -0
  15. package/dist/profiler/attacker-profiler.js +316 -0
  16. package/dist/profiler/attacker-profiler.js.map +1 -0
  17. package/dist/profiler/index.d.ts +8 -0
  18. package/dist/profiler/index.d.ts.map +1 -0
  19. package/dist/profiler/index.js +8 -0
  20. package/dist/profiler/index.js.map +1 -0
  21. package/dist/services/base-service.d.ts +61 -0
  22. package/dist/services/base-service.d.ts.map +1 -0
  23. package/dist/services/base-service.js +190 -0
  24. package/dist/services/base-service.js.map +1 -0
  25. package/dist/services/generic-trap.d.ts +22 -0
  26. package/dist/services/generic-trap.d.ts.map +1 -0
  27. package/dist/services/generic-trap.js +439 -0
  28. package/dist/services/generic-trap.js.map +1 -0
  29. package/dist/services/http-trap.d.ts +36 -0
  30. package/dist/services/http-trap.d.ts.map +1 -0
  31. package/dist/services/http-trap.js +218 -0
  32. package/dist/services/http-trap.js.map +1 -0
  33. package/dist/services/index.d.ts +26 -0
  34. package/dist/services/index.d.ts.map +1 -0
  35. package/dist/services/index.js +52 -0
  36. package/dist/services/index.js.map +1 -0
  37. package/dist/services/mysql-trap.d.ts +22 -0
  38. package/dist/services/mysql-trap.d.ts.map +1 -0
  39. package/dist/services/mysql-trap.js +374 -0
  40. package/dist/services/mysql-trap.js.map +1 -0
  41. package/dist/services/rdp-trap.d.ts +21 -0
  42. package/dist/services/rdp-trap.d.ts.map +1 -0
  43. package/dist/services/rdp-trap.js +299 -0
  44. package/dist/services/rdp-trap.js.map +1 -0
  45. package/dist/services/redis-trap.d.ts +21 -0
  46. package/dist/services/redis-trap.d.ts.map +1 -0
  47. package/dist/services/redis-trap.js +321 -0
  48. package/dist/services/redis-trap.js.map +1 -0
  49. package/dist/services/smb-trap.d.ts +21 -0
  50. package/dist/services/smb-trap.d.ts.map +1 -0
  51. package/dist/services/smb-trap.js +358 -0
  52. package/dist/services/smb-trap.js.map +1 -0
  53. package/dist/services/ssh-trap.d.ts +43 -0
  54. package/dist/services/ssh-trap.d.ts.map +1 -0
  55. package/dist/services/ssh-trap.js +397 -0
  56. package/dist/services/ssh-trap.js.map +1 -0
  57. package/dist/threat-cloud-uploader.d.ts +48 -0
  58. package/dist/threat-cloud-uploader.d.ts.map +1 -0
  59. package/dist/threat-cloud-uploader.js +125 -0
  60. package/dist/threat-cloud-uploader.js.map +1 -0
  61. package/dist/trap-engine.d.ts +80 -0
  62. package/dist/trap-engine.d.ts.map +1 -0
  63. package/dist/trap-engine.js +279 -0
  64. package/dist/trap-engine.js.map +1 -0
  65. package/dist/types.d.ts +229 -0
  66. package/dist/types.d.ts.map +1 -0
  67. package/dist/types.js +89 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +37 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * RDP (X.224 + CredSSP) Protocol Honeypot
3
+ * RDP (X.224 + CredSSP) 協議蜜罐
4
+ *
5
+ * Speaks real RDP initial negotiation:
6
+ * - Parses X.224 Connection Request (CR) with cookie/username
7
+ * - Sends X.224 Connection Confirm (CC) with NLA/CredSSP
8
+ * - Captures TLS ClientHello / CredSSP auth attempts
9
+ *
10
+ * @module @panguard-ai/panguard-trap/services/rdp-trap
11
+ */
12
+ import { createLogger } from '@panguard-ai/core';
13
+ import { BaseTrapService } from './base-service.js';
14
+ const logger = createLogger('panguard-trap:service:rdp');
15
+ // TPKT header (RFC 1006)
16
+ const TPKT_VERSION = 3;
17
+ // X.224 types
18
+ const X224_CONNECTION_REQUEST = 0xe0;
19
+ const X224_CONNECTION_CONFIRM = 0xd0;
20
+ // RDP Negotiation types
21
+ const TYPE_RDP_NEG_REQ = 0x01;
22
+ const TYPE_RDP_NEG_RSP = 0x02;
23
+ const TYPE_RDP_NEG_FAILURE = 0x03;
24
+ // Negotiation protocols
25
+ const PROTOCOL_RDP = 0x00000000;
26
+ const PROTOCOL_SSL = 0x00000001;
27
+ const PROTOCOL_HYBRID = 0x00000002; // CredSSP (NLA)
28
+ // Failure codes
29
+ const SSL_REQUIRED_BY_SERVER = 0x00000001;
30
+ // ---------------------------------------------------------------------------
31
+ // Packet Builders
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * Build TPKT + X.224 Connection Confirm with RDP Negotiation Response.
35
+ *
36
+ * The response tells the client which security protocol the server wants.
37
+ * We respond with CredSSP/NLA to force the client to attempt authentication,
38
+ * which reveals credentials in the TLS/CredSSP handshake.
39
+ */
40
+ function buildConnectionConfirm(requestedProtocol) {
41
+ // X.224 CC payload (without TPKT header)
42
+ // X.224 header: length indicator (1), CC (1), DST-REF (2), SRC-REF (2), class (1) = 7 bytes
43
+ // RDP Neg Response: type (1), flags (1), length (2), selectedProtocol (4) = 8 bytes
44
+ const useNLA = (requestedProtocol & PROTOCOL_HYBRID) !== 0;
45
+ const useSSL = (requestedProtocol & PROTOCOL_SSL) !== 0;
46
+ let selectedProtocol;
47
+ if (useNLA) {
48
+ selectedProtocol = PROTOCOL_HYBRID;
49
+ }
50
+ else if (useSSL) {
51
+ selectedProtocol = PROTOCOL_SSL;
52
+ }
53
+ else {
54
+ selectedProtocol = PROTOCOL_RDP;
55
+ }
56
+ // Build X.224 CC + RDP Neg RSP
57
+ const x224Len = 7 + 8; // 15 bytes
58
+ const x224 = Buffer.alloc(x224Len);
59
+ let pos = 0;
60
+ // X.224 Connection Confirm
61
+ x224[pos++] = x224Len - 1; // Length indicator (excluding itself)
62
+ x224[pos++] = X224_CONNECTION_CONFIRM; // CC
63
+ x224[pos++] = 0x00;
64
+ x224[pos++] = 0x00; // DST-REF
65
+ x224[pos++] = 0x00;
66
+ x224[pos++] = 0x00; // SRC-REF
67
+ x224[pos++] = 0x00; // Class/Options
68
+ // RDP Negotiation Response
69
+ x224[pos++] = TYPE_RDP_NEG_RSP;
70
+ x224[pos++] = 0x00; // Flags
71
+ x224[pos++] = 0x08;
72
+ x224[pos++] = 0x00; // Length (8)
73
+ x224[pos++] = selectedProtocol & 0xff;
74
+ x224[pos++] = (selectedProtocol >> 8) & 0xff;
75
+ x224[pos++] = (selectedProtocol >> 16) & 0xff;
76
+ x224[pos++] = (selectedProtocol >> 24) & 0xff;
77
+ // TPKT header
78
+ const tpktLen = 4 + x224Len;
79
+ const tpkt = Buffer.alloc(4);
80
+ tpkt[0] = TPKT_VERSION;
81
+ tpkt[1] = 0x00; // Reserved
82
+ tpkt[2] = (tpktLen >> 8) & 0xff;
83
+ tpkt[3] = tpktLen & 0xff;
84
+ return Buffer.concat([tpkt, x224]);
85
+ }
86
+ /** Build RDP Negotiation Failure response */
87
+ function buildNegFailure(failureCode) {
88
+ const x224Len = 7 + 8;
89
+ const x224 = Buffer.alloc(x224Len);
90
+ let pos = 0;
91
+ x224[pos++] = x224Len - 1;
92
+ x224[pos++] = X224_CONNECTION_CONFIRM;
93
+ x224[pos++] = 0x00;
94
+ x224[pos++] = 0x00;
95
+ x224[pos++] = 0x00;
96
+ x224[pos++] = 0x00;
97
+ x224[pos++] = 0x00;
98
+ x224[pos++] = TYPE_RDP_NEG_FAILURE;
99
+ x224[pos++] = 0x00;
100
+ x224[pos++] = 0x08;
101
+ x224[pos++] = 0x00;
102
+ x224[pos++] = failureCode & 0xff;
103
+ x224[pos++] = (failureCode >> 8) & 0xff;
104
+ x224[pos++] = (failureCode >> 16) & 0xff;
105
+ x224[pos++] = (failureCode >> 24) & 0xff;
106
+ const tpktLen = 4 + x224Len;
107
+ const tpkt = Buffer.alloc(4);
108
+ tpkt[0] = TPKT_VERSION;
109
+ tpkt[1] = 0x00;
110
+ tpkt[2] = (tpktLen >> 8) & 0xff;
111
+ tpkt[3] = tpktLen & 0xff;
112
+ return Buffer.concat([tpkt, x224]);
113
+ }
114
+ /**
115
+ * Parse X.224 Connection Request to extract:
116
+ * - Cookie (mstshash=username)
117
+ * - Requested security protocols
118
+ */
119
+ function parseConnectionRequest(data) {
120
+ const result = {
121
+ cookie: null,
122
+ username: null,
123
+ requestedProtocol: 0,
124
+ };
125
+ if (data.length < 11)
126
+ return result;
127
+ // Skip TPKT (4 bytes) + X.224 length indicator (1) + CR code (1) + DST-REF (2) + SRC-REF (2) + class (1)
128
+ const pos = 4 + 1 + 1 + 2 + 2 + 1; // = 11
129
+ // Look for cookie and RDP Neg Req in the remaining data
130
+ const remaining = data.subarray(pos);
131
+ const str = remaining.toString('ascii');
132
+ // Extract cookie (Cookie: mstshash=username\r\n)
133
+ const cookieMatch = str.match(/Cookie:\s*mstshash=([^\r\n]+)/i);
134
+ if (cookieMatch) {
135
+ result.cookie = cookieMatch[1].trim();
136
+ result.username = result.cookie;
137
+ }
138
+ // Look for RDP Negotiation Request at the end
139
+ // Find TYPE_RDP_NEG_REQ byte
140
+ for (let i = remaining.length - 8; i >= 0; i--) {
141
+ if (remaining[i] === TYPE_RDP_NEG_REQ &&
142
+ remaining[i + 2] === 0x08 &&
143
+ remaining[i + 3] === 0x00) {
144
+ result.requestedProtocol = remaining.readUInt32LE(i + 4);
145
+ break;
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // RDP Trap Service
152
+ // ---------------------------------------------------------------------------
153
+ export class RDPTrapService extends BaseTrapService {
154
+ server = null;
155
+ constructor(config) {
156
+ super({ ...config, type: 'rdp' });
157
+ }
158
+ async doStart() {
159
+ const net = await import('node:net');
160
+ this.server = net.createServer((socket) => this.handleConnection(socket));
161
+ return new Promise((resolve, reject) => {
162
+ this.server.listen(this.config.port, () => resolve());
163
+ this.server.on('error', reject);
164
+ });
165
+ }
166
+ async doStop() {
167
+ return new Promise((resolve) => {
168
+ if (this.server) {
169
+ this.server.close(() => resolve());
170
+ }
171
+ else {
172
+ resolve();
173
+ }
174
+ });
175
+ }
176
+ handleConnection(socket) {
177
+ const remoteIP = socket.remoteAddress ?? 'unknown';
178
+ const remotePort = socket.remotePort ?? 0;
179
+ const session = this.createSession(remoteIP, remotePort);
180
+ this.addMitreTechnique(session.sessionId, 'T1021.001'); // RDP
181
+ let phase = 'x224';
182
+ let dataCount = 0;
183
+ const timeout = this.config.sessionTimeoutMs ?? 60_000;
184
+ socket.setTimeout(timeout);
185
+ socket.on('data', (data) => {
186
+ dataCount++;
187
+ if (phase === 'x224') {
188
+ // Expect X.224 Connection Request
189
+ if (data.length < 11)
190
+ return;
191
+ // Check TPKT header
192
+ if (data[0] !== TPKT_VERSION) {
193
+ // Not a valid TPKT, might be a scanner sending garbage
194
+ this.recordEvent(session.sessionId, 'exploit_attempt', 'Non-TPKT data received', {
195
+ rawHex: data.subarray(0, 32).toString('hex'),
196
+ });
197
+ return;
198
+ }
199
+ // Check X.224 CR
200
+ const x224Type = data[5];
201
+ if (x224Type !== X224_CONNECTION_REQUEST) {
202
+ return;
203
+ }
204
+ const parsed = parseConnectionRequest(data);
205
+ if (parsed.username) {
206
+ this.recordCredential(session.sessionId, parsed.username, '***RDP_COOKIE***', false);
207
+ this.recordEvent(session.sessionId, 'authentication_attempt', `RDP cookie: ${parsed.username}`, {
208
+ cookie: parsed.cookie,
209
+ });
210
+ }
211
+ this.recordCommand(session.sessionId, `X224_CR protocol=${parsed.requestedProtocol}`);
212
+ // Send Connection Confirm
213
+ const delay = this.config.responseDelayMs ?? 200;
214
+ setTimeout(() => {
215
+ try {
216
+ if (parsed.requestedProtocol & PROTOCOL_HYBRID) {
217
+ // Client supports NLA - send confirm, they'll attempt CredSSP/TLS
218
+ socket.write(buildConnectionConfirm(parsed.requestedProtocol));
219
+ phase = 'tls';
220
+ }
221
+ else if (parsed.requestedProtocol & PROTOCOL_SSL) {
222
+ // Client wants SSL only - send confirm
223
+ socket.write(buildConnectionConfirm(parsed.requestedProtocol));
224
+ phase = 'tls';
225
+ }
226
+ else {
227
+ // Plain RDP - send failure requiring SSL
228
+ socket.write(buildNegFailure(SSL_REQUIRED_BY_SERVER));
229
+ socket.end();
230
+ }
231
+ }
232
+ catch {
233
+ // socket closed
234
+ }
235
+ }, delay);
236
+ return;
237
+ }
238
+ if (phase === 'tls') {
239
+ // Client will send TLS ClientHello or CredSSP NTLM
240
+ this.recordEvent(session.sessionId, 'authentication_attempt', 'TLS/CredSSP handshake data', {
241
+ dataLength: data.length,
242
+ firstBytes: data.subarray(0, 16).toString('hex'),
243
+ });
244
+ // Check for NTLMSSP in CredSSP
245
+ const ntlmIdx = data.indexOf(Buffer.from('NTLMSSP\0', 'ascii'));
246
+ if (ntlmIdx >= 0) {
247
+ this.addMitreTechnique(session.sessionId, 'T1110');
248
+ // Try to extract username from NTLMSSP Auth (type 3)
249
+ const msgType = data.readUInt32LE(ntlmIdx + 8);
250
+ if (msgType === 3) {
251
+ // NTLMSSP Auth message
252
+ try {
253
+ const userLen = data.readUInt16LE(ntlmIdx + 36);
254
+ const userOff = data.readUInt32LE(ntlmIdx + 40);
255
+ const username = data
256
+ .subarray(ntlmIdx + userOff, ntlmIdx + userOff + userLen)
257
+ .toString('utf16le');
258
+ const domainLen = data.readUInt16LE(ntlmIdx + 28);
259
+ const domainOff = data.readUInt32LE(ntlmIdx + 32);
260
+ const domain = data
261
+ .subarray(ntlmIdx + domainOff, ntlmIdx + domainOff + domainLen)
262
+ .toString('utf16le');
263
+ const fullUser = domain ? `${domain}\\${username}` : username;
264
+ this.recordCredential(session.sessionId, fullUser, '***NTLM_RDP***', false);
265
+ }
266
+ catch {
267
+ // Malformed NTLMSSP
268
+ }
269
+ }
270
+ }
271
+ // After a few data exchanges, close with auth failure
272
+ if (dataCount > 5) {
273
+ phase = 'post_tls';
274
+ setTimeout(() => {
275
+ try {
276
+ socket.end();
277
+ }
278
+ catch {
279
+ /* */
280
+ }
281
+ }, 500);
282
+ }
283
+ return;
284
+ }
285
+ // post_tls - just record and close
286
+ this.recordCommand(session.sessionId, `post_tls_data_len=${data.length}`);
287
+ });
288
+ socket.on('timeout', () => {
289
+ logger.info(`RDP session timeout: ${session.sessionId}`);
290
+ socket.end();
291
+ });
292
+ socket.on('close', () => this.endSession(session.sessionId));
293
+ socket.on('error', (err) => {
294
+ logger.debug(`RDP socket error: ${err.message}`);
295
+ this.endSession(session.sessionId);
296
+ });
297
+ }
298
+ }
299
+ //# sourceMappingURL=rdp-trap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rdp-trap.js","sourceRoot":"","sources":["../../src/services/rdp-trap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Redis RESP Protocol Honeypot
3
+ * Redis RESP 協議蜜罐
4
+ *
5
+ * Speaks the real Redis Serialization Protocol (RESP):
6
+ * - Parses inline commands and RESP bulk strings
7
+ * - Handles AUTH, CONFIG, EVAL, SLAVEOF with realistic responses
8
+ * - Detects exploit patterns (CVE-2022-0543, rogue server, etc.)
9
+ *
10
+ * @module @panguard-ai/panguard-trap/services/redis-trap
11
+ */
12
+ import type { TrapServiceConfig } from '../types.js';
13
+ import { BaseTrapService } from './base-service.js';
14
+ export declare class RedisTrapService extends BaseTrapService {
15
+ private server;
16
+ constructor(config: TrapServiceConfig);
17
+ protected doStart(): Promise<void>;
18
+ protected doStop(): Promise<void>;
19
+ private handleConnection;
20
+ }
21
+ //# sourceMappingURL=redis-trap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-trap.d.ts","sourceRoot":"","sources":["../../src/services/redis-trap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAsIpD,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,OAAO,CAAC,MAAM,CAAmE;gBAErE,MAAM,EAAE,iBAAiB;cAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;cAUxB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAUvC,OAAO,CAAC,gBAAgB;CAgMzB"}
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Redis RESP Protocol Honeypot
3
+ * Redis RESP 協議蜜罐
4
+ *
5
+ * Speaks the real Redis Serialization Protocol (RESP):
6
+ * - Parses inline commands and RESP bulk strings
7
+ * - Handles AUTH, CONFIG, EVAL, SLAVEOF with realistic responses
8
+ * - Detects exploit patterns (CVE-2022-0543, rogue server, etc.)
9
+ *
10
+ * @module @panguard-ai/panguard-trap/services/redis-trap
11
+ */
12
+ import { createLogger } from '@panguard-ai/core';
13
+ import { BaseTrapService } from './base-service.js';
14
+ const logger = createLogger('panguard-trap:service:redis');
15
+ // ---------------------------------------------------------------------------
16
+ // RESP builder helpers
17
+ // ---------------------------------------------------------------------------
18
+ function respSimple(msg) {
19
+ return `+${msg}\r\n`;
20
+ }
21
+ function respError(msg) {
22
+ return `-${msg}\r\n`;
23
+ }
24
+ function respInt(n) {
25
+ return `:${n}\r\n`;
26
+ }
27
+ function respBulk(s) {
28
+ return `$${Buffer.byteLength(s)}\r\n${s}\r\n`;
29
+ }
30
+ function respNullBulk() {
31
+ return '$-1\r\n';
32
+ }
33
+ function respArray(items) {
34
+ let out = `*${items.length}\r\n`;
35
+ for (const item of items) {
36
+ out += respBulk(item);
37
+ }
38
+ return out;
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // RESP parser - handles both inline and multibulk commands
42
+ // ---------------------------------------------------------------------------
43
+ function parseRESP(buf) {
44
+ const commands = [];
45
+ const lines = buf.split('\r\n');
46
+ let i = 0;
47
+ while (i < lines.length) {
48
+ const line = lines[i];
49
+ if (line.length === 0) {
50
+ i++;
51
+ continue;
52
+ }
53
+ if (line.startsWith('*')) {
54
+ // Multibulk
55
+ const count = parseInt(line.slice(1), 10);
56
+ if (isNaN(count) || count < 0) {
57
+ i++;
58
+ continue;
59
+ }
60
+ const args = [];
61
+ i++;
62
+ for (let j = 0; j < count && i < lines.length; j++) {
63
+ const header = lines[i];
64
+ if (header.startsWith('$')) {
65
+ i++;
66
+ if (i < lines.length) {
67
+ args.push(lines[i]);
68
+ i++;
69
+ }
70
+ }
71
+ else {
72
+ args.push(header);
73
+ i++;
74
+ }
75
+ }
76
+ if (args.length > 0)
77
+ commands.push(args);
78
+ }
79
+ else {
80
+ // Inline command
81
+ const parts = line.trim().split(/\s+/);
82
+ if (parts.length > 0 && parts[0]) {
83
+ commands.push(parts);
84
+ }
85
+ i++;
86
+ }
87
+ }
88
+ return commands;
89
+ }
90
+ // ---------------------------------------------------------------------------
91
+ // MITRE patterns for Redis exploitation
92
+ // ---------------------------------------------------------------------------
93
+ const REDIS_MITRE_PATTERNS = [
94
+ [/CONFIG\s+SET\s+dir/i, 'T1059'], // Arbitrary file write
95
+ [/CONFIG\s+SET\s+dbfilename/i, 'T1059'],
96
+ [/SLAVEOF|REPLICAOF/i, 'T1219'], // Rogue server / C2
97
+ [/MODULE\s+LOAD/i, 'T1129'], // Shared modules
98
+ [/EVAL.*os\.execute/i, 'T1059'], // Lua RCE (CVE-2022-0543)
99
+ [/EVAL.*io\.popen/i, 'T1059'],
100
+ [/EVAL.*loadlib/i, 'T1059'],
101
+ [/DEBUG\s+SET-ACTIVE-EXPIRE/i, 'T1499'], // DoS
102
+ [/FLUSHALL|FLUSHDB/i, 'T1485'], // Data destruction
103
+ [/BGSAVE|SAVE/i, 'T1005'], // Data access
104
+ ];
105
+ // ---------------------------------------------------------------------------
106
+ // Fake INFO response
107
+ // ---------------------------------------------------------------------------
108
+ const FAKE_INFO = [
109
+ '# Server',
110
+ 'redis_version:6.2.14',
111
+ 'redis_git_sha1:00000000',
112
+ 'redis_mode:standalone',
113
+ 'os:Linux 5.15.0-91-generic x86_64',
114
+ 'arch_bits:64',
115
+ 'tcp_port:6379',
116
+ 'uptime_in_seconds:172800',
117
+ 'uptime_in_days:2',
118
+ '',
119
+ '# Clients',
120
+ 'connected_clients:1',
121
+ 'blocked_clients:0',
122
+ '',
123
+ '# Memory',
124
+ 'used_memory:1024000',
125
+ 'used_memory_human:1000.00K',
126
+ 'used_memory_peak:2048000',
127
+ '',
128
+ '# Keyspace',
129
+ 'db0:keys=42,expires=5,avg_ttl=86400000',
130
+ ].join('\r\n');
131
+ // ---------------------------------------------------------------------------
132
+ // Redis Trap Service
133
+ // ---------------------------------------------------------------------------
134
+ export class RedisTrapService extends BaseTrapService {
135
+ server = null;
136
+ constructor(config) {
137
+ super({ ...config, type: 'redis' });
138
+ }
139
+ async doStart() {
140
+ const net = await import('node:net');
141
+ this.server = net.createServer((socket) => this.handleConnection(socket));
142
+ return new Promise((resolve, reject) => {
143
+ this.server.listen(this.config.port, () => resolve());
144
+ this.server.on('error', reject);
145
+ });
146
+ }
147
+ async doStop() {
148
+ return new Promise((resolve) => {
149
+ if (this.server) {
150
+ this.server.close(() => resolve());
151
+ }
152
+ else {
153
+ resolve();
154
+ }
155
+ });
156
+ }
157
+ handleConnection(socket) {
158
+ const remoteIP = socket.remoteAddress ?? 'unknown';
159
+ const remotePort = socket.remotePort ?? 0;
160
+ const session = this.createSession(remoteIP, remotePort);
161
+ this.addMitreTechnique(session.sessionId, 'T1190'); // Exploit public-facing
162
+ let authAttempts = 0;
163
+ const timeout = this.config.sessionTimeoutMs ?? 30_000;
164
+ socket.setTimeout(timeout);
165
+ // Redis has no banner - waits for client command
166
+ socket.on('data', (data) => {
167
+ const raw = data.toString('utf-8');
168
+ const commands = parseRESP(raw);
169
+ for (const args of commands) {
170
+ if (args.length === 0)
171
+ continue;
172
+ const cmd = args[0].toUpperCase();
173
+ const fullCmd = args.join(' ');
174
+ this.recordCommand(session.sessionId, fullCmd);
175
+ // MITRE detection
176
+ for (const [pattern, technique] of REDIS_MITRE_PATTERNS) {
177
+ if (pattern.test(fullCmd)) {
178
+ this.addMitreTechnique(session.sessionId, technique);
179
+ this.recordEvent(session.sessionId, 'exploit_attempt', fullCmd, { technique });
180
+ }
181
+ }
182
+ const delay = this.config.responseDelayMs ?? 30;
183
+ const respond = (resp) => {
184
+ setTimeout(() => {
185
+ try {
186
+ socket.write(resp);
187
+ }
188
+ catch {
189
+ /* closed */
190
+ }
191
+ }, delay);
192
+ };
193
+ // Handle AUTH
194
+ if (cmd === 'AUTH') {
195
+ authAttempts++;
196
+ const password = args[1] ?? '';
197
+ this.recordCredential(session.sessionId, 'default', password, authAttempts >= 3);
198
+ if (authAttempts >= 3) {
199
+ this.addMitreTechnique(session.sessionId, 'T1078');
200
+ respond(respSimple('OK'));
201
+ }
202
+ else {
203
+ respond(respError('WRONGPASS invalid username-password pair'));
204
+ }
205
+ continue;
206
+ }
207
+ if (cmd === 'QUIT') {
208
+ try {
209
+ socket.write(respSimple('OK'));
210
+ }
211
+ catch {
212
+ /* */
213
+ }
214
+ socket.end();
215
+ return;
216
+ }
217
+ if (cmd === 'PING') {
218
+ respond(args[1] ? respBulk(args[1]) : respSimple('PONG'));
219
+ continue;
220
+ }
221
+ if (cmd === 'ECHO' && args[1]) {
222
+ respond(respBulk(args[1]));
223
+ continue;
224
+ }
225
+ if (cmd === 'INFO') {
226
+ respond(respBulk(FAKE_INFO));
227
+ continue;
228
+ }
229
+ if (cmd === 'DBSIZE') {
230
+ respond(respInt(42));
231
+ continue;
232
+ }
233
+ if (cmd === 'CONFIG') {
234
+ const sub = args[1]?.toUpperCase();
235
+ if (sub === 'SET') {
236
+ respond(respError('ERR Insufficient permissions to modify config'));
237
+ }
238
+ else if (sub === 'GET') {
239
+ const key = args[2] ?? '*';
240
+ respond(respArray([key, '']));
241
+ }
242
+ else {
243
+ respond(respSimple('OK'));
244
+ }
245
+ continue;
246
+ }
247
+ if (cmd === 'KEYS') {
248
+ respond(respArray(['session:abc123', 'cache:homepage', 'user:1001', 'token:jwt_xyz']));
249
+ continue;
250
+ }
251
+ if (cmd === 'GET') {
252
+ respond(respNullBulk());
253
+ continue;
254
+ }
255
+ if (cmd === 'SET' || cmd === 'DEL' || cmd === 'MSET' || cmd === 'HSET') {
256
+ respond(respError("READONLY You can't write against a read only replica."));
257
+ continue;
258
+ }
259
+ if (cmd === 'SLAVEOF' || cmd === 'REPLICAOF') {
260
+ respond(respError('ERR SLAVEOF not allowed'));
261
+ continue;
262
+ }
263
+ if (cmd === 'MODULE') {
264
+ respond(respError('ERR MODULE command not allowed'));
265
+ continue;
266
+ }
267
+ if (cmd === 'EVAL' || cmd === 'EVALSHA' || cmd === 'SCRIPT') {
268
+ respond(respError('NOSCRIPT No matching script. Are you sure the script is loaded?'));
269
+ continue;
270
+ }
271
+ if (cmd === 'FLUSHALL' || cmd === 'FLUSHDB') {
272
+ respond(respError('ERR operation not permitted'));
273
+ continue;
274
+ }
275
+ if (cmd === 'CLIENT') {
276
+ const sub = args[1]?.toUpperCase();
277
+ if (sub === 'SETNAME') {
278
+ respond(respSimple('OK'));
279
+ }
280
+ else if (sub === 'GETNAME') {
281
+ respond(respNullBulk());
282
+ }
283
+ else if (sub === 'LIST') {
284
+ respond(respBulk(`id=1 addr=${remoteIP}:${remotePort} fd=8 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client`));
285
+ }
286
+ else {
287
+ respond(respSimple('OK'));
288
+ }
289
+ continue;
290
+ }
291
+ if (cmd === 'SELECT') {
292
+ respond(respSimple('OK'));
293
+ continue;
294
+ }
295
+ if (cmd === 'TYPE') {
296
+ respond(respSimple('string'));
297
+ continue;
298
+ }
299
+ if (cmd === 'TTL' || cmd === 'PTTL') {
300
+ respond(respInt(-1));
301
+ continue;
302
+ }
303
+ if (cmd === 'SCAN') {
304
+ respond(`*2\r\n$1\r\n0\r\n${respArray(['session:abc123', 'cache:homepage', 'user:1001'])}`);
305
+ continue;
306
+ }
307
+ respond(respError(`ERR unknown command '${cmd}'`));
308
+ }
309
+ });
310
+ socket.on('timeout', () => {
311
+ logger.info(`Redis session timeout: ${session.sessionId}`);
312
+ socket.end();
313
+ });
314
+ socket.on('close', () => this.endSession(session.sessionId));
315
+ socket.on('error', (err) => {
316
+ logger.debug(`Redis socket error: ${err.message}`);
317
+ this.endSession(session.sessionId);
318
+ });
319
+ }
320
+ }
321
+ //# sourceMappingURL=redis-trap.js.map