XenForo 2.3.12 build 2031270 accepted empty OAuth2 credentials at the token endpoint. An empty PKCE verifier allowed an authorization code to be redeemed without proving possession of the original verifier. Empty client secrets affected authorization-code and refresh-token grants through the same code pattern.
Technical details
The token endpoint called assertRequiredApiInput(), which checked that the parameter existed but did not require a non-empty value. Credential verification was then guarded by truthiness checks:
if ($input['client_secret']) { /* compare it */ }
if ($input['code_verifier']) { /* verify PKCE */ }
PHP evaluates both '' and '0' as false, so either value bypassed the comparison block.
Reproduction
- Complete a normal S256 authorization flow for a public OAuth2 client and capture the authorization code.
- Exchange the code at the token endpoint with
code_verifier= instead of the verifier used to create the challenge.
- Use the returned access token to request
/api/me.
The server returned access and refresh tokens, and /api/me accepted the access token. Supplying an incorrect non-empty verifier caused the exchange to fail.
Impact
Anyone who obtained a live authorization code for a public client could redeem it without the PKCE verifier. The issued tokens retained the user-approved scopes. Exploitation still required a valid authorization code.
XenForo 2.3.13 rejects empty credentials before processing the grant.
References
XenForo 2.3.12 build 2031270 accepted empty OAuth2 credentials at the token endpoint. An empty PKCE verifier allowed an authorization code to be redeemed without proving possession of the original verifier. Empty client secrets affected authorization-code and refresh-token grants through the same code pattern.
## Technical details
The token endpoint called `assertRequiredApiInput()`, which checked that the parameter existed but did not require a non-empty value. Credential verification was then guarded by truthiness checks:
```php
if ($input['client_secret']) { /* compare it */ }
if ($input['code_verifier']) { /* verify PKCE */ }
```
PHP evaluates both `''` and `'0'` as false, so either value bypassed the comparison block.
## Reproduction
1. Complete a normal S256 authorization flow for a public OAuth2 client and capture the authorization code.
2. Exchange the code at the token endpoint with `code_verifier=` instead of the verifier used to create the challenge.
3. Use the returned access token to request `/api/me`.
The server returned access and refresh tokens, and `/api/me` accepted the access token. Supplying an incorrect non-empty verifier caused the exchange to fail.
## Impact
Anyone who obtained a live authorization code for a public client could redeem it without the PKCE verifier. The issued tokens retained the user-approved scopes. Exploitation still required a valid authorization code.
XenForo 2.3.13 rejects empty credentials before processing the grant.
## References
- [CVE-2026-73309](https://www.cve.org/CVERecord?id=CVE-2026-73309)
- [PoC](https://github.com/BomboBombone/CVE-2026-73309)
- [VulnCheck advisory](https://www.vulncheck.com/advisories/xenforo-authentication-bypass-via-oauth2-token-endpoint)
- [XenForo 2.3.13 release](https://xenforo.com/community/threads/xenforo-2-3-13-and-add-ons-released-includes-security-fixes.239857/)