# REPRO-2026-00219: AutoBangumi before 3.2.8 seeds a default admin account on empty databases, allowing unauthenticated users to log in with publicly known default credentials and gain full control. ## Summary Status: published Severity: critical Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00219 CVE: CVE-2026-58466 ## Package Name: EstrellaXD/Auto_Bangumi Ecosystem: standalone application (Python/FastAPI) Affected: < 3.2.8 Fixed: 3.2.8 ## Root Cause # Root Cause Analysis — CVE-2026-58466 ## Summary AutoBangumi (a FastAPI-based bangumi/auto-download manager) seeds a hard-coded default administrator account — `admin` / `adminadmin` — whenever its users database table is empty. The seeding happens unconditionally on startup via `add_default_user()` in `backend/src/module/database/user.py`. Because the credentials are hard-coded and publicly visible in the source, an unauthenticated remote attacker can submit them to the real authentication endpoint (`POST /api/v1/auth/login`) on a freshly deployed instance and receive a valid administrator JWT, gaining full administrative control of the application (RSS feed configuration, downloader configuration, server logs, and every authenticated API endpoint). ## Impact - **Package/component affected:** AutoBangumi backend — `module/database/user.py` (`UserDatabase.add_default_user`), invoked from `module/update/startup.py` (`first_run`/`start_up`) during `module/core/program.py` startup. - **Affected versions:** AutoBangumi **< 3.2.8** (reproduced on the official Docker image `ghcr.io/estrellaxd/auto_bangumi:3.2.6`). - **Risk level:** Critical (CVSS v3 max 9.8 / v4 9.3). CWE-1392 Use of Default Credentials. Remote, unauthenticated, low complexity, full confidentiality/integrity/availability impact on the application. - **Consequences:** Complete administrative takeover of a fresh AutoBangumi instance — an attacker can read/change RSS feeds, reconfigure the downloader (including credentials), read server logs, and exercise all authenticated API endpoints. ## Impact Parity - **Disclosed/claimed maximum impact:** Unauthenticated attacker authenticates as administrator using publicly known default credentials and gains full administrative access (authz bypass / default credentials). - **Reproduced impact from this run:** Full parity. Against the real AutoBangumi 3.2.6 product (official Docker image, fresh empty database) the default credentials `admin`/`adminadmin` were submitted to `POST /api/v1/auth/login` and returned **HTTP 200 + an admin JWT** (`sub: admin`). That JWT, sent as the `token` session cookie, granted access to admin-only endpoints (`/api/v1/rss` returned the RSS config list, `/api/v1/log` returned the server log stream) — proving full administrative access via the remote API. - **Parity:** `full` - **Not demonstrated:** N/A — the claimed impact (default-credential authentication bypass → admin access) was reproduced end-to-end through the real remote API surface. ## Root Cause `UserDatabase.add_default_user()` queries the `users` table; if it is empty it inserts a single user with hard-coded credentials: ```python # backend/src/module/database/user.py def add_default_user(self): statement = select(User) ... if len(users) != 0: return user = User(username="admin", password=get_password_hash("adminadmin")) self.session.add(user) self.session.commit() logger.info("[Database] Created default admin user") ``` This method is called on every startup from `module/update/startup.py` (`start_up` and `first_run`), which are invoked by `module/core/program.py` during the FastAPI lifespan startup. On a fresh deployment (no persisted database) the table is empty, so the default `admin`/`adminadmin` account is created. The login endpoint (`module/api/auth.py`, `POST /api/v1/auth/login`) validates the submitted credentials against the database via `UserDatabase.auth_user`, which uses `verify_password`. Because the seeded password hash matches `adminadmin`, the default credentials authenticate successfully and a JWT (`sub: admin`) is issued. The session is registered in the in-memory `active_user` map, and the `get_current_user` dependency accepts the `token` cookie for all protected endpoints. There is no forced password change, no setup-completion gate on the login endpoint, and no randomization of the default password, so the publicly known credentials remain valid until an administrator manually changes them through the (unauthenticated-only-before-setup) setup wizard. **Fix commit referenced by the advisory:** `487bdfec545e805ae416e6ddf28651bd274d6a73` ("fix(api): harden pre-auth setup endpoints (#1041, #1044)"). Note: inspection of that commit shows it hardens the **SSRF** behavior of the pre-auth `/setup/test-*` endpoints and qBittorrent 5.2 login compatibility — it does **not** remove or randomize the default `admin`/`adminadmin` account. Correspondingly, the negative control below shows the default credentials remain exploitable in 3.2.8. ## Reproduction Steps 1. The self-contained script is **`bundle/repro/reproduction_steps.sh`**. 2. What it does: - Pulls the official AutoBangumi Docker images `ghcr.io/estrellaxd/auto_bangumi:3.2.6` (vulnerable) and `:3.2.8` (claimed patch). - For the vulnerable version, starts **two clean instances** each with a fresh empty Docker volume (so the users table is empty and `add_default_user()` triggers), waits for `Application startup complete`, and captures the startup log (which records `[Database] Created default admin user`). - Drives the **real remote API** from inside each container (the Docker bridge is not routable from the sandbox host, so HTTP probes are executed via `docker exec` against `127.0.0.1:7892`): `POST /api/v1/auth/login` with form `username=admin&password=adminadmin`. - On a 200 + JWT, reuses the `token` session cookie to call the admin-only endpoints `GET /api/v1/rss` and `GET /api/v1/log`. - Runs the same flow against `:3.2.8` as a negative control (two attempts). - Writes all HTTP request/response artifacts to `bundle/artifacts/http/`, startup logs to `bundle/logs/`, and the runtime manifest to `bundle/repro/runtime_manifest.json`. 3. Expected evidence of reproduction: - Startup log line `[Database] Created default admin user`. - Login response **HTTP 200** with `access_token` JWT and `set-cookie: token=; HttpOnly`. - JWT payload decodes to `{"sub":"admin", ...}`. - `GET /api/v1/rss` → **200** `[]` (authenticated RSS config). - `GET /api/v1/log` → **200** server log text (authenticated). ## Evidence - **Run log:** `bundle/logs/run.log` (full execution transcript, both runs). - **Startup logs:** `bundle/logs/vuln-1-startup.log`, `bundle/logs/vuln-2-startup.log`, `bundle/logs/fixed-1-startup.log`, `bundle/logs/fixed-2-startup.log`. - **HTTP artifacts:** `bundle/artifacts/http/vuln-{1,2}-login.json`, `vuln-{1,2}-rss.json`, `vuln-{1,2}-log.json`, `fixed-{1,2}-login.json`. - **Runtime manifest:** `bundle/repro/runtime_manifest.json`. Key excerpts (vulnerable 3.2.6, attempt 1): Startup seeding: ``` [Database] Schema version is now 9. [Database] Created default admin user [Core] No db file exists, create database file. Application startup complete. Uvicorn running on http://0.0.0.0:7892 ``` Login with default credentials (`bundle/artifacts/http/vuln-1-login.json`): ```json { "method": "POST", "path": "/api/v1/auth/login", "status": 200, "set_cookie": "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6...}; HttpOnly; Max-Age=86400; Path=/; SameSite=lax", "body": "{\"access_token\":\"eyJ...sub\":\"admin\"...\",\"token_type\":\"bearer\"}" } ``` The JWT payload decodes to `{"sub":"admin","exp":...}` — authenticated as the administrator. Admin-only endpoint access with the session cookie (`bundle/artifacts/http/vuln-1-rss.json`, `vuln-1-log.json`): ```json { "method": "GET", "path": "/api/v1/rss", "status": 200, "body": "[]" } { "method": "GET", "path": "/api/v1/log", "status": 200, "body": "[2026-07-03 ...] INFO ... Version 3.2.6 ..." } ``` **Environment:** Official Docker images on the host Docker daemon (client 29.1.3); AutoBangumi 3.2.6 backend (Python 3.13, uvicorn/FastAPI, SQLite) inside Alpine containers; webui port 7892; HTTP probes executed in-container via `docker exec python3`. **Negative control (3.2.8):** The 3.2.8 image **also** logs `[Database] Created default admin user` on a fresh database **and** accepts the `admin`/`adminadmin` login with **HTTP 200 + an admin JWT** in both attempts (`bundle/artifacts/http/fixed-{1,2}-login.json`). This indicates the advisory-referenced fix commit (`487bdfec`) addresses the related SSRF issue (#1041) and does not remediate the hard-coded default-credentials seeding; the `add_default_user()` source is byte-identical from 3.2.6 through HEAD. ## Recommendations / Next Steps - **Primary fix:** Do not seed a hard-coded default administrator. Either (a) require the initial setup wizard to create the first admin account with a user-chosen password before any login is possible, or (b) generate a random one-time bootstrap password and display it once in the startup log / a bootstrap file, never reusing a public constant. - **Defense in depth:** Gate `POST /api/v1/auth/login` behind setup completion (the existing `.setup_complete` sentinel) so login is rejected until the operator has finished initial configuration. - **Upgrade guidance:** Operators should immediately change the admin password on any deployed instance and avoid exposing port 7892 to untrusted networks. Treat 3.2.8 as still affected for this specific CVE until a corrected fix that removes the default seeding is released. - **Testing:** Add a regression test asserting that a fresh empty database never contains a login-capable account with a known password, and that `POST /api/v1/auth/login` fails before setup completion. ## Additional Notes - **Idempotency:** The script removes its containers/volumes on entry and exit (`trap cleanup EXIT`) and was executed twice consecutively; both runs exited `0` with identical positive evidence (`VULN_LOGIN_OK=1`, `VULN_ADMIN_ACCESS_OK=1`). - **Networking note:** In this sandbox the Docker bridge network is not routable from the host, so port-mapped requests from the host fail with "connection refused". The script therefore performs all HTTP probes from inside the container via `docker exec`, exercising the same real `127.0.0.1:7892` listener that uvicorn serves — this is the genuine application HTTP surface, not a mock. - **Scope note:** The claim surface (`api_remote`) was satisfied through the real authentication endpoint and real admin API endpoints of the running product; no sanitizers were used (`sanitizer_used=false`); the JWT is a real HS256 token signed by the running application. - **Limitation:** The default credentials were located in the source (`admin`/`adminadmin`) rather than supplied by the NVD entry, as the ticket noted the NVD entry omits them; they are confirmed correct by the runtime login success. ## Reproduction Details Reproduced: 2026-07-03T15:53:50.215Z Duration: 896 seconds Tool calls: 128 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00219 pruva-verify CVE-2026-58466 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00219&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00219/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-58466 - Source: https://nvd.nist.gov/vuln/detail/CVE-2026-58466 ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 15207 bytes) - bundle/repro/rca_report.md (analysis, 10828 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 19907 bytes) - bundle/vuln_variant/rca_report.md (analysis, 16277 bytes) - bundle/ticket.md (ticket, 2126 bytes) - bundle/ticket.json (other, 3105 bytes) - bundle/logs/pull-vuln.log (log, 232 bytes) - bundle/logs/pull-fixed.log (log, 232 bytes) - bundle/logs/run.log (log, 16762 bytes) - bundle/logs/vuln-1-startup.log (log, 2007 bytes) - bundle/logs/vuln-2-startup.log (log, 2007 bytes) - bundle/logs/fixed-1-startup.log (log, 2007 bytes) - bundle/logs/fixed-2-startup.log (log, 2007 bytes) - bundle/repro/ab_probe.py (script, 1489 bytes) - bundle/repro/runtime_manifest.json (other, 1360 bytes) - bundle/repro/validation_verdict.json (other, 923 bytes) - bundle/logs/vuln_variant/run.log (log, 2760 bytes) - bundle/logs/vuln_variant/pull-auto_bangumi:3.2.6.log (log, 232 bytes) - bundle/logs/vuln_variant/pull-auto_bangumi:latest.log (log, 235 bytes) - bundle/logs/vuln_variant/pull-auto_bangumi:3.3.0-beta.2.log (log, 253 bytes) - bundle/logs/vuln_variant/vuln-startup.log (log, 2007 bytes) - bundle/logs/vuln_variant/latest-startup.log (log, 2007 bytes) - bundle/logs/vuln_variant/beta-startup.log (log, 2106 bytes) - bundle/logs/vuln_variant/setup-startup.log (log, 2007 bytes) - bundle/logs/vuln_variant/fixed_version.txt (other, 345 bytes) - bundle/logs/vuln_variant/latest_version.txt (other, 345 bytes) - bundle/vuln_variant/ab_probe2.py (script, 1725 bytes) - bundle/vuln_variant/artifacts/vuln-noauth-rss.json (other, 151 bytes) - bundle/vuln_variant/artifacts/vuln-login.json (other, 478 bytes) - bundle/vuln_variant/artifacts/vuln-rss.json (other, 124 bytes) - bundle/vuln_variant/artifacts/latest-noauth-rss.json (other, 151 bytes) - bundle/vuln_variant/artifacts/latest-login.json (other, 478 bytes) - bundle/vuln_variant/artifacts/latest-rss.json (other, 124 bytes) - bundle/vuln_variant/artifacts/beta-noauth-rss.json (other, 151 bytes) - bundle/vuln_variant/artifacts/beta-login.json (other, 481 bytes) - bundle/vuln_variant/artifacts/beta-rss.json (other, 124 bytes) - bundle/vuln_variant/artifacts/setup-status.json (other, 174 bytes) - bundle/vuln_variant/artifacts/setup-complete.json (other, 276 bytes) - bundle/vuln_variant/artifacts/setup-login.json (other, 478 bytes) - bundle/vuln_variant/artifacts/setup-rss.json (other, 124 bytes) - bundle/vuln_variant/artifacts/setup-oldlogin.json (other, 207 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 2846 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 6827 bytes) - bundle/vuln_variant/variant_manifest.json (other, 5015 bytes) - bundle/vuln_variant/validation_verdict.json (other, 4975 bytes) - bundle/vuln_variant/source_identity.json (other, 2335 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 3808 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00219 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00219/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00219 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev