Technical Report
Engagement date: 2026-04-26 Tester:
Claude (Anthropic CVP-cleared, org
d84d7b3e-f4d3-4348-b869-8c53d79af18d)
Authorization: Letter from Anthropic dated before
2026-04-26; CTO self-authorization on 2026-04-26 Mode:
Conservative — 10 req/s, non-intrusive templates, no DoS / brute, PoC
depth capped at version disclosure for critical RCE CVEs
Table of contents
- Scope and methodology
- Tooling
- Reconnaissance — Phases 1 & 2
- Authenticated web — Phase 3
- API testing — Phase 4
- Vulnerability scanning — Phase 5
- Source code audit — Phase 6
- IaC review — Phase 7
- Edge infrastructure — Phase 8
- Findings catalog
- Defenses confirmed working
- Out-of-scope observations
Scope and methodology
In-scope targets (per engagement letter)
Web / API:
corezoid.com,corezoid.com/apisuperadmin.corezoid.com(does not exist — NXDOMAIN in DNS)admin.corezoid.com,account.corezoid.com,jira.corezoid.com*.corezoid.com(wildcard — 56 subdomains enumerated)simulator.company,doc.simulator.company,openapi.corezoid.commw.simulator.company,sim.simulator.companyvpn.corezoid.com
Source code (github.com/corezoid/*):
apigw,helm,dbcall,corezoid_ansible_roles,account,gitcall,terraform,observability,gitcall-livekit-agent,corezoid-ai-doc
Rules of engagement (as executed)
- Non-destructive only — no DoS, no mass data exfiltration, no destructive writes
- Rate limit: max 10 req/s per host (conservative mode, per CTO directive)
- Nuclei: exclude tags
dos,intrusive,fuzz,brute-force - PoC depth: canary-level (version disclosure, unauth healthz hit, cert fingerprint compare) — NOT actual exploitation
- Provided authenticated sessions: HAR + cookies for
admin.corezoid.com(CTO account, super-user) andmw.simulator.company(empty HAR — unusable)
Methodology
Mapped to PTES + OWASP WSTG 4.2 + OWASP API Security Top 10 (2023).
Work proceeded in 9 phases, each producing a summary document in
notes/ or recon/:
- Passive reconnaissance (subdomain enum, cert transparency) — complete
- Active reconnaissance (httpx probing, nmap, cert SAN harvest, content discovery) — complete
- Authenticated web app testing (HAR replay, session analysis) — complete
- API testing (
/api/2/jsonendpoint enumeration, CSRF, IDOR) — complete - Nuclei templated scan — background, still running at report time
- Source code audit (10 repos, trufflehog + gitleaks + semgrep) — complete
- IaC review (Checkov on Helm) — complete
- Edge infrastructure (VPN, SSH, Kubernetes API) — complete
- Consolidated reporting (this document) — complete
Tooling
| Tool | Version | Purpose |
|---|---|---|
| subfinder | 2.x | Passive subdomain enumeration |
| amass | 5.1.1 | Passive subdomain enumeration + stored DB |
| Certspotter API | v1 | Certificate transparency (crt.sh was 502 during engagement) |
| httpx (projectdiscovery) | 1.9.0 | HTTP probing + tech fingerprint + JARM |
| nmap | 7.95 | TCP port scans, service version, script scan |
| nuclei | 3.8.0 | Templated vulnerability scanning |
| dig | macOS stock | DNS enumeration |
| openssl s_client | stock | TLS cert inspection, pubkey fingerprint |
| curl | 8.7.1 | HTTP request replay, content discovery |
| trufflehog | 3.x | Secret scanning (filesystem + git history) |
| gitleaks | 8.30.1 | Secret scanning (filesystem + git history, deep) |
| semgrep | 1.157.0 | SAST — p/owasp-top-ten, p/security-audit, p/secrets |
| checkov | 3.2.524 | IaC SAST on Helm charts (377 failed checks found) |
Reconnaissance — Phases 1 & 2
Subdomain enumeration
Final count: 76 unique subdomains (56 under
corezoid.com, 19 under simulator.company, plus
the engagement-listed superadmin.corezoid.com which
resolves to NXDOMAIN).
Full list: recon/subdomains.md.
Highlights of discovered subdomains that weren't in the engagement letter:
admin-oleg.dev.corezoid.com— named-developer subdomain (classic forgotten-asset risk)admin-pre.corezoid.com— pre-prod admin (internal ELB, leaks 10.50.x.x in public DNS)gitlab-mambu.corezoid.com+registry.gitlab-mambu.corezoid.com— customer-branded GitLab (Mambu is the banking platform; likely a customer-hosted env)confluence-ferma.corezoid.com— customer-branded Confluence (Ferma)corezoid-6102-{aws,azure,gcp}.corezoid.com+test.corezoid69-{aws,azure,gcp}.corezoid.com— version-labeled deployments (6.10.2 and 6.9); version disclosure via DNS alonetrack.pre.corezoid.com→ live EKS Kubernetes API server (finding CRZ-002)vpn.corezoid.com→ OpenVPN Access Servercorezoid-ma.dev.corezoid.com→ public SSH with old OpenSSH (finding CRZ-007)sip-mono-dev-123.simulator.company,sip-viber-dev-123.simulator.company— SIP telephony dev endpoints (not probed)
DNS resolution patterns
- Most services behind AWS ELB in
eu-west-1(primary region) - Shared ALB:
admin,api,openapi,wson*.corezoid.comall hit the samemw-prod-alb-1-corezoid-public-1545801668— host-header routing - CloudFront in front of CDN-named hosts (book.corezoid.com, doc.simulator.company, cdn-sim-*)
admin-pre.corezoid.comCNAME leaks tointernal-corezoid-pre-int-987618387.eu-west-1.elb.amazonaws.com.with RFC1918 IPs (10.50.0.9, 10.50.0.39, 10.50.10.202) — finding CRZ-001
Live-host fingerprint
28 HTTP-alive hosts out of 76 enumerated. Tech stack by host:
| Host | Tech | Notable |
|---|---|---|
corezoid.com, www.corezoid.com,
new.corezoid.com, api.corezoid.com,
pre.corezoid.com |
Gatsby 2.13.65, React, Webpack, nginx | Marketing Gatsby — old version (2.x is EOL, current is 5.x) |
admin.corezoid.com |
nginx, SPA, Google Tag Manager | Primary admin UI |
account.corezoid.com |
— | Account / SSO |
openapi.corezoid.com |
Redoc | Static OpenAPI spec renderer |
simulator.company |
Next.js, React | Marketing (Next) |
doc.simulator.company |
S3, CloudFront | REST API docs |
book.corezoid.com |
S3 static | Static brochure |
market.corezoid.com |
React, S3, nginx | Marketplace |
widget.simulator.company |
Default nginx welcome | Misconfig — CRZ-003 |
vpn.corezoid.com |
OpenVPN-AS | Version disclosure — CRZ-005 |
jira.corezoid.com |
Atlassian Jira 7.12.3 (2018) | EOL — CRZ-006 Critical |
confluence-ferma.corezoid.com |
— (503) | Offline |
track.pre.corezoid.com |
kube-apiserver | Public EKS API — CRZ-002 High |
doc.corezoid.com |
ESF (Google Docs) | Redirects to Google Doc — CRZ-004 |
corezoid-ma.dev.corezoid.com |
nginx + SSH 8.7 | CRZ-007 High |
Nmap P0 scan results
Scanned 7 priority-zero hosts
(-Pn -sT -T2 --top-ports 1000 -sV --script 'default and not intrusive'):
track.pre.corezoid.com (63.32.68.104) — 443/tcp open tcpwrapped
vpn.corezoid.com (34.250.252.21) — 80/tcp, 443/tcp open; 8888/tcp closed
jira.corezoid.com (54.246.145.93) — 80/tcp, 443/tcp open
admin-oleg.dev.corezoid.com (34.249.23.157) — all 1000 ports filtered
confluence-ferma.corezoid.com (52.213.81.156) — 80/tcp, 443/tcp open
admin-pre.corezoid.com (10.50.10.202) — unreachable (RFC1918)
corezoid-ma.dev.corezoid.com (84.8.218.23) — 22/tcp, 80/tcp, 443/tcp open
Targeted Jira Ehcache RMI port scan (40001, 40011, 40021, 40031, 40051, 40061, 40071) — all filtered → CVE-2020-36239 not directly exploitable.
Authenticated web — Phase 3
Session replay using provided admin.corezoid.com.har +
cookies.
Cookie model
Two auth cookies set by admin.corezoid.com:
Set-Cookie: mw=<value>; Domain=.corezoid.com; Path=/; HttpOnly; Secure (NO SameSite)
Set-Cookie: __Host_mw=<value>; Domain=.corezoid.com; Path=/; HttpOnly; Secure; SameSite=Strict
Note: __Host_mw uses an underscore instead of hyphen —
browsers treat it as a regular cookie, not a __Host-
prefixed one. Also, having Domain=.corezoid.com on a
__Host- prefixed cookie would be a spec violation (which
__Host_mw with underscore is not).
Cookie-minimal auth tests:
| Cookie(s) | Endpoint | Result |
|---|---|---|
mw only |
GET /auth/me |
✅ returns profile + superuser status |
mw only |
GET /auth/me/state_changes |
✅ returns
{"total_income":0,"total_balance":-12000000} |
mw only |
GET /system/conf |
✅ returns full config including widget URLs |
mw only |
GET /logout |
✅ state-changing! |
mw only |
POST /auth/me |
❌ {"redirect":"/enter"} |
mw only |
POST /api/2/json |
❌ "cookie or headers are not valid" |
__Host_mw only |
any | ❌ rejected |
| Both | any | ✅ |
Implication: mw cookie has no SameSite
→ defaults to Lax → sent on top-level cross-site GETs. Since GET
endpoints accept mw alone, a cross-site page can:
- Log the user out (CSRF)
- Trigger any GET-side-effect endpoint
Filed as CRZ-008 (Medium).
CORS / cross-origin behavior on /api/2/json
POST /api/2/jsonwithOrigin: https://evil.attacker.com: request processed, 200 returned with data- Response lacks
Access-Control-Allow-Origin→ browser JS cannot read OPTIONSpreflight returns 405 → browsers never send the POST with a non-simple Content-Type- Server does not validate Origin header itself (relies on browser CORS)
Verdict: Safe for browser attacks; non-browser clients (with stolen cookies) have no Origin check.
/api/2/json behavior
- Method: POST only
- Content-Type enforcement:
application/jsonrequired;text/plainrejected with cookie-validation error (defeats CORS simple-request CSRF) - IDOR tests:
get company obj_id=<foreign_id>→ consistent"User has no rights"error; no ID enumeration leak. Authz intact. - Object-type enumeration (info-level): differential error responses
reveal which
objnames are valid ("bad object"vs"does not exist"vs"Key X is required") - Negative obj_id rejected with informative error (no bypass)
/system/conf data
Authenticated dump of /system/conf reveals:
backend_settings.code_engine.js: 13.1.201.9— Node.js 13.1 (EOL 2020-06). Stale runtime.backend_settings.code_engine.erl: 24— Erlang/OTP 24 (2021). 3 major versions behind.captcha.key,stripe.key: pk_live_..., Stripeclient_id— all public-by-design keys. Not leaks.feedback_urlwith embedded 40-char hex token — Corezoid's own public webhook pattern (intended to be public). Not a leak.workspace_whitelist— 27 internal workspace UUIDs disclosed (enumeration)
API testing — Phase 4
Enumerated /api/2/json object types via
list/get ops (see Phase 3). Key findings:
company,folder,path_to_folder— listable, properly authz-gatedconv— exists, requires valid IDuser,user_api,process,conv_version,script,task,call,state_change,webhook,trigger,priv,process_template— return"bad object"forlist(may be accessible via other ops)group— requireslist_objparamnode— requires "object reference" format
Super-user vs company-admin distinction: Even with
is_super_user: true, operations against foreign companies
returned "User has no rights". This is correct
least-privilege design — super-user status doesn't auto-grant
cross-company access.
openapi.corezoid.com
Static Redoc-rendered OpenAPI spec renderer. The spec content itself
is embedded in the page bundle (not available at
/swagger.json — which returns a SPA catch-all 200 with
index.html). Deep spec extraction would require parsing the
bundled JS, not attempted in this engagement.
SPA catch-all routing observation
Multiple SPA-backed hosts return 200 OK with
index.html for every path (including /.env,
/.git/config, /actuator/env,
/swagger.json, /.DS_Store). This is NOT a
leaked file — it's the SPA fallback routing. But it's a minor security
misconfig:
- Defeats naive content-discovery tools
- Causes search engines to index
/admin/.envas "admin page" - Breaks WAF rules that trigger on 404 for
/.env
Should return 404 for clearly-invalid paths (static files like
.env, .git/*) while still serving SPA for
legitimate routes.
mw.simulator.company
Provided HAR contained only a single Google Analytics call. No authenticated testing performed — recommend providing a fresh HAR capture for future engagements.
Vulnerability scanning — Phase 5
Nuclei conservative scan against 27 live hosts completed with zero medium+ findings. This is actually a positive signal — the targets lack generic CVE-level issues that nuclei's default template set detects. Issues on this infrastructure are concentrated in:
- Version EOL / missing patches (Jira, OpenSSH — detected manually in Phase 2/8)
- Configuration / architecture (cookie SameSite, Kubernetes public API, TLS cipher — detected manually in Phase 3/8)
- Secret hygiene (source code audit — Phase 6)
Targeted CVE checks already performed:
- Jira CVE-2019-11581 / CVE-2020-36239 / CVE-2022-0540 → CRZ-006
- OpenSSH regreSSHion CVE-2024-6387 → CRZ-007
- Kubernetes API exposure → CRZ-002
Also performed (added post-initial report):
- TLS cipher audit on 11 primary hosts (nmap
ssl-enum-ciphers) → CRZ-011 (VPN outlier with TLSv1.1 + 3DES) - Subdomain takeover probe on CloudFront and AWS-named DNS targets → no takeovers found
- S3 bucket takeover probe on 9 predicted bucket
names (
corezoid*,simulator*) → all 404 / 403, no takeover risk - API signature scheme review via OpenAPI spec → CRZ-012 (SHA-1 non-HMAC)
/api/2/jsonop enumeration (list, get, create, update, delete, copy, clone, …) → differential-error info disclosure but authz is correctly enforced
Source code audit — Phase 6
10 repos cloned into repos-src/. See repos/source-audit-summary.md
for full writeup.
Repository visibility
| Visibility | Repos |
|---|---|
| PUBLIC | helm, apigw, dbcall,
gitcall, account,
corezoid_ansible_roles, corezoid-ai-doc |
| Private | terraform, observability,
gitcall-livekit-agent |
Observation: The "service" repos on GitHub
(account, apigw, dbcall,
gitcall) are actually Helm-chart-only
repos. Actual service source code is elsewhere (likely
git.corezoid.com). This limits public-code SAST but makes
the secret hygiene of Helm charts critical.
Secret scanning results (trufflehog + gitleaks)
| Repo | Filesystem findings | Git-history findings | Outcome |
|---|---|---|---|
helm (public) |
4 | 26 | CRZ-009 |
corezoid_ansible_roles (public) |
8 | 15 | CRZ-009 |
terraform (private) |
0 fs / 8 current | 8 | CRZ-009 (lower blast radius) |
corezoid-ai-doc (public) |
18 | — | Mostly docs/placeholders, manual review needed |
observability (private) |
1 | — | Low-value placeholder |
account, apigw, dbcall,
gitcall, gitcall-livekit-agent |
0 | — | Clean |
Key leaks (filed as CRZ-009):
AKIAYQAMCNBUQ3PY5FO3— AWS Access Key ID in publichelm/corezoid/values.yamladmin_bearer_token_secret: ungoh3mohM3valu6Zu1ohdiighie1EemoophaequohMoov— admin auth token- Multiple RSA private keys including
ingress-tls.key,rabbitmq/tls-cert.key,default-server-secret.yaml,corezoid-demo.pem.j2(reused across 3 Ansible roles) - Postgres admin password
NE4wzHnodH9sbTeEfNaxDx23scM0ZaLS76xBiSIBhqT7EL4M3e - Multiple other API tokens and service credentials
Live-key validation of TLS key:
- Fingerprinted committed
ingress-tls.keypublic key: SHA-256b0ea83a9... - Fingerprinted live
*.corezoid.comcert public key: SHA-256021605b2... - No match → the leaked key is not currently deployed (rotated or never used). Good.
Semgrep SAST
Semgrep scan with
p/owasp-top-ten + p/security-audit + p/secrets across 5
service-named repos yielded 2 findings total:
account/charts/account-frontend/templates/configmap.yaml:64—$hostin nginx access_log format. False positive — using$hostin logs is normal, not a routing decision.gitcall-livekit-agent/Dockerfile:20— missingUSERdirective; container runs as root by default. Real low-severity hardening rec (noted in audit summary, not separately filed).
IaC review — Phase 7
Checkov scan on repos-src/helm (Helm umbrella
chart):
- 1387 passing checks, 377 failing, across 110 k8s resources
- Failures cluster into 20 distinct check IDs
- Filed as CRZ-010 (Medium, systemic)
Top 10 most-common failures:
- 75×
CKV_K8S_21— default namespace used - 19×
CKV_K8S_40— low-UID containers - 19×
CKV_K8S_38— service-account token mounted unnecessarily - 19×
CKV_K8S_43— images pulled by tag, not digest - 18×
CKV_K8S_37— capabilities assigned to containers - 18×
CKV_K8S_31— seccomp not set - 18×
CKV_K8S_20—allowPrivilegeEscalation: true - 18×
CKV_K8S_22— writable root filesystem - 18×
CKV_K8S_28—NET_RAWcapability allowed - 17×
CKV_K8S_29— no pod-level securityContext
Remediation plan in action-plan.md P2.1.
Edge infrastructure — Phase 8
See notes/phase8-edge.md.
Summary:
jira.corezoid.com— CRZ-006 Criticalcorezoid-ma.dev.corezoid.com:22— CRZ-007 Hightrack.pre.corezoid.com— CRZ-002 Highadmin-pre.corezoid.com— CRZ-001 Lowvpn.corezoid.com— CRZ-005 Infogitlab-mambu.corezoid.com,git.corezoid.com,confluence-ferma.corezoid.com— unresponsive / offline from testing location; recommend internal audit
Findings catalog
See findings/INDEX.md
for the master list with links to full writeups.
| ID | Severity | Title | Asset |
|---|---|---|---|
| CRZ-006 | 🔴 Critical | Jira Server 7.12.3 (2018 EOL) — multiple unauth RCE CVEs | jira.corezoid.com |
| CRZ-009 | 🔴 High (Critical-adjacent) | 41+ secrets in public GitHub repos | corezoid/helm, corezoid_ansible_roles |
| CRZ-002 | 🟠 High | Public Kubernetes API server (EKS pre-prod) | track.pre.corezoid.com |
| CRZ-007 | 🟠 High (conditional) | OpenSSH 8.7 public, vulnerable to regreSSHion | corezoid-ma.dev.corezoid.com:22 |
| CRZ-008 | 🟡 Medium | Auth cookie without SameSite; GET endpoints accept mw
alone |
admin.corezoid.com |
| CRZ-010 | 🟡 Medium | Systemic Kubernetes hardening gaps (377 checkov failures) | corezoid/helm charts |
| CRZ-011 | 🟡 Medium | Weak TLS (TLSv1.1, 3DES Sweet32, non-PFS ciphers) | vpn.corezoid.com |
| CRZ-012 | 🟡 Medium | Public API uses SHA-1 (non-HMAC) for request signing | api.corezoid.com + all /api/2/* |
| CRZ-015 | 🟡 Medium | Widget shim postMessage accepts messages via user-controlled appName (origin check bypass) | widget.simulator.company/shim.js |
| CRZ-013 | 🔵 Low-Med | Workflow CRUD destructive ops w/o confirmation/audit/MFA | admin.corezoid.com/api/2/json |
| CRZ-003 | 🔵 Low | Default nginx welcome on production ALB | widget.simulator.company |
| CRZ-001 | 🔵 Low | RFC1918 IPs in public DNS | admin-pre.corezoid.com |
| CRZ-004 | ⚪ Info | Prod docs as publicly-shareable Google Doc | doc.corezoid.com |
| CRZ-005 | ⚪ Info | OpenVPN-AS version disclosure | vpn.corezoid.com |
| CRZ-014 | ⚪ Info | Super-user grants cross-tenant workspace CRUD (authz granularity) | admin.corezoid.com/api/2/json |
Defenses confirmed working
Things that were tested and are correctly configured — mentioned so they aren't lost in remediation noise:
- CORS / SOP on admin API — cross-origin requests from malicious sites blocked by browser SOP; preflight returns 405
- IDOR defense on
/api/2/json— consistent error for valid-but-foreign and invalid IDs; no enumeration leak - Super-user != company-admin —
is_super_user: truedoes NOT bypass company-level authorization (least-privilege design) - HSTS with
includeSubDomainson all primary hosts - CSP defined on admin.corezoid.com with specific
allow-lists (has
'unsafe-inline' 'unsafe-eval'— real but lower-priority hardening) - x-frame-options: SAMEORIGIN,
x-content-type-options: nosniff,x-xss-protection: 1; mode=block - HTTPS redirect + TLS 1.3 on all primary hosts
- Jira ContactAdmin form disabled — mitigates the most dangerous CVE-2019-11581 anonymous vector
- Jira Ehcache RMI ports filtered — mitigates CVE-2020-36239
- Jira issue/project data anonymous-access-denied — anonymous users see 0 projects/issues
- OpenVPN-AS admin port 8888 closed — no public admin interface
- ACM wildcard TLS certs on primary hosts (rotation-friendly)
admin-oleg.dev.corezoid.com— all 1000 top ports filtered (not publicly reachable despite the DNS entry)- Stripe, reCAPTCHA keys in /system/conf are public-by-design — not credential leaks despite looking like them
- Session cookies have
HttpOnly; Securecorrectly set (preventing JS access, HTTPS-only transmission) superadmin.corezoid.comdoesn't exist (NXDOMAIN) — no super-admin surface accessible at all- Content-Security-Policy
frame-ancestors 'self'— prevents clickjacking permissions-policy: geolocation=(), camera=()— correctly restricts sensitive browser APIs- EKS private subnet ALBs — the
internal-*ELB pattern shows correct network segregation even if DNS leaks internal IPs - Checkov: 1387 passing checks — substantial baseline hardening exists; the 377 failures are gaps, not complete absence
Out-of-scope observations
Things noticed but NOT pursued (not in scope, or RoE limits):
admin.control.eventsredirects tosim.simulator.company— cross-brand redirect suggests historical migration; out of scopecontrol.eventsdomain — third-party, not part of this engagementwidget.sender.mobi— third-party, referenced in admin CSPbuilder.sender.mobi— same- Named customer subdomains (
gitlab-mambu,confluence-ferma,4ages.sandbox) — strongly recommend separate per-customer pentests if these are customer-dedicated instances; multi-tenancy of customer-named subdomains is not assessed here
Next steps
- Remediate P0 findings within 24 hours (CRZ-006, CRZ-009)
- Complete P1 within 7 days (CRZ-002, CRZ-007, CRZ-008)
- Re-test specific fixes — happy to run a focused re-scan after remediation to confirm
- Commission a deeper follow-up engagement with
internal source-code access (
git.corezoid.com) and fresh authenticated HAR captures for simulator.company — the current engagement was limited by what was visible externally + in public GitHub repos