@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.
- package/dist/cli/index.d.ts +45 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +298 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/intel/index.d.ts +38 -0
- package/dist/intel/index.d.ts.map +1 -0
- package/dist/intel/index.js +157 -0
- package/dist/intel/index.js.map +1 -0
- package/dist/profiler/attacker-profiler.d.ts +68 -0
- package/dist/profiler/attacker-profiler.d.ts.map +1 -0
- package/dist/profiler/attacker-profiler.js +316 -0
- package/dist/profiler/attacker-profiler.js.map +1 -0
- package/dist/profiler/index.d.ts +8 -0
- package/dist/profiler/index.d.ts.map +1 -0
- package/dist/profiler/index.js +8 -0
- package/dist/profiler/index.js.map +1 -0
- package/dist/services/base-service.d.ts +61 -0
- package/dist/services/base-service.d.ts.map +1 -0
- package/dist/services/base-service.js +190 -0
- package/dist/services/base-service.js.map +1 -0
- package/dist/services/generic-trap.d.ts +22 -0
- package/dist/services/generic-trap.d.ts.map +1 -0
- package/dist/services/generic-trap.js +439 -0
- package/dist/services/generic-trap.js.map +1 -0
- package/dist/services/http-trap.d.ts +36 -0
- package/dist/services/http-trap.d.ts.map +1 -0
- package/dist/services/http-trap.js +218 -0
- package/dist/services/http-trap.js.map +1 -0
- package/dist/services/index.d.ts +26 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +52 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/mysql-trap.d.ts +22 -0
- package/dist/services/mysql-trap.d.ts.map +1 -0
- package/dist/services/mysql-trap.js +374 -0
- package/dist/services/mysql-trap.js.map +1 -0
- package/dist/services/rdp-trap.d.ts +21 -0
- package/dist/services/rdp-trap.d.ts.map +1 -0
- package/dist/services/rdp-trap.js +299 -0
- package/dist/services/rdp-trap.js.map +1 -0
- package/dist/services/redis-trap.d.ts +21 -0
- package/dist/services/redis-trap.d.ts.map +1 -0
- package/dist/services/redis-trap.js +321 -0
- package/dist/services/redis-trap.js.map +1 -0
- package/dist/services/smb-trap.d.ts +21 -0
- package/dist/services/smb-trap.d.ts.map +1 -0
- package/dist/services/smb-trap.js +358 -0
- package/dist/services/smb-trap.js.map +1 -0
- package/dist/services/ssh-trap.d.ts +43 -0
- package/dist/services/ssh-trap.d.ts.map +1 -0
- package/dist/services/ssh-trap.js +397 -0
- package/dist/services/ssh-trap.js.map +1 -0
- package/dist/threat-cloud-uploader.d.ts +48 -0
- package/dist/threat-cloud-uploader.d.ts.map +1 -0
- package/dist/threat-cloud-uploader.js +125 -0
- package/dist/threat-cloud-uploader.js.map +1 -0
- package/dist/trap-engine.d.ts +80 -0
- package/dist/trap-engine.d.ts.map +1 -0
- package/dist/trap-engine.js +279 -0
- package/dist/trap-engine.js.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +89 -0
- package/dist/types.js.map +1 -0
- 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
|