@etcsec-com/etc-collector 1.4.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/.env.example +60 -0
- package/.env.test.example +33 -0
- package/.github/workflows/ci.yml +83 -0
- package/.github/workflows/release.yml +246 -0
- package/.prettierrc.json +10 -0
- package/CHANGELOG.md +15 -0
- package/Dockerfile +57 -0
- package/LICENSE +190 -0
- package/README.md +194 -0
- package/dist/api/controllers/audit.controller.d.ts +21 -0
- package/dist/api/controllers/audit.controller.d.ts.map +1 -0
- package/dist/api/controllers/audit.controller.js +179 -0
- package/dist/api/controllers/audit.controller.js.map +1 -0
- package/dist/api/controllers/auth.controller.d.ts +16 -0
- package/dist/api/controllers/auth.controller.d.ts.map +1 -0
- package/dist/api/controllers/auth.controller.js +146 -0
- package/dist/api/controllers/auth.controller.js.map +1 -0
- package/dist/api/controllers/export.controller.d.ts +27 -0
- package/dist/api/controllers/export.controller.d.ts.map +1 -0
- package/dist/api/controllers/export.controller.js +80 -0
- package/dist/api/controllers/export.controller.js.map +1 -0
- package/dist/api/controllers/health.controller.d.ts +5 -0
- package/dist/api/controllers/health.controller.d.ts.map +1 -0
- package/dist/api/controllers/health.controller.js +16 -0
- package/dist/api/controllers/health.controller.js.map +1 -0
- package/dist/api/controllers/jobs.controller.d.ts +13 -0
- package/dist/api/controllers/jobs.controller.d.ts.map +1 -0
- package/dist/api/controllers/jobs.controller.js +125 -0
- package/dist/api/controllers/jobs.controller.js.map +1 -0
- package/dist/api/controllers/providers.controller.d.ts +15 -0
- package/dist/api/controllers/providers.controller.d.ts.map +1 -0
- package/dist/api/controllers/providers.controller.js +112 -0
- package/dist/api/controllers/providers.controller.js.map +1 -0
- package/dist/api/dto/AuditRequest.dto.d.ts +6 -0
- package/dist/api/dto/AuditRequest.dto.d.ts.map +1 -0
- package/dist/api/dto/AuditRequest.dto.js +3 -0
- package/dist/api/dto/AuditRequest.dto.js.map +1 -0
- package/dist/api/dto/AuditResponse.dto.d.ts +17 -0
- package/dist/api/dto/AuditResponse.dto.d.ts.map +1 -0
- package/dist/api/dto/AuditResponse.dto.js +3 -0
- package/dist/api/dto/AuditResponse.dto.js.map +1 -0
- package/dist/api/dto/TokenRequest.dto.d.ts +6 -0
- package/dist/api/dto/TokenRequest.dto.d.ts.map +1 -0
- package/dist/api/dto/TokenRequest.dto.js +3 -0
- package/dist/api/dto/TokenRequest.dto.js.map +1 -0
- package/dist/api/dto/TokenResponse.dto.d.ts +12 -0
- package/dist/api/dto/TokenResponse.dto.d.ts.map +1 -0
- package/dist/api/dto/TokenResponse.dto.js +3 -0
- package/dist/api/dto/TokenResponse.dto.js.map +1 -0
- package/dist/api/middlewares/authenticate.d.ts +12 -0
- package/dist/api/middlewares/authenticate.d.ts.map +1 -0
- package/dist/api/middlewares/authenticate.js +141 -0
- package/dist/api/middlewares/authenticate.js.map +1 -0
- package/dist/api/middlewares/errorHandler.d.ts +3 -0
- package/dist/api/middlewares/errorHandler.d.ts.map +1 -0
- package/dist/api/middlewares/errorHandler.js +30 -0
- package/dist/api/middlewares/errorHandler.js.map +1 -0
- package/dist/api/middlewares/rateLimit.d.ts +3 -0
- package/dist/api/middlewares/rateLimit.d.ts.map +1 -0
- package/dist/api/middlewares/rateLimit.js +34 -0
- package/dist/api/middlewares/rateLimit.js.map +1 -0
- package/dist/api/middlewares/validate.d.ts +4 -0
- package/dist/api/middlewares/validate.d.ts.map +1 -0
- package/dist/api/middlewares/validate.js +31 -0
- package/dist/api/middlewares/validate.js.map +1 -0
- package/dist/api/routes/audit.routes.d.ts +5 -0
- package/dist/api/routes/audit.routes.d.ts.map +1 -0
- package/dist/api/routes/audit.routes.js +24 -0
- package/dist/api/routes/audit.routes.js.map +1 -0
- package/dist/api/routes/auth.routes.d.ts +6 -0
- package/dist/api/routes/auth.routes.d.ts.map +1 -0
- package/dist/api/routes/auth.routes.js +22 -0
- package/dist/api/routes/auth.routes.js.map +1 -0
- package/dist/api/routes/export.routes.d.ts +5 -0
- package/dist/api/routes/export.routes.d.ts.map +1 -0
- package/dist/api/routes/export.routes.js +16 -0
- package/dist/api/routes/export.routes.js.map +1 -0
- package/dist/api/routes/health.routes.d.ts +4 -0
- package/dist/api/routes/health.routes.d.ts.map +1 -0
- package/dist/api/routes/health.routes.js +11 -0
- package/dist/api/routes/health.routes.js.map +1 -0
- package/dist/api/routes/index.d.ts +10 -0
- package/dist/api/routes/index.d.ts.map +1 -0
- package/dist/api/routes/index.js +20 -0
- package/dist/api/routes/index.js.map +1 -0
- package/dist/api/routes/providers.routes.d.ts +5 -0
- package/dist/api/routes/providers.routes.d.ts.map +1 -0
- package/dist/api/routes/providers.routes.js +13 -0
- package/dist/api/routes/providers.routes.js.map +1 -0
- package/dist/api/validators/audit.schemas.d.ts +60 -0
- package/dist/api/validators/audit.schemas.d.ts.map +1 -0
- package/dist/api/validators/audit.schemas.js +55 -0
- package/dist/api/validators/audit.schemas.js.map +1 -0
- package/dist/api/validators/auth.schemas.d.ts +17 -0
- package/dist/api/validators/auth.schemas.d.ts.map +1 -0
- package/dist/api/validators/auth.schemas.js +21 -0
- package/dist/api/validators/auth.schemas.js.map +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +62 -0
- package/dist/app.js.map +1 -0
- package/dist/config/config.schema.d.ts +65 -0
- package/dist/config/config.schema.d.ts.map +1 -0
- package/dist/config/config.schema.js +95 -0
- package/dist/config/config.schema.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +75 -0
- package/dist/config/index.js.map +1 -0
- package/dist/container.d.ts +47 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +137 -0
- package/dist/container.js.map +1 -0
- package/dist/data/database.d.ts +13 -0
- package/dist/data/database.d.ts.map +1 -0
- package/dist/data/database.js +68 -0
- package/dist/data/database.js.map +1 -0
- package/dist/data/jobs/token-cleanup.job.d.ts +23 -0
- package/dist/data/jobs/token-cleanup.job.d.ts.map +1 -0
- package/dist/data/jobs/token-cleanup.job.js +96 -0
- package/dist/data/jobs/token-cleanup.job.js.map +1 -0
- package/dist/data/migrations/migration.runner.d.ts +13 -0
- package/dist/data/migrations/migration.runner.d.ts.map +1 -0
- package/dist/data/migrations/migration.runner.js +136 -0
- package/dist/data/migrations/migration.runner.js.map +1 -0
- package/dist/data/models/Token.model.d.ts +30 -0
- package/dist/data/models/Token.model.d.ts.map +1 -0
- package/dist/data/models/Token.model.js +3 -0
- package/dist/data/models/Token.model.js.map +1 -0
- package/dist/data/repositories/token.repository.d.ts +16 -0
- package/dist/data/repositories/token.repository.d.ts.map +1 -0
- package/dist/data/repositories/token.repository.js +97 -0
- package/dist/data/repositories/token.repository.js.map +1 -0
- package/dist/providers/azure/auth.provider.d.ts +5 -0
- package/dist/providers/azure/auth.provider.d.ts.map +1 -0
- package/dist/providers/azure/auth.provider.js +13 -0
- package/dist/providers/azure/auth.provider.js.map +1 -0
- package/dist/providers/azure/azure-errors.d.ts +40 -0
- package/dist/providers/azure/azure-errors.d.ts.map +1 -0
- package/dist/providers/azure/azure-errors.js +121 -0
- package/dist/providers/azure/azure-errors.js.map +1 -0
- package/dist/providers/azure/azure-retry.d.ts +41 -0
- package/dist/providers/azure/azure-retry.d.ts.map +1 -0
- package/dist/providers/azure/azure-retry.js +85 -0
- package/dist/providers/azure/azure-retry.js.map +1 -0
- package/dist/providers/azure/graph-client.d.ts +26 -0
- package/dist/providers/azure/graph-client.d.ts.map +1 -0
- package/dist/providers/azure/graph-client.js +146 -0
- package/dist/providers/azure/graph-client.js.map +1 -0
- package/dist/providers/azure/graph.provider.d.ts +23 -0
- package/dist/providers/azure/graph.provider.d.ts.map +1 -0
- package/dist/providers/azure/graph.provider.js +161 -0
- package/dist/providers/azure/graph.provider.js.map +1 -0
- package/dist/providers/azure/queries/app.queries.d.ts +6 -0
- package/dist/providers/azure/queries/app.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/app.queries.js +9 -0
- package/dist/providers/azure/queries/app.queries.js.map +1 -0
- package/dist/providers/azure/queries/policy.queries.d.ts +6 -0
- package/dist/providers/azure/queries/policy.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/policy.queries.js +9 -0
- package/dist/providers/azure/queries/policy.queries.js.map +1 -0
- package/dist/providers/azure/queries/user.queries.d.ts +7 -0
- package/dist/providers/azure/queries/user.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/user.queries.js +10 -0
- package/dist/providers/azure/queries/user.queries.js.map +1 -0
- package/dist/providers/interfaces/IGraphProvider.d.ts +31 -0
- package/dist/providers/interfaces/IGraphProvider.d.ts.map +1 -0
- package/dist/providers/interfaces/IGraphProvider.js +3 -0
- package/dist/providers/interfaces/IGraphProvider.js.map +1 -0
- package/dist/providers/interfaces/ILDAPProvider.d.ts +37 -0
- package/dist/providers/interfaces/ILDAPProvider.d.ts.map +1 -0
- package/dist/providers/interfaces/ILDAPProvider.js +3 -0
- package/dist/providers/interfaces/ILDAPProvider.js.map +1 -0
- package/dist/providers/ldap/acl-parser.d.ts +8 -0
- package/dist/providers/ldap/acl-parser.d.ts.map +1 -0
- package/dist/providers/ldap/acl-parser.js +157 -0
- package/dist/providers/ldap/acl-parser.js.map +1 -0
- package/dist/providers/ldap/ad-mappers.d.ts +8 -0
- package/dist/providers/ldap/ad-mappers.d.ts.map +1 -0
- package/dist/providers/ldap/ad-mappers.js +162 -0
- package/dist/providers/ldap/ad-mappers.js.map +1 -0
- package/dist/providers/ldap/ldap-client.d.ts +33 -0
- package/dist/providers/ldap/ldap-client.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-client.js +195 -0
- package/dist/providers/ldap/ldap-client.js.map +1 -0
- package/dist/providers/ldap/ldap-errors.d.ts +48 -0
- package/dist/providers/ldap/ldap-errors.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-errors.js +120 -0
- package/dist/providers/ldap/ldap-errors.js.map +1 -0
- package/dist/providers/ldap/ldap-retry.d.ts +14 -0
- package/dist/providers/ldap/ldap-retry.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-retry.js +102 -0
- package/dist/providers/ldap/ldap-retry.js.map +1 -0
- package/dist/providers/ldap/ldap-sanitizer.d.ts +12 -0
- package/dist/providers/ldap/ldap-sanitizer.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-sanitizer.js +104 -0
- package/dist/providers/ldap/ldap-sanitizer.js.map +1 -0
- package/dist/providers/ldap/ldap.provider.d.ts +21 -0
- package/dist/providers/ldap/ldap.provider.d.ts.map +1 -0
- package/dist/providers/ldap/ldap.provider.js +165 -0
- package/dist/providers/ldap/ldap.provider.js.map +1 -0
- package/dist/providers/ldap/queries/computer.queries.d.ts +6 -0
- package/dist/providers/ldap/queries/computer.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/computer.queries.js +9 -0
- package/dist/providers/ldap/queries/computer.queries.js.map +1 -0
- package/dist/providers/ldap/queries/group.queries.d.ts +6 -0
- package/dist/providers/ldap/queries/group.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/group.queries.js +9 -0
- package/dist/providers/ldap/queries/group.queries.js.map +1 -0
- package/dist/providers/ldap/queries/user.queries.d.ts +7 -0
- package/dist/providers/ldap/queries/user.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/user.queries.js +10 -0
- package/dist/providers/ldap/queries/user.queries.js.map +1 -0
- package/dist/providers/smb/smb.provider.d.ts +68 -0
- package/dist/providers/smb/smb.provider.d.ts.map +1 -0
- package/dist/providers/smb/smb.provider.js +382 -0
- package/dist/providers/smb/smb.provider.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +44 -0
- package/dist/server.js.map +1 -0
- package/dist/services/audit/ad-audit.service.d.ts +70 -0
- package/dist/services/audit/ad-audit.service.d.ts.map +1 -0
- package/dist/services/audit/ad-audit.service.js +1019 -0
- package/dist/services/audit/ad-audit.service.js.map +1 -0
- package/dist/services/audit/attack-graph.service.d.ts +62 -0
- package/dist/services/audit/attack-graph.service.d.ts.map +1 -0
- package/dist/services/audit/attack-graph.service.js +702 -0
- package/dist/services/audit/attack-graph.service.js.map +1 -0
- package/dist/services/audit/audit.service.d.ts +4 -0
- package/dist/services/audit/audit.service.d.ts.map +1 -0
- package/dist/services/audit/audit.service.js +10 -0
- package/dist/services/audit/audit.service.js.map +1 -0
- package/dist/services/audit/azure-audit.service.d.ts +37 -0
- package/dist/services/audit/azure-audit.service.d.ts.map +1 -0
- package/dist/services/audit/azure-audit.service.js +153 -0
- package/dist/services/audit/azure-audit.service.js.map +1 -0
- package/dist/services/audit/detectors/ad/accounts.detector.d.ts +37 -0
- package/dist/services/audit/detectors/ad/accounts.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/accounts.detector.js +881 -0
- package/dist/services/audit/detectors/ad/accounts.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/adcs.detector.d.ts +21 -0
- package/dist/services/audit/detectors/ad/adcs.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/adcs.detector.js +227 -0
- package/dist/services/audit/detectors/ad/adcs.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/advanced.detector.d.ts +63 -0
- package/dist/services/audit/detectors/ad/advanced.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/advanced.detector.js +867 -0
- package/dist/services/audit/detectors/ad/advanced.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts +16 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.js +369 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/compliance.detector.d.ts +28 -0
- package/dist/services/audit/detectors/ad/compliance.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/compliance.detector.js +896 -0
- package/dist/services/audit/detectors/ad/compliance.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/computers.detector.d.ts +30 -0
- package/dist/services/audit/detectors/ad/computers.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/computers.detector.js +799 -0
- package/dist/services/audit/detectors/ad/computers.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/gpo.detector.d.ts +17 -0
- package/dist/services/audit/detectors/ad/gpo.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/gpo.detector.js +257 -0
- package/dist/services/audit/detectors/ad/gpo.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/groups.detector.d.ts +19 -0
- package/dist/services/audit/detectors/ad/groups.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/groups.detector.js +488 -0
- package/dist/services/audit/detectors/ad/groups.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/index.d.ts +15 -0
- package/dist/services/audit/detectors/ad/index.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/index.js +51 -0
- package/dist/services/audit/detectors/ad/index.js.map +1 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.d.ts +17 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.js +293 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.d.ts +23 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.js +328 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/network.detector.d.ts +39 -0
- package/dist/services/audit/detectors/ad/network.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/network.detector.js +257 -0
- package/dist/services/audit/detectors/ad/network.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/password.detector.d.ts +14 -0
- package/dist/services/audit/detectors/ad/password.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/password.detector.js +235 -0
- package/dist/services/audit/detectors/ad/password.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/permissions.detector.d.ts +20 -0
- package/dist/services/audit/detectors/ad/permissions.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/permissions.detector.js +392 -0
- package/dist/services/audit/detectors/ad/permissions.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/trusts.detector.d.ts +11 -0
- package/dist/services/audit/detectors/ad/trusts.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/trusts.detector.js +186 -0
- package/dist/services/audit/detectors/ad/trusts.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/app-security.detector.d.ts +11 -0
- package/dist/services/audit/detectors/azure/app-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/app-security.detector.js +184 -0
- package/dist/services/audit/detectors/azure/app-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts +10 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.js +130 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts +8 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.js +113 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/user-security.detector.d.ts +14 -0
- package/dist/services/audit/detectors/azure/user-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/user-security.detector.js +198 -0
- package/dist/services/audit/detectors/azure/user-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/index.d.ts +2 -0
- package/dist/services/audit/detectors/index.d.ts.map +1 -0
- package/dist/services/audit/detectors/index.js +38 -0
- package/dist/services/audit/detectors/index.js.map +1 -0
- package/dist/services/audit/response-formatter.d.ts +176 -0
- package/dist/services/audit/response-formatter.d.ts.map +1 -0
- package/dist/services/audit/response-formatter.js +240 -0
- package/dist/services/audit/response-formatter.js.map +1 -0
- package/dist/services/audit/scoring.service.d.ts +15 -0
- package/dist/services/audit/scoring.service.d.ts.map +1 -0
- package/dist/services/audit/scoring.service.js +139 -0
- package/dist/services/audit/scoring.service.js.map +1 -0
- package/dist/services/auth/crypto.service.d.ts +19 -0
- package/dist/services/auth/crypto.service.d.ts.map +1 -0
- package/dist/services/auth/crypto.service.js +135 -0
- package/dist/services/auth/crypto.service.js.map +1 -0
- package/dist/services/auth/errors.d.ts +19 -0
- package/dist/services/auth/errors.d.ts.map +1 -0
- package/dist/services/auth/errors.js +46 -0
- package/dist/services/auth/errors.js.map +1 -0
- package/dist/services/auth/token.service.d.ts +41 -0
- package/dist/services/auth/token.service.d.ts.map +1 -0
- package/dist/services/auth/token.service.js +208 -0
- package/dist/services/auth/token.service.js.map +1 -0
- package/dist/services/config/config.service.d.ts +6 -0
- package/dist/services/config/config.service.d.ts.map +1 -0
- package/dist/services/config/config.service.js +64 -0
- package/dist/services/config/config.service.js.map +1 -0
- package/dist/services/export/export.service.d.ts +28 -0
- package/dist/services/export/export.service.d.ts.map +1 -0
- package/dist/services/export/export.service.js +28 -0
- package/dist/services/export/export.service.js.map +1 -0
- package/dist/services/export/formatters/csv.formatter.d.ts +8 -0
- package/dist/services/export/formatters/csv.formatter.d.ts.map +1 -0
- package/dist/services/export/formatters/csv.formatter.js +46 -0
- package/dist/services/export/formatters/csv.formatter.js.map +1 -0
- package/dist/services/export/formatters/json.formatter.d.ts +40 -0
- package/dist/services/export/formatters/json.formatter.d.ts.map +1 -0
- package/dist/services/export/formatters/json.formatter.js +58 -0
- package/dist/services/export/formatters/json.formatter.js.map +1 -0
- package/dist/services/jobs/azure-job-runner.d.ts +38 -0
- package/dist/services/jobs/azure-job-runner.d.ts.map +1 -0
- package/dist/services/jobs/azure-job-runner.js +199 -0
- package/dist/services/jobs/azure-job-runner.js.map +1 -0
- package/dist/services/jobs/index.d.ts +4 -0
- package/dist/services/jobs/index.d.ts.map +1 -0
- package/dist/services/jobs/index.js +20 -0
- package/dist/services/jobs/index.js.map +1 -0
- package/dist/services/jobs/job-runner.d.ts +64 -0
- package/dist/services/jobs/job-runner.d.ts.map +1 -0
- package/dist/services/jobs/job-runner.js +952 -0
- package/dist/services/jobs/job-runner.js.map +1 -0
- package/dist/services/jobs/job-store.d.ts +27 -0
- package/dist/services/jobs/job-store.d.ts.map +1 -0
- package/dist/services/jobs/job-store.js +261 -0
- package/dist/services/jobs/job-store.js.map +1 -0
- package/dist/services/jobs/job.types.d.ts +67 -0
- package/dist/services/jobs/job.types.d.ts.map +1 -0
- package/dist/services/jobs/job.types.js +36 -0
- package/dist/services/jobs/job.types.js.map +1 -0
- package/dist/types/ad.types.d.ts +74 -0
- package/dist/types/ad.types.d.ts.map +1 -0
- package/dist/types/ad.types.js +3 -0
- package/dist/types/ad.types.js.map +1 -0
- package/dist/types/adcs.types.d.ts +58 -0
- package/dist/types/adcs.types.d.ts.map +1 -0
- package/dist/types/adcs.types.js +38 -0
- package/dist/types/adcs.types.js.map +1 -0
- package/dist/types/attack-graph.types.d.ts +135 -0
- package/dist/types/attack-graph.types.d.ts.map +1 -0
- package/dist/types/attack-graph.types.js +58 -0
- package/dist/types/attack-graph.types.js.map +1 -0
- package/dist/types/audit.types.d.ts +34 -0
- package/dist/types/audit.types.d.ts.map +1 -0
- package/dist/types/audit.types.js +3 -0
- package/dist/types/audit.types.js.map +1 -0
- package/dist/types/azure.types.d.ts +61 -0
- package/dist/types/azure.types.d.ts.map +1 -0
- package/dist/types/azure.types.js +3 -0
- package/dist/types/azure.types.js.map +1 -0
- package/dist/types/config.types.d.ts +63 -0
- package/dist/types/config.types.d.ts.map +1 -0
- package/dist/types/config.types.js +3 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/error.types.d.ts +33 -0
- package/dist/types/error.types.d.ts.map +1 -0
- package/dist/types/error.types.js +70 -0
- package/dist/types/error.types.js.map +1 -0
- package/dist/types/finding.types.d.ts +133 -0
- package/dist/types/finding.types.d.ts.map +1 -0
- package/dist/types/finding.types.js +3 -0
- package/dist/types/finding.types.js.map +1 -0
- package/dist/types/gpo.types.d.ts +39 -0
- package/dist/types/gpo.types.d.ts.map +1 -0
- package/dist/types/gpo.types.js +15 -0
- package/dist/types/gpo.types.js.map +1 -0
- package/dist/types/token.types.d.ts +26 -0
- package/dist/types/token.types.d.ts.map +1 -0
- package/dist/types/token.types.js +3 -0
- package/dist/types/token.types.js.map +1 -0
- package/dist/types/trust.types.d.ts +45 -0
- package/dist/types/trust.types.d.ts.map +1 -0
- package/dist/types/trust.types.js +71 -0
- package/dist/types/trust.types.js.map +1 -0
- package/dist/utils/entity-converter.d.ts +17 -0
- package/dist/utils/entity-converter.d.ts.map +1 -0
- package/dist/utils/entity-converter.js +285 -0
- package/dist/utils/entity-converter.js.map +1 -0
- package/dist/utils/graph.util.d.ts +66 -0
- package/dist/utils/graph.util.d.ts.map +1 -0
- package/dist/utils/graph.util.js +382 -0
- package/dist/utils/graph.util.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/type-name-normalizer.d.ts +5 -0
- package/dist/utils/type-name-normalizer.d.ts.map +1 -0
- package/dist/utils/type-name-normalizer.js +218 -0
- package/dist/utils/type-name-normalizer.js.map +1 -0
- package/docker-compose.yml +26 -0
- package/docs/api/README.md +178 -0
- package/docs/api/openapi.yaml +1524 -0
- package/eslint.config.js +54 -0
- package/jest.config.js +38 -0
- package/package.json +97 -0
- package/scripts/fetch-ad-cert.sh +142 -0
- package/src/.gitkeep +0 -0
- package/src/api/.gitkeep +0 -0
- package/src/api/controllers/.gitkeep +0 -0
- package/src/api/controllers/audit.controller.ts +313 -0
- package/src/api/controllers/auth.controller.ts +258 -0
- package/src/api/controllers/export.controller.ts +153 -0
- package/src/api/controllers/health.controller.ts +16 -0
- package/src/api/controllers/jobs.controller.ts +187 -0
- package/src/api/controllers/providers.controller.ts +165 -0
- package/src/api/dto/.gitkeep +0 -0
- package/src/api/dto/AuditRequest.dto.ts +8 -0
- package/src/api/dto/AuditResponse.dto.ts +19 -0
- package/src/api/dto/TokenRequest.dto.ts +8 -0
- package/src/api/dto/TokenResponse.dto.ts +14 -0
- package/src/api/middlewares/.gitkeep +0 -0
- package/src/api/middlewares/authenticate.ts +203 -0
- package/src/api/middlewares/errorHandler.ts +54 -0
- package/src/api/middlewares/rateLimit.ts +35 -0
- package/src/api/middlewares/validate.ts +32 -0
- package/src/api/routes/.gitkeep +0 -0
- package/src/api/routes/audit.routes.ts +77 -0
- package/src/api/routes/auth.routes.ts +71 -0
- package/src/api/routes/export.routes.ts +34 -0
- package/src/api/routes/health.routes.ts +14 -0
- package/src/api/routes/index.ts +40 -0
- package/src/api/routes/providers.routes.ts +24 -0
- package/src/api/validators/.gitkeep +0 -0
- package/src/api/validators/audit.schemas.ts +59 -0
- package/src/api/validators/auth.schemas.ts +59 -0
- package/src/app.ts +87 -0
- package/src/config/.gitkeep +0 -0
- package/src/config/config.schema.ts +108 -0
- package/src/config/index.ts +82 -0
- package/src/container.ts +221 -0
- package/src/data/.gitkeep +0 -0
- package/src/data/database.ts +78 -0
- package/src/data/jobs/token-cleanup.job.ts +166 -0
- package/src/data/migrations/.gitkeep +0 -0
- package/src/data/migrations/001_initial_schema.sql +47 -0
- package/src/data/migrations/migration.runner.ts +125 -0
- package/src/data/models/.gitkeep +0 -0
- package/src/data/models/Token.model.ts +35 -0
- package/src/data/repositories/.gitkeep +0 -0
- package/src/data/repositories/token.repository.ts +160 -0
- package/src/providers/.gitkeep +0 -0
- package/src/providers/azure/.gitkeep +0 -0
- package/src/providers/azure/auth.provider.ts +14 -0
- package/src/providers/azure/azure-errors.ts +189 -0
- package/src/providers/azure/azure-retry.ts +168 -0
- package/src/providers/azure/graph-client.ts +315 -0
- package/src/providers/azure/graph.provider.ts +294 -0
- package/src/providers/azure/queries/app.queries.ts +9 -0
- package/src/providers/azure/queries/policy.queries.ts +9 -0
- package/src/providers/azure/queries/user.queries.ts +10 -0
- package/src/providers/interfaces/.gitkeep +0 -0
- package/src/providers/interfaces/IGraphProvider.ts +117 -0
- package/src/providers/interfaces/ILDAPProvider.ts +142 -0
- package/src/providers/ldap/.gitkeep +0 -0
- package/src/providers/ldap/acl-parser.ts +231 -0
- package/src/providers/ldap/ad-mappers.ts +280 -0
- package/src/providers/ldap/ldap-client.ts +259 -0
- package/src/providers/ldap/ldap-errors.ts +188 -0
- package/src/providers/ldap/ldap-retry.ts +267 -0
- package/src/providers/ldap/ldap-sanitizer.ts +273 -0
- package/src/providers/ldap/ldap.provider.ts +293 -0
- package/src/providers/ldap/queries/computer.queries.ts +9 -0
- package/src/providers/ldap/queries/group.queries.ts +9 -0
- package/src/providers/ldap/queries/user.queries.ts +10 -0
- package/src/providers/smb/smb.provider.ts +653 -0
- package/src/server.ts +60 -0
- package/src/services/.gitkeep +0 -0
- package/src/services/audit/.gitkeep +0 -0
- package/src/services/audit/ad-audit.service.ts +1481 -0
- package/src/services/audit/attack-graph.service.ts +1104 -0
- package/src/services/audit/audit.service.ts +12 -0
- package/src/services/audit/azure-audit.service.ts +286 -0
- package/src/services/audit/detectors/ad/accounts.detector.ts +1232 -0
- package/src/services/audit/detectors/ad/adcs.detector.ts +449 -0
- package/src/services/audit/detectors/ad/advanced.detector.ts +1270 -0
- package/src/services/audit/detectors/ad/attack-paths.detector.ts +600 -0
- package/src/services/audit/detectors/ad/compliance.detector.ts +1421 -0
- package/src/services/audit/detectors/ad/computers.detector.ts +1188 -0
- package/src/services/audit/detectors/ad/gpo.detector.ts +485 -0
- package/src/services/audit/detectors/ad/groups.detector.ts +685 -0
- package/src/services/audit/detectors/ad/index.ts +84 -0
- package/src/services/audit/detectors/ad/kerberos.detector.ts +424 -0
- package/src/services/audit/detectors/ad/monitoring.detector.ts +501 -0
- package/src/services/audit/detectors/ad/network.detector.ts +538 -0
- package/src/services/audit/detectors/ad/password.detector.ts +324 -0
- package/src/services/audit/detectors/ad/permissions.detector.ts +637 -0
- package/src/services/audit/detectors/ad/trusts.detector.ts +315 -0
- package/src/services/audit/detectors/azure/app-security.detector.ts +246 -0
- package/src/services/audit/detectors/azure/conditional-access.detector.ts +186 -0
- package/src/services/audit/detectors/azure/privilege-security.detector.ts +176 -0
- package/src/services/audit/detectors/azure/user-security.detector.ts +280 -0
- package/src/services/audit/detectors/index.ts +18 -0
- package/src/services/audit/response-formatter.ts +604 -0
- package/src/services/audit/scoring.service.ts +234 -0
- package/src/services/auth/.gitkeep +0 -0
- package/src/services/auth/crypto.service.ts +230 -0
- package/src/services/auth/errors.ts +47 -0
- package/src/services/auth/token.service.ts +420 -0
- package/src/services/config/.gitkeep +0 -0
- package/src/services/config/config.service.ts +75 -0
- package/src/services/export/.gitkeep +0 -0
- package/src/services/export/export.service.ts +99 -0
- package/src/services/export/formatters/csv.formatter.ts +124 -0
- package/src/services/export/formatters/json.formatter.ts +160 -0
- package/src/services/jobs/azure-job-runner.ts +312 -0
- package/src/services/jobs/index.ts +9 -0
- package/src/services/jobs/job-runner.ts +1280 -0
- package/src/services/jobs/job-store.ts +384 -0
- package/src/services/jobs/job.types.ts +182 -0
- package/src/types/.gitkeep +0 -0
- package/src/types/ad.types.ts +91 -0
- package/src/types/adcs.types.ts +107 -0
- package/src/types/attack-graph.types.ts +260 -0
- package/src/types/audit.types.ts +42 -0
- package/src/types/azure.types.ts +68 -0
- package/src/types/config.types.ts +79 -0
- package/src/types/error.types.ts +69 -0
- package/src/types/finding.types.ts +284 -0
- package/src/types/gpo.types.ts +72 -0
- package/src/types/smb2.d.ts +73 -0
- package/src/types/token.types.ts +32 -0
- package/src/types/trust.types.ts +140 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/entity-converter.ts +453 -0
- package/src/utils/graph.util.ts +609 -0
- package/src/utils/logger.ts +111 -0
- package/src/utils/type-name-normalizer.ts +302 -0
- package/tests/.gitkeep +0 -0
- package/tests/e2e/.gitkeep +0 -0
- package/tests/fixtures/.gitkeep +0 -0
- package/tests/integration/.gitkeep +0 -0
- package/tests/integration/README.md +156 -0
- package/tests/integration/ad-audit.integration.test.ts +216 -0
- package/tests/integration/api/.gitkeep +0 -0
- package/tests/integration/api/endpoints.integration.test.ts +431 -0
- package/tests/integration/auth/jwt-authentication.integration.test.ts +358 -0
- package/tests/integration/providers/.gitkeep +0 -0
- package/tests/integration/providers/azure-basic.integration.test.ts +167 -0
- package/tests/integration/providers/ldap-basic.integration.test.ts +152 -0
- package/tests/integration/providers/ldap-connectivity.test.ts +44 -0
- package/tests/integration/providers/ldap-provider.integration.test.ts +347 -0
- package/tests/mocks/.gitkeep +0 -0
- package/tests/setup.ts +16 -0
- package/tests/unit/.gitkeep +0 -0
- package/tests/unit/api/middlewares/authenticate.test.ts +446 -0
- package/tests/unit/providers/.gitkeep +0 -0
- package/tests/unit/providers/azure/azure-errors.test.ts +193 -0
- package/tests/unit/providers/azure/azure-retry.test.ts +254 -0
- package/tests/unit/providers/azure/graph-provider.test.ts +313 -0
- package/tests/unit/providers/ldap/ad-mappers.test.ts +392 -0
- package/tests/unit/providers/ldap/ldap-provider.test.ts +376 -0
- package/tests/unit/providers/ldap/ldap-retry.test.ts +377 -0
- package/tests/unit/providers/ldap/ldap-sanitizer.test.ts +301 -0
- package/tests/unit/sample.test.ts +19 -0
- package/tests/unit/services/.gitkeep +0 -0
- package/tests/unit/services/audit/detectors/ad/accounts.detector.test.ts +393 -0
- package/tests/unit/services/audit/detectors/ad/advanced.detector.test.ts +380 -0
- package/tests/unit/services/audit/detectors/ad/computers.detector.test.ts +440 -0
- package/tests/unit/services/audit/detectors/ad/groups.detector.test.ts +276 -0
- package/tests/unit/services/audit/detectors/ad/kerberos.detector.test.ts +215 -0
- package/tests/unit/services/audit/detectors/ad/password.detector.test.ts +226 -0
- package/tests/unit/services/audit/detectors/ad/permissions.detector.test.ts +244 -0
- package/tests/unit/services/audit/detectors/azure/app-security.detector.test.ts +349 -0
- package/tests/unit/services/audit/detectors/azure/conditional-access.detector.test.ts +374 -0
- package/tests/unit/services/audit/detectors/azure/privilege-security.detector.test.ts +374 -0
- package/tests/unit/services/audit/detectors/azure/user-security.detector.test.ts +297 -0
- package/tests/unit/services/auth/crypto.service.test.ts +296 -0
- package/tests/unit/services/auth/token.service.test.ts +579 -0
- package/tests/unit/services/export/export.service.test.ts +241 -0
- package/tests/unit/services/export/formatters/csv.formatter.test.ts +270 -0
- package/tests/unit/services/export/formatters/json.formatter.test.ts +258 -0
- package/tests/unit/utils/.gitkeep +0 -0
- package/tsconfig.json +50 -0
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Groups Security Vulnerability Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects group-related vulnerabilities in AD.
|
|
5
|
+
* Story 1.7: AD Vulnerability Detection Engine
|
|
6
|
+
*
|
|
7
|
+
* Vulnerabilities detected (14):
|
|
8
|
+
* - GPO_MODIFY_RIGHTS (High)
|
|
9
|
+
* - DNS_ADMINS_MEMBER (High)
|
|
10
|
+
* - OVERSIZED_GROUP_CRITICAL (High)
|
|
11
|
+
* - OVERSIZED_GROUP_HIGH (Medium)
|
|
12
|
+
* - OVERSIZED_GROUP (Medium)
|
|
13
|
+
* - PRE_WINDOWS_2000_ACCESS (Medium)
|
|
14
|
+
* - DANGEROUS_GROUP_NESTING (Medium)
|
|
15
|
+
* - GROUP_EMPTY_PRIVILEGED (Low) - Phase 2C
|
|
16
|
+
* - GROUP_CIRCULAR_NESTING (Medium) - Phase 2C
|
|
17
|
+
* - GROUP_EXCESSIVE_MEMBERS (Medium) - Phase 2C
|
|
18
|
+
* - BUILTIN_MODIFIED (High) - Phase 2C
|
|
19
|
+
* - GROUP_EVERYONE_IN_PRIVILEGED (Critical) - Phase 4
|
|
20
|
+
* - GROUP_AUTHENTICATED_USERS_PRIVILEGED (High) - Phase 4
|
|
21
|
+
* - GROUP_PROTECTED_USERS_EMPTY (Medium) - Phase 4
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { ADUser, ADGroup } from '../../../../types/ad.types';
|
|
25
|
+
import { Finding } from '../../../../types/finding.types';
|
|
26
|
+
import { toAffectedUserEntities, toAffectedADGroupEntities } from '../../../../utils/entity-converter';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check for Group Policy Creator Owners membership
|
|
30
|
+
*/
|
|
31
|
+
export function detectGpoModifyRights(users: ADUser[], includeDetails: boolean): Finding {
|
|
32
|
+
const affected = users.filter((u) => {
|
|
33
|
+
if (!u.memberOf) return false;
|
|
34
|
+
return u.memberOf.some((dn) => dn.includes('CN=Group Policy Creator Owners'));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
type: 'GPO_MODIFY_RIGHTS',
|
|
39
|
+
severity: 'high',
|
|
40
|
+
category: 'groups',
|
|
41
|
+
title: 'Group Policy Creator Owners Member',
|
|
42
|
+
description: 'Users in Group Policy Creator Owners group. Can create/modify GPOs and execute code on domain machines.',
|
|
43
|
+
count: affected.length,
|
|
44
|
+
affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check for DnsAdmins membership
|
|
50
|
+
*/
|
|
51
|
+
export function detectDnsAdminsMember(users: ADUser[], includeDetails: boolean): Finding {
|
|
52
|
+
const affected = users.filter((u) => {
|
|
53
|
+
if (!u.memberOf) return false;
|
|
54
|
+
return u.memberOf.some((dn) => dn.includes('CN=DnsAdmins'));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
type: 'DNS_ADMINS_MEMBER',
|
|
59
|
+
severity: 'high',
|
|
60
|
+
category: 'groups',
|
|
61
|
+
title: 'DnsAdmins Member',
|
|
62
|
+
description: 'Users in DnsAdmins group. Can load arbitrary DLLs on domain controllers (escalation to Domain Admin).',
|
|
63
|
+
count: affected.length,
|
|
64
|
+
affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check for Pre-Windows 2000 Compatible Access membership
|
|
70
|
+
*/
|
|
71
|
+
export function detectPreWindows2000Access(users: ADUser[], includeDetails: boolean): Finding {
|
|
72
|
+
const affected = users.filter((u) => {
|
|
73
|
+
if (!u.memberOf) return false;
|
|
74
|
+
return u.memberOf.some((dn) => dn.includes('CN=Pre-Windows 2000 Compatible Access'));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
type: 'PRE_WINDOWS_2000_ACCESS',
|
|
79
|
+
severity: 'medium',
|
|
80
|
+
category: 'groups',
|
|
81
|
+
title: 'Pre-Windows 2000 Compatible Access',
|
|
82
|
+
description: 'Pre-Windows 2000 Compatible Access group has members. Overly permissive read access to AD objects.',
|
|
83
|
+
count: affected.length,
|
|
84
|
+
affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check for oversized groups (500+ members)
|
|
90
|
+
* PingCastle threshold: >500 members = critical
|
|
91
|
+
*/
|
|
92
|
+
export function detectOversizedGroupCritical(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
93
|
+
const affected = groups.filter((g) => {
|
|
94
|
+
if (!g.member) return false;
|
|
95
|
+
return g.member.length > 500;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
type: 'OVERSIZED_GROUP_CRITICAL',
|
|
100
|
+
severity: 'high',
|
|
101
|
+
category: 'groups',
|
|
102
|
+
title: 'Oversized Group (Critical)',
|
|
103
|
+
description: 'Groups with 500+ members. Management/audit difficulty, excessive privileges, performance issues.',
|
|
104
|
+
count: affected.length,
|
|
105
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check for oversized groups (200-500 members)
|
|
111
|
+
*/
|
|
112
|
+
export function detectOversizedGroupHigh(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
113
|
+
const affected = groups.filter((g) => {
|
|
114
|
+
if (!g.member) return false;
|
|
115
|
+
return g.member.length > 200 && g.member.length <= 500;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
type: 'OVERSIZED_GROUP_HIGH',
|
|
120
|
+
severity: 'medium',
|
|
121
|
+
category: 'groups',
|
|
122
|
+
title: 'Oversized Group (High)',
|
|
123
|
+
description: 'Groups with 200-500 members. Management difficulty and potential privilege creep.',
|
|
124
|
+
count: affected.length,
|
|
125
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check for oversized groups (100-500 members)
|
|
131
|
+
*/
|
|
132
|
+
export function detectOversizedGroup(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
133
|
+
const affected = groups.filter((g) => {
|
|
134
|
+
if (!g.member) return false;
|
|
135
|
+
return g.member.length > 100 && g.member.length <= 500;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
type: 'OVERSIZED_GROUP',
|
|
140
|
+
severity: 'medium',
|
|
141
|
+
category: 'groups',
|
|
142
|
+
title: 'Oversized Group',
|
|
143
|
+
description: 'Groups with 100-500 members. May indicate overly broad permissions.',
|
|
144
|
+
count: affected.length,
|
|
145
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check for dangerous group nesting (sensitive groups nested in less sensitive groups)
|
|
151
|
+
*/
|
|
152
|
+
export function detectDangerousGroupNesting(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
153
|
+
const protectedGroups = [
|
|
154
|
+
'Domain Admins',
|
|
155
|
+
'Enterprise Admins',
|
|
156
|
+
'Schema Admins',
|
|
157
|
+
'Administrators',
|
|
158
|
+
'Account Operators',
|
|
159
|
+
'Backup Operators',
|
|
160
|
+
'Server Operators',
|
|
161
|
+
'Print Operators',
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const affected = groups.filter((g) => {
|
|
165
|
+
if (!g.memberOf) return false;
|
|
166
|
+
|
|
167
|
+
// Check if this is a protected group
|
|
168
|
+
const isProtected = protectedGroups.some((pg) => g.dn.includes(`CN=${pg}`));
|
|
169
|
+
if (!isProtected) return false;
|
|
170
|
+
|
|
171
|
+
// Check if it's nested in a non-protected group
|
|
172
|
+
const hasUnexpectedNesting = g.memberOf.some((dn) => {
|
|
173
|
+
return !protectedGroups.some((pg) => dn.includes(`CN=${pg}`));
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return hasUnexpectedNesting;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
type: 'DANGEROUS_GROUP_NESTING',
|
|
181
|
+
severity: 'medium',
|
|
182
|
+
category: 'groups',
|
|
183
|
+
title: 'Dangerous Group Nesting',
|
|
184
|
+
description: 'Sensitive group nested in less sensitive group. Unintended privilege escalation path.',
|
|
185
|
+
count: affected.length,
|
|
186
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ==================== PHASE 2C DETECTORS ====================
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Detect empty privileged groups
|
|
194
|
+
* Privileged groups should either be used or documented as intentionally empty
|
|
195
|
+
*/
|
|
196
|
+
export function detectGroupEmptyPrivileged(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
197
|
+
const privilegedGroups = [
|
|
198
|
+
'Domain Admins',
|
|
199
|
+
'Enterprise Admins',
|
|
200
|
+
'Schema Admins',
|
|
201
|
+
'Administrators',
|
|
202
|
+
'Account Operators',
|
|
203
|
+
'Server Operators',
|
|
204
|
+
'Backup Operators',
|
|
205
|
+
'Print Operators',
|
|
206
|
+
'DnsAdmins',
|
|
207
|
+
'Group Policy Creator Owners',
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const affected = groups.filter((g) => {
|
|
211
|
+
const name = g.sAMAccountName || g.displayName || '';
|
|
212
|
+
const isPrivileged = privilegedGroups.some(
|
|
213
|
+
(pg) => name.toLowerCase() === pg.toLowerCase() || g.dn.toLowerCase().includes(`cn=${pg.toLowerCase()}`)
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
if (!isPrivileged) return false;
|
|
217
|
+
|
|
218
|
+
// Check if group is empty
|
|
219
|
+
const memberCount = g.member?.length ?? 0;
|
|
220
|
+
return memberCount === 0;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
type: 'GROUP_EMPTY_PRIVILEGED',
|
|
225
|
+
severity: 'low',
|
|
226
|
+
category: 'groups',
|
|
227
|
+
title: 'Empty Privileged Group',
|
|
228
|
+
description:
|
|
229
|
+
'Privileged groups with no members. While not a vulnerability, empty admin groups may indicate misconfiguration or unused infrastructure.',
|
|
230
|
+
count: affected.length,
|
|
231
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
232
|
+
details:
|
|
233
|
+
affected.length > 0
|
|
234
|
+
? {
|
|
235
|
+
groups: affected.map((g) => g.sAMAccountName || g.dn),
|
|
236
|
+
recommendation: 'Document intentionally empty groups or remove if unused.',
|
|
237
|
+
}
|
|
238
|
+
: undefined,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Detect circular group nesting
|
|
244
|
+
* Groups that are members of each other create infinite loops
|
|
245
|
+
*/
|
|
246
|
+
export function detectGroupCircularNesting(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
247
|
+
const groupDnMap = new Map<string, ADGroup>();
|
|
248
|
+
for (const g of groups) {
|
|
249
|
+
groupDnMap.set(g.dn.toLowerCase(), g);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const circularGroups: ADGroup[] = [];
|
|
253
|
+
const visited = new Set<string>();
|
|
254
|
+
|
|
255
|
+
const detectCycle = (groupDn: string, path: Set<string>): boolean => {
|
|
256
|
+
const normalizedDn = groupDn.toLowerCase();
|
|
257
|
+
if (path.has(normalizedDn)) return true; // Cycle detected
|
|
258
|
+
if (visited.has(normalizedDn)) return false; // Already checked, no cycle
|
|
259
|
+
|
|
260
|
+
visited.add(normalizedDn);
|
|
261
|
+
path.add(normalizedDn);
|
|
262
|
+
|
|
263
|
+
const group = groupDnMap.get(normalizedDn);
|
|
264
|
+
if (group?.memberOf) {
|
|
265
|
+
for (const parentDn of group.memberOf) {
|
|
266
|
+
if (groupDnMap.has(parentDn.toLowerCase())) {
|
|
267
|
+
if (detectCycle(parentDn, path)) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
path.delete(normalizedDn);
|
|
275
|
+
return false;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
for (const group of groups) {
|
|
279
|
+
visited.clear();
|
|
280
|
+
if (detectCycle(group.dn, new Set())) {
|
|
281
|
+
circularGroups.push(group);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
type: 'GROUP_CIRCULAR_NESTING',
|
|
287
|
+
severity: 'medium',
|
|
288
|
+
category: 'groups',
|
|
289
|
+
title: 'Circular Group Nesting',
|
|
290
|
+
description:
|
|
291
|
+
'Groups contain circular membership references. This can cause authentication issues and makes privilege analysis unreliable.',
|
|
292
|
+
count: circularGroups.length,
|
|
293
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(circularGroups) : undefined,
|
|
294
|
+
details:
|
|
295
|
+
circularGroups.length > 0
|
|
296
|
+
? {
|
|
297
|
+
recommendation: 'Remove circular nesting by reviewing and restructuring group membership.',
|
|
298
|
+
impact: 'May cause token bloat, authentication failures, and unreliable access control.',
|
|
299
|
+
}
|
|
300
|
+
: undefined,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Detect groups with excessive direct members
|
|
306
|
+
* Large groups are difficult to manage and review
|
|
307
|
+
*/
|
|
308
|
+
export function detectGroupExcessiveMembers(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
309
|
+
const EXCESSIVE_THRESHOLD = 100;
|
|
310
|
+
|
|
311
|
+
const affected = groups.filter((g) => {
|
|
312
|
+
const memberCount = g.member?.length ?? 0;
|
|
313
|
+
return memberCount > EXCESSIVE_THRESHOLD;
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Sort by member count (most members first)
|
|
317
|
+
affected.sort((a, b) => (b.member?.length ?? 0) - (a.member?.length ?? 0));
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
type: 'GROUP_EXCESSIVE_MEMBERS',
|
|
321
|
+
severity: 'medium',
|
|
322
|
+
category: 'groups',
|
|
323
|
+
title: 'Group with Excessive Members',
|
|
324
|
+
description: `Groups with more than ${EXCESSIVE_THRESHOLD} direct members. Large groups are difficult to audit and may grant unintended access.`,
|
|
325
|
+
count: affected.length,
|
|
326
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
327
|
+
details:
|
|
328
|
+
affected.length > 0
|
|
329
|
+
? {
|
|
330
|
+
threshold: EXCESSIVE_THRESHOLD,
|
|
331
|
+
largestGroups: affected.slice(0, 5).map((g) => ({
|
|
332
|
+
name: g.sAMAccountName || g.dn,
|
|
333
|
+
memberCount: g.member?.length ?? 0,
|
|
334
|
+
})),
|
|
335
|
+
recommendation:
|
|
336
|
+
'Review large groups and consider breaking into smaller, role-based groups for better access control.',
|
|
337
|
+
}
|
|
338
|
+
: undefined,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Detect builtin groups with non-standard members
|
|
344
|
+
* Builtin groups should only contain expected system accounts
|
|
345
|
+
*/
|
|
346
|
+
export function detectBuiltinModified(groups: ADGroup[], includeDetails: boolean): Finding {
|
|
347
|
+
// Builtin groups and their expected default members
|
|
348
|
+
const builtinDefaults: { [key: string]: string[] } = {
|
|
349
|
+
Administrators: ['Administrator', 'Domain Admins', 'Enterprise Admins'],
|
|
350
|
+
Users: ['Domain Users', 'Authenticated Users', 'INTERACTIVE'],
|
|
351
|
+
Guests: ['Guest', 'Domain Guests'],
|
|
352
|
+
'Remote Desktop Users': [],
|
|
353
|
+
'Network Configuration Operators': [],
|
|
354
|
+
'Performance Monitor Users': [],
|
|
355
|
+
'Performance Log Users': [],
|
|
356
|
+
'Distributed COM Users': [],
|
|
357
|
+
'IIS_IUSRS': [],
|
|
358
|
+
'Cryptographic Operators': [],
|
|
359
|
+
'Event Log Readers': [],
|
|
360
|
+
'Certificate Service DCOM Access': [],
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const affected = groups.filter((g) => {
|
|
364
|
+
const name = g.sAMAccountName || '';
|
|
365
|
+
|
|
366
|
+
// Check if it's a builtin group we monitor
|
|
367
|
+
if (!builtinDefaults[name]) return false;
|
|
368
|
+
|
|
369
|
+
const expectedMembers = builtinDefaults[name];
|
|
370
|
+
const actualMembers = g.member ?? [];
|
|
371
|
+
|
|
372
|
+
// Check for unexpected members
|
|
373
|
+
const hasUnexpectedMembers = actualMembers.some((memberDn) => {
|
|
374
|
+
const memberCn = memberDn.match(/CN=([^,]+)/i)?.[1] || '';
|
|
375
|
+
// Check if this member is in the expected list
|
|
376
|
+
return !expectedMembers.some((exp) => memberCn.toLowerCase().includes(exp.toLowerCase()));
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return hasUnexpectedMembers;
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
type: 'BUILTIN_MODIFIED',
|
|
384
|
+
severity: 'high',
|
|
385
|
+
category: 'groups',
|
|
386
|
+
title: 'Builtin Group Modified',
|
|
387
|
+
description:
|
|
388
|
+
'Builtin groups contain non-standard members. This may indicate privilege escalation or backdoor access.',
|
|
389
|
+
count: affected.length,
|
|
390
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
391
|
+
details:
|
|
392
|
+
affected.length > 0
|
|
393
|
+
? {
|
|
394
|
+
groups: affected.map((g) => g.sAMAccountName || g.dn),
|
|
395
|
+
recommendation:
|
|
396
|
+
'Review membership of builtin groups and remove unexpected members. Document any intentional additions.',
|
|
397
|
+
risk: 'Attackers often add accounts to builtin groups for persistent access.',
|
|
398
|
+
}
|
|
399
|
+
: undefined,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Detect Everyone group in privileged groups
|
|
405
|
+
*
|
|
406
|
+
* The Everyone principal should never be a member of privileged groups.
|
|
407
|
+
* This grants all users (including anonymous) privileged access.
|
|
408
|
+
*
|
|
409
|
+
* @param groups - Array of AD groups
|
|
410
|
+
* @param includeDetails - Whether to include affected entity details
|
|
411
|
+
* @returns Finding for GROUP_EVERYONE_IN_PRIVILEGED
|
|
412
|
+
*/
|
|
413
|
+
export function detectGroupEveryoneInPrivileged(
|
|
414
|
+
groups: ADGroup[],
|
|
415
|
+
includeDetails: boolean
|
|
416
|
+
): Finding {
|
|
417
|
+
const privilegedGroups = [
|
|
418
|
+
'Domain Admins',
|
|
419
|
+
'Enterprise Admins',
|
|
420
|
+
'Schema Admins',
|
|
421
|
+
'Administrators',
|
|
422
|
+
'Account Operators',
|
|
423
|
+
'Server Operators',
|
|
424
|
+
'Backup Operators',
|
|
425
|
+
'Print Operators',
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
const affected = groups.filter((g) => {
|
|
429
|
+
const groupName = g.sAMAccountName || g.cn || '';
|
|
430
|
+
const isPrivileged = privilegedGroups.some(
|
|
431
|
+
(pg) => groupName.toLowerCase() === pg.toLowerCase()
|
|
432
|
+
);
|
|
433
|
+
if (!isPrivileged || !g.member) return false;
|
|
434
|
+
|
|
435
|
+
// Check if Everyone (S-1-1-0) or World is a member
|
|
436
|
+
return g.member.some(
|
|
437
|
+
(m) =>
|
|
438
|
+
m.toLowerCase().includes('everyone') ||
|
|
439
|
+
m.includes('S-1-1-0') ||
|
|
440
|
+
m.toLowerCase().includes('world')
|
|
441
|
+
);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
type: 'GROUP_EVERYONE_IN_PRIVILEGED',
|
|
446
|
+
severity: 'critical',
|
|
447
|
+
category: 'groups',
|
|
448
|
+
title: 'Everyone in Privileged Group',
|
|
449
|
+
description:
|
|
450
|
+
'The Everyone principal is a member of a privileged group. ' +
|
|
451
|
+
'This grants ALL users (including anonymous) administrative privileges.',
|
|
452
|
+
count: affected.length,
|
|
453
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
454
|
+
details: {
|
|
455
|
+
recommendation: 'Immediately remove Everyone from privileged groups.',
|
|
456
|
+
risk: 'Complete domain compromise - anyone can authenticate as admin.',
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Detect Authenticated Users in privileged groups
|
|
463
|
+
*
|
|
464
|
+
* Authenticated Users should not be members of privileged groups.
|
|
465
|
+
* This grants all domain users admin access.
|
|
466
|
+
*
|
|
467
|
+
* @param groups - Array of AD groups
|
|
468
|
+
* @param includeDetails - Whether to include affected entity details
|
|
469
|
+
* @returns Finding for GROUP_AUTHENTICATED_USERS_PRIVILEGED
|
|
470
|
+
*/
|
|
471
|
+
export function detectGroupAuthenticatedUsersPrivileged(
|
|
472
|
+
groups: ADGroup[],
|
|
473
|
+
includeDetails: boolean
|
|
474
|
+
): Finding {
|
|
475
|
+
const privilegedGroups = [
|
|
476
|
+
'Domain Admins',
|
|
477
|
+
'Enterprise Admins',
|
|
478
|
+
'Schema Admins',
|
|
479
|
+
'Administrators',
|
|
480
|
+
'Account Operators',
|
|
481
|
+
'Server Operators',
|
|
482
|
+
'Backup Operators',
|
|
483
|
+
];
|
|
484
|
+
|
|
485
|
+
const affected = groups.filter((g) => {
|
|
486
|
+
const groupName = g.sAMAccountName || g.cn || '';
|
|
487
|
+
const isPrivileged = privilegedGroups.some(
|
|
488
|
+
(pg) => groupName.toLowerCase() === pg.toLowerCase()
|
|
489
|
+
);
|
|
490
|
+
if (!isPrivileged || !g.member) return false;
|
|
491
|
+
|
|
492
|
+
// Check if Authenticated Users (S-1-5-11) is a member
|
|
493
|
+
return g.member.some(
|
|
494
|
+
(m) =>
|
|
495
|
+
m.toLowerCase().includes('authenticated users') ||
|
|
496
|
+
m.includes('S-1-5-11') ||
|
|
497
|
+
m.toLowerCase().includes('utilisateurs authentifiés')
|
|
498
|
+
);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
type: 'GROUP_AUTHENTICATED_USERS_PRIVILEGED',
|
|
503
|
+
severity: 'high',
|
|
504
|
+
category: 'groups',
|
|
505
|
+
title: 'Authenticated Users in Privileged Group',
|
|
506
|
+
description:
|
|
507
|
+
'Authenticated Users principal is a member of a privileged group. ' +
|
|
508
|
+
'This grants ALL authenticated domain users administrative privileges.',
|
|
509
|
+
count: affected.length,
|
|
510
|
+
affectedEntities: includeDetails ? toAffectedADGroupEntities(affected) : undefined,
|
|
511
|
+
details: {
|
|
512
|
+
recommendation: 'Remove Authenticated Users from privileged groups immediately.',
|
|
513
|
+
risk: 'Any domain user can perform administrative actions.',
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Detect empty Protected Users group
|
|
520
|
+
*
|
|
521
|
+
* The Protected Users group provides enhanced security for privileged accounts.
|
|
522
|
+
* If empty, privileged accounts lack these protections.
|
|
523
|
+
*
|
|
524
|
+
* @param groups - Array of AD groups
|
|
525
|
+
* @param _includeDetails - Whether to include affected entity details
|
|
526
|
+
* @returns Finding for GROUP_PROTECTED_USERS_EMPTY
|
|
527
|
+
*/
|
|
528
|
+
export function detectGroupProtectedUsersEmpty(
|
|
529
|
+
groups: ADGroup[],
|
|
530
|
+
_includeDetails: boolean
|
|
531
|
+
): Finding {
|
|
532
|
+
const protectedUsersGroup = groups.find(
|
|
533
|
+
(g) =>
|
|
534
|
+
(g.sAMAccountName || g.cn || '').toLowerCase() === 'protected users'
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
const isEmpty =
|
|
538
|
+
!protectedUsersGroup ||
|
|
539
|
+
!protectedUsersGroup.member ||
|
|
540
|
+
protectedUsersGroup.member.length === 0;
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
type: 'GROUP_PROTECTED_USERS_EMPTY',
|
|
544
|
+
severity: 'medium',
|
|
545
|
+
category: 'groups',
|
|
546
|
+
title: 'Protected Users Group Empty',
|
|
547
|
+
description:
|
|
548
|
+
'The Protected Users group has no members. ' +
|
|
549
|
+
'Privileged accounts should be added to this group for enhanced security (NTLM disabled, Kerberos delegation blocked, credential caching prevented).',
|
|
550
|
+
count: isEmpty ? 1 : 0,
|
|
551
|
+
details: {
|
|
552
|
+
memberCount: protectedUsersGroup?.member?.length || 0,
|
|
553
|
+
recommendation:
|
|
554
|
+
'Add Domain Admins, Enterprise Admins, and other privileged accounts to Protected Users group.',
|
|
555
|
+
benefits: [
|
|
556
|
+
'NTLM authentication disabled',
|
|
557
|
+
'Kerberos delegation blocked',
|
|
558
|
+
'Credential caching prevented',
|
|
559
|
+
'DES/RC4 encryption disabled',
|
|
560
|
+
],
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Detect excessive privileged accounts
|
|
567
|
+
* Flags when there are too many accounts in high-privilege groups
|
|
568
|
+
* PingCastle threshold: > 10 Domain Admins, > 50 total privileged
|
|
569
|
+
*/
|
|
570
|
+
export function detectExcessivePrivilegedAccounts(
|
|
571
|
+
users: ADUser[],
|
|
572
|
+
groups: ADGroup[],
|
|
573
|
+
includeDetails: boolean
|
|
574
|
+
): Finding {
|
|
575
|
+
const privilegedGroupNames = [
|
|
576
|
+
'Domain Admins',
|
|
577
|
+
'Enterprise Admins',
|
|
578
|
+
'Schema Admins',
|
|
579
|
+
'Administrators',
|
|
580
|
+
'Account Operators',
|
|
581
|
+
'Backup Operators',
|
|
582
|
+
'Server Operators',
|
|
583
|
+
'Print Operators',
|
|
584
|
+
];
|
|
585
|
+
|
|
586
|
+
// Count unique privileged users
|
|
587
|
+
const privilegedUsers = new Set<string>();
|
|
588
|
+
const groupCounts: Record<string, number> = {};
|
|
589
|
+
|
|
590
|
+
for (const user of users) {
|
|
591
|
+
if (!user.memberOf) continue;
|
|
592
|
+
for (const groupDn of user.memberOf) {
|
|
593
|
+
for (const groupName of privilegedGroupNames) {
|
|
594
|
+
if (groupDn.toUpperCase().includes(`CN=${groupName.toUpperCase()}`)) {
|
|
595
|
+
privilegedUsers.add(user.dn);
|
|
596
|
+
groupCounts[groupName] = (groupCounts[groupName] || 0) + 1;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Also count from group membership directly
|
|
603
|
+
for (const group of groups) {
|
|
604
|
+
const groupName = privilegedGroupNames.find((name) =>
|
|
605
|
+
group.sAMAccountName?.toUpperCase() === name.toUpperCase() ||
|
|
606
|
+
group.dn?.toUpperCase().includes(`CN=${name.toUpperCase()}`)
|
|
607
|
+
);
|
|
608
|
+
if (groupName && group.member) {
|
|
609
|
+
groupCounts[groupName] = Math.max(groupCounts[groupName] || 0, group.member.length);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const totalPrivileged = privilegedUsers.size;
|
|
614
|
+
const domainAdmins = groupCounts['Domain Admins'] || 0;
|
|
615
|
+
const enterpriseAdmins = groupCounts['Enterprise Admins'] || 0;
|
|
616
|
+
|
|
617
|
+
// Flag if > 10 Domain Admins OR > 50 total privileged (PingCastle thresholds)
|
|
618
|
+
const isExcessive = domainAdmins > 10 || totalPrivileged > 50;
|
|
619
|
+
|
|
620
|
+
return {
|
|
621
|
+
type: 'EXCESSIVE_PRIVILEGED_ACCOUNTS',
|
|
622
|
+
severity: isExcessive ? 'medium' : 'low',
|
|
623
|
+
category: 'groups',
|
|
624
|
+
title: 'Excessive Privileged Accounts',
|
|
625
|
+
description:
|
|
626
|
+
'Large number of accounts with administrative privileges increases attack surface. ' +
|
|
627
|
+
'Each privileged account is a potential target for credential theft.',
|
|
628
|
+
count: isExcessive ? totalPrivileged : 0,
|
|
629
|
+
affectedEntities: includeDetails && isExcessive
|
|
630
|
+
? Array.from(privilegedUsers).map((dn) => {
|
|
631
|
+
const user = users.find((u) => u.dn === dn);
|
|
632
|
+
if (user) {
|
|
633
|
+
const entities = toAffectedUserEntities([user]);
|
|
634
|
+
return entities[0] || dn;
|
|
635
|
+
}
|
|
636
|
+
return dn;
|
|
637
|
+
})
|
|
638
|
+
: undefined,
|
|
639
|
+
details: {
|
|
640
|
+
totalPrivilegedUsers: totalPrivileged,
|
|
641
|
+
domainAdmins,
|
|
642
|
+
enterpriseAdmins,
|
|
643
|
+
schemaAdmins: groupCounts['Schema Admins'] || 0,
|
|
644
|
+
administrators: groupCounts['Administrators'] || 0,
|
|
645
|
+
accountOperators: groupCounts['Account Operators'] || 0,
|
|
646
|
+
backupOperators: groupCounts['Backup Operators'] || 0,
|
|
647
|
+
serverOperators: groupCounts['Server Operators'] || 0,
|
|
648
|
+
printOperators: groupCounts['Print Operators'] || 0,
|
|
649
|
+
threshold: 'Domain Admins > 10 or total privileged > 50',
|
|
650
|
+
recommendation: 'Review privileged group memberships and apply least privilege principle.',
|
|
651
|
+
},
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Detect all group-related vulnerabilities
|
|
657
|
+
*/
|
|
658
|
+
export function detectGroupsVulnerabilities(
|
|
659
|
+
users: ADUser[],
|
|
660
|
+
groups: ADGroup[],
|
|
661
|
+
includeDetails: boolean
|
|
662
|
+
): Finding[] {
|
|
663
|
+
return [
|
|
664
|
+
// User membership checks
|
|
665
|
+
detectGpoModifyRights(users, includeDetails),
|
|
666
|
+
detectDnsAdminsMember(users, includeDetails),
|
|
667
|
+
detectPreWindows2000Access(users, includeDetails),
|
|
668
|
+
// Group analysis checks
|
|
669
|
+
detectOversizedGroupCritical(groups, includeDetails),
|
|
670
|
+
detectOversizedGroupHigh(groups, includeDetails),
|
|
671
|
+
detectOversizedGroup(groups, includeDetails),
|
|
672
|
+
detectDangerousGroupNesting(groups, includeDetails),
|
|
673
|
+
// Phase 2C: Enhanced detections
|
|
674
|
+
detectGroupEmptyPrivileged(groups, includeDetails),
|
|
675
|
+
detectGroupCircularNesting(groups, includeDetails),
|
|
676
|
+
detectGroupExcessiveMembers(groups, includeDetails),
|
|
677
|
+
detectBuiltinModified(groups, includeDetails),
|
|
678
|
+
// Phase 4: Advanced detections
|
|
679
|
+
detectGroupEveryoneInPrivileged(groups, includeDetails),
|
|
680
|
+
detectGroupAuthenticatedUsersPrivileged(groups, includeDetails),
|
|
681
|
+
detectGroupProtectedUsersEmpty(groups, includeDetails),
|
|
682
|
+
// NEW: Excessive privileged accounts
|
|
683
|
+
detectExcessivePrivilegedAccounts(users, groups, includeDetails),
|
|
684
|
+
].filter((finding) => finding.count > 0);
|
|
685
|
+
}
|