I reproduced the structure behind the "Korean Leaks" incident, where one compromised managed-service-provider (MSP) account cascaded into a breach of 28 downstream clients. It shows how per-client scoped access, instead of one shared credential, contains the blast radius to a single client. Both are free right now.
Not for real MSP operators — this is a learning resource for backend/infra developers running multi-tenant SaaS or managing multiple client systems, to see in code exactly why one compromised vendor can take down everyone downstream.
A vulnerable MSP access model where one credential reaches every client tenant.
// Vulnerable MSP access model — modeled on the "Korean Leaks" campaign:
// a single compromised GJTec credential cascaded into a breach of 28
// downstream financial institutions. The flaw: one credential has
// standing access to every client tenant.
class SharedCredentialAccess {
constructor(tenants) {
this.tenants = tenants; // [{id, name}, ...]
this.credential = { id: 'msp-admin', scope: 'all-tenants' };
}
// A stolen credential can act against ANY tenant — no isolation.
accessTenant(credential, tenantId) {
if (credential.scope !== 'all-tenants') return { status: 'denied' };
const tenant = this.tenants.find(t => t.id === tenantId);
return { status: 'breached', tenant };
}
simulateCompromise() {
// Attacker has the one credential; every tenant is reachable.
return this.tenants.map(t => this.accessTenant(this.credential, t.id));
}
}
module.exports = { SharedCredentialAccess };
The fix: per-tenant scoped credentials that contain a breach to one client.
// The fix: credentials are scoped to exactly one tenant and expire
// quickly. Compromising the credential used against one client never
// grants access to any other client — this is what would have
// contained the GJTec-style MSP breach to a single institution.
class TenantIsolatedAccess {
constructor(tenants) {
this.tenants = tenants;
this.credentials = new Map(
tenants.map(t => [t.id, { tenantId: t.id, issuedAt: Date.now(), ttlMs: 15 * 60 * 1000 }])
);
this.blockedAttempts = [];
}
accessTenant(credential, tenantId) {
if (credential.tenantId !== tenantId) {
this.blockedAttempts.push({ credentialTenant: credential.tenantId, attemptedTenant: tenantId });
return { status: 'denied', reason: 'scope_mismatch' };
}
const tenant = this.tenants.find(t => t.id === tenantId);
return { status: 'breached', tenant }; // only the targeted tenant is ever reachable
}
simulateCompromise(compromisedTenantId) {
const stolenCredential = this.credentials.get(compromisedTenantId);
return this.tenants.map(t => this.accessTenant(stolenCredential, t.id));
}
}
module.exports = { TenantIsolatedAccess };
Questions or feedback about this product? Leave your email if you'd like a reply (optional).