272 lines
14 KiB
JavaScript
272 lines
14 KiB
JavaScript
const fs = require('fs');
|
|
const { RemoteTerminal } = require('./remote_exec');
|
|
|
|
const LOG_FILE = 'D:/cumin/ghaymah_dns_net_sec_tests/logs/ghaymah_execution.log';
|
|
const RESULTS_FILE = 'D:/cumin/ghaymah_dns_net_sec_tests/logs/ghaymah_test_results.json';
|
|
|
|
const testResults = [];
|
|
let logLines = [];
|
|
|
|
function log(phase, title, output = '') {
|
|
const ts = new Date().toISOString();
|
|
const header = `[${ts}] [${phase}] ${title}`;
|
|
console.log(header);
|
|
logLines.push(header);
|
|
if (output) {
|
|
console.log(output);
|
|
logLines.push(output);
|
|
}
|
|
logLines.push('');
|
|
fs.writeFileSync(LOG_FILE, logLines.join('\n'));
|
|
}
|
|
|
|
function recordResult(category, testName, status, details) {
|
|
testResults.push({
|
|
timestamp: new Date().toISOString(),
|
|
category,
|
|
testName,
|
|
status, // 'PASS' | 'FAIL' | 'BLOCKED' | 'INFO'
|
|
details
|
|
});
|
|
fs.writeFileSync(RESULTS_FILE, JSON.stringify(testResults, null, 2));
|
|
}
|
|
|
|
async function main() {
|
|
log('INIT', '=== STARTING COMPREHENSIVE GHAYMAH PLATFORM TESTS ===');
|
|
log('INIT', 'Target: Ghaymah Systems (deploy.ghaymah.systems)');
|
|
log('INIT', 'Project: dns-tcp-test (3f55ceef-78ae-40c8-a41b-0505db8c59ee)');
|
|
log('INIT', 'Client Container: client-terminal (tsl0922/ttyd:alpine)');
|
|
log('INIT', 'Target Container: test-redis (redis:alpine)');
|
|
|
|
const term = new RemoteTerminal();
|
|
await term.connect();
|
|
log('INIT', 'RemoteTerminal connected via WebSocket to Ghaymah pod!');
|
|
|
|
// Install netcat or tools if needed (busybox already has nc, nslookup, wget, ip, etc.)
|
|
log('PRE-FLIGHT', 'Checking container environment tools...');
|
|
const toolsCheck = await term.exec('which nslookup nc wget ip awk grep');
|
|
log('PRE-FLIGHT', 'Available tools in Alpine container:', toolsCheck.output);
|
|
|
|
// ══════════════════════════════════════════════════════════
|
|
// PHASE 3.1: CONTAINER ENVIRONMENT & NETWORK ARCHITECTURE
|
|
// ══════════════════════════════════════════════════════════
|
|
log('PHASE3-ENV', '=== PHASE 3.1: CONTAINER ENVIRONMENT & NETWORK DISCOVERY ===');
|
|
|
|
const whoami = await term.exec('whoami && id && hostname');
|
|
log('PHASE3-ENV', 'Container identity:', whoami.output);
|
|
recordResult('Environment', 'Container Identity', 'INFO', whoami.output);
|
|
|
|
const ipAddr = await term.exec('ip addr');
|
|
log('PHASE3-ENV', 'Network interfaces (ip addr):', ipAddr.output);
|
|
recordResult('Environment', 'Network Interfaces', 'INFO', ipAddr.output);
|
|
|
|
const ipRoute = await term.exec('ip route');
|
|
log('PHASE3-ENV', 'Routing table (ip route):', ipRoute.output);
|
|
recordResult('Environment', 'Routing Table', 'INFO', ipRoute.output);
|
|
|
|
const resolvConf = await term.exec('cat /etc/resolv.conf');
|
|
log('PHASE3-ENV', 'DNS Configuration (/etc/resolv.conf):', resolvConf.output);
|
|
recordResult('Environment', 'DNS Configuration', 'INFO', resolvConf.output);
|
|
|
|
// ══════════════════════════════════════════════════════════
|
|
// PHASE 3.2: INTERNAL DNS VERIFICATION
|
|
// ══════════════════════════════════════════════════════════
|
|
log('PHASE3-DNS', '=== PHASE 3.2: INTERNAL DNS VERIFICATION ===');
|
|
log('PHASE3-DNS', 'Objective: Prove App A discovers App B using internal DNS hostnames (not direct IP)');
|
|
|
|
// Pattern 1: Short hostname 'test-redis'
|
|
log('PHASE3-DNS', 'Test 1: nslookup test-redis');
|
|
const dns1 = await term.exec('nslookup test-redis');
|
|
log('PHASE3-DNS', 'Result (test-redis):', dns1.output);
|
|
const dns1Pass = dns1.exitCode === 0 && (dns1.output.includes('Address') || dns1.output.includes('10.'));
|
|
recordResult('Internal DNS', 'Short Hostname Resolution (test-redis)', dns1Pass ? 'PASS' : 'FAIL', {
|
|
command: 'nslookup test-redis',
|
|
output: dns1.output,
|
|
exitCode: dns1.exitCode
|
|
});
|
|
|
|
// Pattern 2: Search domain expansion via /etc/hosts or getent
|
|
log('PHASE3-DNS', 'Test 2: getent hosts test-redis');
|
|
const dns2 = await term.exec('getent hosts test-redis || ping -c 1 -W 2 test-redis');
|
|
log('PHASE3-DNS', 'Result (getent / ping test-redis):', dns2.output);
|
|
recordResult('Internal DNS', 'Resolver Lookup (test-redis)', dns2.exitCode === 0 ? 'PASS' : 'FAIL', dns2.output);
|
|
|
|
// Pattern 3: Namespace-qualified FQDN
|
|
// Search domain in resolv.conf was: d62f9886-fced-4cf2-98e4-5b62000d4f03.svc.cluster.local
|
|
const fqdn = 'test-redis.d62f9886-fced-4cf2-98e4-5b62000d4f03.svc.cluster.local';
|
|
log('PHASE3-DNS', `Test 3: nslookup ${fqdn}`);
|
|
const dns3 = await term.exec(`nslookup ${fqdn}`);
|
|
log('PHASE3-DNS', `Result (${fqdn}):`, dns3.output);
|
|
recordResult('Internal DNS', 'Kubernetes FQDN Resolution', dns3.exitCode === 0 ? 'PASS' : 'FAIL', dns3.output);
|
|
|
|
// Pattern 4: Mesh-style DNS patterns (if configured by mesh/service router)
|
|
const meshPatterns = [
|
|
'test-redis.mesh',
|
|
'test-redis.dns-tcp-test.mesh',
|
|
'test-redis.local',
|
|
'test-redis.internal'
|
|
];
|
|
for (const mName of meshPatterns) {
|
|
const dRes = await term.exec(`nslookup ${mName}`);
|
|
log('PHASE3-DNS', `Pattern ${mName}:`, dRes.output);
|
|
recordResult('Internal DNS Pattern Check', mName, dRes.exitCode === 0 ? 'PASS' : 'NXDOMAIN', dRes.output);
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════
|
|
// PHASE 3.3: RAW TCP CONNECTIONS OVER PRIVATE NETWORK
|
|
// ══════════════════════════════════════════════════════════
|
|
log('PHASE3-TCP', '=== PHASE 3.3: RAW TCP CONNECTIONS (NON-HTTP) ===');
|
|
log('PHASE3-TCP', 'Target: test-redis on port 6379 over private network');
|
|
|
|
// Test TCP connectivity via DNS hostname
|
|
log('PHASE3-TCP', 'Test 1: TCP Port Probe via DNS Hostname (nc -z -w 3 test-redis 6379)');
|
|
const tcpProbe = await term.exec('nc -z -w 3 test-redis 6379');
|
|
log('PHASE3-TCP', 'TCP probe exit code:', tcpProbe.exitCode.toString());
|
|
recordResult('TCP Connectivity', 'TCP Port 6379 Probe via DNS Hostname', tcpProbe.exitCode === 0 ? 'PASS' : 'FAIL', {
|
|
command: 'nc -z -w 3 test-redis 6379',
|
|
output: tcpProbe.output,
|
|
exitCode: tcpProbe.exitCode
|
|
});
|
|
|
|
// Test Raw TCP Protocol Handshake: Redis PING -> PONG
|
|
log('PHASE3-TCP', 'Test 2: Raw TCP Handshake: Send PING\\r\\n -> Expect +PONG\\r\\n');
|
|
const pingCmd = 'printf "*1\\r\\n\\$4\\r\\nPING\\r\\n" | nc -w 3 test-redis 6379';
|
|
const tcpPing = await term.exec(pingCmd);
|
|
log('PHASE3-TCP', 'Redis PING response:', tcpPing.output);
|
|
const pingPass = tcpPing.output.includes('+PONG') || tcpPing.output.includes('PONG');
|
|
recordResult('TCP Protocol', 'Redis Raw TCP PING/PONG', pingPass ? 'PASS' : 'FAIL', {
|
|
command: pingCmd,
|
|
output: tcpPing.output,
|
|
exitCode: tcpPing.exitCode
|
|
});
|
|
|
|
// Test Raw TCP Data Exchange: SET key and GET key
|
|
log('PHASE3-TCP', 'Test 3: Raw TCP Data Exchange: SET ghaymah_test "OK_TCP_WORKS" && GET ghaymah_test');
|
|
const redisScript = `
|
|
printf "*3\\r\\n\\$3\\r\\nSET\\r\\n\\$12\\r\\nghaymah_test\\r\\n\\$11\\r\\nOK_TCP_PASS\\r\\n" | nc -w 3 test-redis 6379
|
|
printf "*2\\r\\n\\$3\\r\\nGET\\r\\n\\$12\\r\\nghaymah_test\\r\\n" | nc -w 3 test-redis 6379
|
|
`;
|
|
const tcpData = await term.exec(redisScript);
|
|
log('PHASE3-TCP', 'Redis SET & GET response:', tcpData.output);
|
|
const dataPass = tcpData.output.includes('OK_TCP_PASS');
|
|
recordResult('TCP Data Integrity', 'Redis State Storage & Retrieval via Raw TCP', dataPass ? 'PASS' : 'FAIL', {
|
|
output: tcpData.output,
|
|
exitCode: tcpData.exitCode
|
|
});
|
|
|
|
// ══════════════════════════════════════════════════════════
|
|
// PHASE 4.1: SECURITY & PENETRATION TESTING: ISOLATION
|
|
// ══════════════════════════════════════════════════════════
|
|
log('PHASE4-ISO', '=== PHASE 4.1: SECURITY ISOLATION & SCOPING ===');
|
|
log('PHASE4-ISO', 'Objective: Test lateral movement and cross-project isolation');
|
|
|
|
// Test 1: Cross-Project DNS Resolution
|
|
// auth-deploy belongs to project 'auth-deploy' (5bd7e471-6be2-40a8-aff9-268beb29c631)
|
|
// scanner-clean belongs to project 'scanner-clean' (19c3ee5e-68c3-4e34-94c9-d03ab6a9df51)
|
|
const crossProjectTargets = [
|
|
'auth-deploy',
|
|
'auth-deploy.mesh',
|
|
'auth-deploy.local',
|
|
'scanner-clean',
|
|
'scanner-clean.mesh'
|
|
];
|
|
|
|
for (const target of crossProjectTargets) {
|
|
const xRes = await term.exec(`nslookup ${target}`);
|
|
log('PHASE4-ISO', `Cross-project DNS lookup for '${target}':`, xRes.output);
|
|
const isolated = xRes.exitCode !== 0 || xRes.output.includes('NXDOMAIN') || xRes.output.includes("can't find");
|
|
recordResult('Isolation', `Cross-Project DNS: ${target}`, isolated ? 'BLOCKED (SECURE)' : 'LEAKED (VULNERABLE)', {
|
|
target,
|
|
output: xRes.output,
|
|
exitCode: xRes.exitCode
|
|
});
|
|
}
|
|
|
|
// Test 2: Cross-Tenant DNS Resolution
|
|
const crossTenantTargets = [
|
|
'kubernetes.default.svc',
|
|
'metadata.google.internal',
|
|
'admin-panel',
|
|
'backend.internal',
|
|
'database.default'
|
|
];
|
|
|
|
for (const target of crossTenantTargets) {
|
|
const ctRes = await term.exec(`nslookup ${target}`);
|
|
log('PHASE4-ISO', `Cross-tenant DNS lookup for '${target}':`, ctRes.output);
|
|
recordResult('Tenant Isolation', `Cross-Tenant DNS: ${target}`, ctRes.exitCode !== 0 ? 'BLOCKED' : 'RESOLVED', ctRes.output);
|
|
}
|
|
|
|
// Test 3: Lateral Movement via Internal IP Probing
|
|
log('PHASE4-ISO', 'Probing adjacent internal IP subnets (10.42.0.0/16, 10.43.0.0/16, 10.100.0.0/16)...');
|
|
const probeIps = [
|
|
'10.43.0.1', // Kubernetes API Service IP
|
|
'10.43.0.10', // CoreDNS Service IP
|
|
'10.42.5.1', // Local Pod Subnet Gateway
|
|
'10.100.0.1', // WireGuard/Mesh Gateway (if present)
|
|
'10.100.0.2',
|
|
'172.17.0.1', // Docker bridge
|
|
'169.254.169.254'// Cloud Metadata service
|
|
];
|
|
|
|
for (const ip of probeIps) {
|
|
const pRes = await term.exec(`nc -z -w 1 ${ip} 80 || nc -z -w 1 ${ip} 443 || nc -z -w 1 ${ip} 53`);
|
|
log('PHASE4-ISO', `IP Probe ${ip}: exit code ${pRes.exitCode}`);
|
|
recordResult('Network Isolation', `Direct IP Probing: ${ip}`, pRes.exitCode === 0 ? 'ACCESSIBLE' : 'BLOCKED', {
|
|
ip,
|
|
exitCode: pRes.exitCode
|
|
});
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════
|
|
// PHASE 4.2: NETWORK ENUMERATION & PORT SCANNING
|
|
// ══════════════════════════════════════════════════════════
|
|
log('PHASE4-SCAN', '=== PHASE 4.2: NETWORK ENUMERATION ===');
|
|
|
|
log('PHASE4-SCAN', 'Scanning ports on test-redis...');
|
|
const portScanScript = `
|
|
for p in 22 53 80 443 3000 4000 5432 6379 8080 9000; do
|
|
nc -z -w 1 test-redis $p && echo "PORT $p OPEN" || echo "PORT $p CLOSED"
|
|
done
|
|
`;
|
|
const portScan = await term.exec(portScanScript);
|
|
log('PHASE4-SCAN', 'Port scan results on test-redis:', portScan.output);
|
|
recordResult('Network Enumeration', 'Port Scan test-redis', 'COMPLETED', portScan.output);
|
|
|
|
// ══════════════════════════════════════════════════════════
|
|
// PHASE 4.3: DUAL ROUTING & DNS LEAKS
|
|
// ══════════════════════════════════════════════════════════
|
|
log('PHASE4-LEAK', '=== PHASE 4.3: DUAL ROUTING & DNS LEAK DETECTION ===');
|
|
|
|
// Test 1: External HTTP egress
|
|
log('PHASE4-LEAK', 'Test 1: External internet egress via HTTP (httpbin.org/ip)...');
|
|
const extIp = await term.exec('wget -qO- https://httpbin.org/ip || wget -qO- http://ifconfig.me/ip');
|
|
log('PHASE4-LEAK', 'Public Egress IP response:', extIp.output);
|
|
const extPass = extIp.exitCode === 0 && extIp.output.length > 5;
|
|
recordResult('Dual Routing', 'Outbound Internet Access (Egress)', extPass ? 'PASS' : 'FAIL', extIp.output);
|
|
|
|
// Test 2: External DNS resolution
|
|
const externalDomains = ['google.com', 'github.com', 'cloudflare.com'];
|
|
for (const domain of externalDomains) {
|
|
const extDns = await term.exec(`nslookup ${domain}`);
|
|
log('PHASE4-LEAK', `External DNS resolution for ${domain}:`, extDns.output);
|
|
const dnsPass = extDns.exitCode === 0 && extDns.output.includes('Address');
|
|
recordResult('DNS Routing', `External DNS Resolution (${domain})`, dnsPass ? 'PASS' : 'FAIL', extDns.output);
|
|
}
|
|
|
|
// Test 3: Check for DNS Leak / Internal vs External Resolver Separation
|
|
log('PHASE4-LEAK', 'Testing DNS server handling of non-existent internal domain (leak check)...');
|
|
const leakTest = await term.exec('nslookup leak-test-nonexistent-internal-ghaymah.mesh');
|
|
log('PHASE4-LEAK', 'Leak test query output:', leakTest.output);
|
|
const leakSecure = leakTest.output.includes('NXDOMAIN') || leakTest.output.includes("can't find");
|
|
recordResult('DNS Leak Detection', 'Non-existent Mesh Domain Query', leakSecure ? 'SECURE (NXDOMAIN)' : 'ANOMALY', leakTest.output);
|
|
|
|
term.close();
|
|
log('DONE', '=== ALL GHAYMAH PLATFORM TESTS COMPLETED SUCCESSFULLY ===');
|
|
}
|
|
|
|
main().catch(err => {
|
|
console.error('FATAL ERROR:', err);
|
|
fs.writeFileSync(LOG_FILE, `FATAL: ${err.message}\n${err.stack}`);
|
|
});
|