Fix : MITM

This commit is contained in:
decolua
2026-03-05 21:13:09 +07:00
parent 1c3ba6ef69
commit f4e08fcd16
11 changed files with 497 additions and 150 deletions

View File

@@ -1,55 +1,32 @@
const path = require("path");
const fs = require("fs");
const { MITM_DIR } = require("../paths");
// Wildcard domains — covers all subdomains without needing cert update per tool
const WILDCARD_DOMAINS = [
"*.googleapis.com",
"*.githubcopilot.com",
"*.individual.githubcopilot.com",
"*.business.githubcopilot.com"
];
const { generateRootCA, loadRootCA, generateLeafCert } = require("./rootCA");
/**
* Generate self-signed SSL certificate with wildcard SAN.
* Covers all current and future MITM tool domains automatically.
* Uses selfsigned (pure JS, no openssl needed).
* Generate Root CA certificate (one-time setup)
* This replaces the old static wildcard cert approach
*/
async function generateCert() {
const certDir = MITM_DIR;
const keyPath = path.join(certDir, "server.key");
const certPath = path.join(certDir, "server.crt");
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
console.log("✅ SSL certificate already exists");
return { key: keyPath, cert: certPath };
}
if (!fs.existsSync(certDir)) {
fs.mkdirSync(certDir, { recursive: true });
}
const selfsigned = require("selfsigned");
const attrs = [{ name: "commonName", value: "9router-mitm" }];
const notAfter = new Date();
notAfter.setFullYear(notAfter.getFullYear() + 1);
const pems = await selfsigned.generate(attrs, {
keySize: 2048,
algorithm: "sha256",
notAfterDate: notAfter,
extensions: [
{
name: "subjectAltName",
altNames: WILDCARD_DOMAINS.map(domain => ({ type: 2, value: domain }))
}
]
});
fs.writeFileSync(keyPath, pems.private);
fs.writeFileSync(certPath, pems.cert);
console.log(`✅ Generated wildcard SSL certificate: ${WILDCARD_DOMAINS.join(", ")}`);
return { key: keyPath, cert: certPath };
return await generateRootCA();
}
module.exports = { generateCert };
/**
* Get certificate for a specific domain (dynamic generation)
* Used by SNICallback in server.js
*/
function getCertForDomain(domain) {
try {
const rootCA = loadRootCA();
const leafCert = generateLeafCert(domain, rootCA);
return {
key: leafCert.key,
cert: leafCert.cert
};
} catch (error) {
console.error(`Failed to generate cert for ${domain}:`, error.message);
return null;
}
}
module.exports = { generateCert, getCertForDomain };

View File

@@ -43,8 +43,8 @@ function checkCertInstalledMac(certPath) {
function checkCertInstalledWindows(certPath) {
return new Promise((resolve) => {
// Check Root store for our cert by subject name
exec("certutil -store Root daily-cloudcode-pa.googleapis.com", (error) => {
// Check Root store for our Root CA by common name
exec("certutil -store Root \"9Router MITM Root CA\"", (error) => {
resolve(!error);
});
});
@@ -130,7 +130,7 @@ async function uninstallCertMac(sudoPassword, certPath) {
}
async function uninstallCertWindows() {
const psCommand = `Start-Process certutil -ArgumentList '-delstore','Root','daily-cloudcode-pa.googleapis.com' -Verb RunAs -Wait -WindowStyle Hidden`;
const psCommand = `Start-Process certutil -ArgumentList '-delstore','Root','9Router MITM Root CA' -Verb RunAs -Wait -WindowStyle Hidden`;
return new Promise((resolve, reject) => {
exec(
`powershell -NonInteractive -WindowStyle Hidden -Command "${psCommand}"`,
@@ -144,12 +144,12 @@ async function uninstallCertWindows() {
}
function checkCertInstalledLinux() {
const certFile = `${LINUX_CERT_DIR}/9router-mitm.crt`;
const certFile = `${LINUX_CERT_DIR}/9router-root-ca.crt`;
return Promise.resolve(fs.existsSync(certFile));
}
async function installCertLinux(sudoPassword, certPath) {
const destFile = `${LINUX_CERT_DIR}/9router-mitm.crt`;
const destFile = `${LINUX_CERT_DIR}/9router-root-ca.crt`;
// Try update-ca-certificates (Debian/Ubuntu), fallback to update-ca-trust (Fedora/RHEL)
const cmd = `cp "${certPath}" "${destFile}" && (update-ca-certificates 2>/dev/null || update-ca-trust 2>/dev/null || true)`;
try {
@@ -161,7 +161,7 @@ async function installCertLinux(sudoPassword, certPath) {
}
async function uninstallCertLinux(sudoPassword) {
const destFile = `${LINUX_CERT_DIR}/9router-mitm.crt`;
const destFile = `${LINUX_CERT_DIR}/9router-root-ca.crt`;
const cmd = `rm -f "${destFile}" && (update-ca-certificates 2>/dev/null || update-ca-trust 2>/dev/null || true)`;
try {
await execWithPassword(cmd, sudoPassword);

153
src/mitm/cert/rootCA.js Normal file
View File

@@ -0,0 +1,153 @@
const path = require("path");
const fs = require("fs");
const forge = require("node-forge");
const { MITM_DIR } = require("../paths");
const ROOT_CA_KEY_PATH = path.join(MITM_DIR, "rootCA.key");
const ROOT_CA_CERT_PATH = path.join(MITM_DIR, "rootCA.crt");
/**
* Generate Root CA certificate (only once)
* This Root CA will sign all dynamic leaf certificates
*/
async function generateRootCA() {
if (fs.existsSync(ROOT_CA_KEY_PATH) && fs.existsSync(ROOT_CA_CERT_PATH)) {
console.log("✅ Root CA already exists");
return { key: ROOT_CA_KEY_PATH, cert: ROOT_CA_CERT_PATH };
}
if (!fs.existsSync(MITM_DIR)) {
fs.mkdirSync(MITM_DIR, { recursive: true });
}
console.log("🔐 Generating Root CA certificate...");
// Generate RSA key pair
const keys = forge.pki.rsa.generateKeyPair(2048);
// Create Root CA certificate
const cert = forge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = "01";
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10);
const attrs = [
{ name: "commonName", value: "9Router MITM Root CA" },
{ name: "organizationName", value: "9Router" },
{ name: "countryName", value: "US" }
];
cert.setSubject(attrs);
cert.setIssuer(attrs); // Self-signed
cert.setExtensions([
{
name: "basicConstraints",
cA: true,
critical: true
},
{
name: "keyUsage",
keyCertSign: true,
cRLSign: true,
critical: true
},
{
name: "subjectKeyIdentifier"
}
]);
// Self-sign the certificate
cert.sign(keys.privateKey, forge.md.sha256.create());
// Save to disk
const privateKeyPem = forge.pki.privateKeyToPem(keys.privateKey);
const certPem = forge.pki.certificateToPem(cert);
fs.writeFileSync(ROOT_CA_KEY_PATH, privateKeyPem);
fs.writeFileSync(ROOT_CA_CERT_PATH, certPem);
console.log("✅ Root CA generated successfully");
return { key: ROOT_CA_KEY_PATH, cert: ROOT_CA_CERT_PATH };
}
/**
* Load Root CA from disk
*/
function loadRootCA() {
if (!fs.existsSync(ROOT_CA_KEY_PATH) || !fs.existsSync(ROOT_CA_CERT_PATH)) {
throw new Error("Root CA not found. Generate it first.");
}
const keyPem = fs.readFileSync(ROOT_CA_KEY_PATH, "utf8");
const certPem = fs.readFileSync(ROOT_CA_CERT_PATH, "utf8");
return {
key: forge.pki.privateKeyFromPem(keyPem),
cert: forge.pki.certificateFromPem(certPem)
};
}
/**
* Generate leaf certificate for a specific domain, signed by Root CA
*/
function generateLeafCert(domain, rootCA) {
// Generate key pair for leaf cert
const keys = forge.pki.rsa.generateKeyPair(2048);
// Create leaf certificate
const cert = forge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = Math.floor(Math.random() * 1000000).toString();
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
cert.setSubject([
{ name: "commonName", value: domain }
]);
cert.setIssuer(rootCA.cert.subject.attributes);
cert.setExtensions([
{
name: "basicConstraints",
cA: false
},
{
name: "keyUsage",
digitalSignature: true,
keyEncipherment: true
},
{
name: "extKeyUsage",
serverAuth: true,
clientAuth: true
},
{
name: "subjectAltName",
altNames: [
{ type: 2, value: domain }, // DNS
{ type: 2, value: `*.${domain}` } // Wildcard
]
}
]);
// Sign with Root CA
cert.sign(rootCA.key, forge.md.sha256.create());
return {
key: forge.pki.privateKeyToPem(keys.privateKey),
cert: forge.pki.certificateToPem(cert)
};
}
module.exports = {
generateRootCA,
loadRootCA,
generateLeafCert,
ROOT_CA_CERT_PATH,
ROOT_CA_KEY_PATH
};