## Ticket: CVE-2026-46364 — phpMyFAQ unauthenticated SQL injection via User-Agent header

**Advisory**: GHSA-289f-fq7w-6q2w — https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-289f-fq7w-6q2w
**CVE**: CVE-2026-46364 | **CWE-89** (SQL Injection)
**Severity**: Critical — CVSS 3.1 base 9.8
**Package**: `thorsten/phpmyfaq` (Packagist / composer) | **Repository**: https://github.com/thorsten/phpMyFAQ

### Impact

`phpMyFAQ` is a PHP FAQ / knowledge-base application. Its `BuiltinCaptcha`
class builds SQL by **interpolating the unsanitized `User-Agent` HTTP header**
directly into queries:

- `BuiltinCaptcha::garbageCollector()` — into a `DELETE` query
- `BuiltinCaptcha::saveCaptcha()` — into an `INSERT` query

The **public, unauthenticated** endpoint `GET /api/captcha` reaches this code
path. An attacker can therefore perform **unauthenticated time-based blind SQL
injection** simply by sending a request with a crafted `User-Agent` header — no
login, no token, no user interaction required.

### Affected / fixed versions

Affected: `phpMyFAQ` `< 4.1.2`.
Fixed: **`4.1.2`**, in commit
[`b9f25109fddb38eee19987183798638d07943f92`](https://github.com/thorsten/phpMyFAQ/commit/b9f25109fddb38eee19987183798638d07943f92).

Reproduce on a vulnerable build (**`4.1.1`**) and verify the fix on
**`4.1.2`**.

### Where to look

```bash
git clone https://github.com/thorsten/phpMyFAQ.git
cd phpMyFAQ && git show b9f25109fddb38eee19987183798638d07943f92
```

The fix commit changes `BuiltinCaptcha::garbageCollector()` /
`BuiltinCaptcha::saveCaptcha()` so the `User-Agent` value is parameterized /
escaped instead of being concatenated into the `DELETE` / `INSERT` SQL.

### Reproduction approach

The bug is observable over HTTP — no authentication is needed. The signal is
**response timing**: a `SLEEP`-based payload in the `User-Agent` header delays
the response only when the injection executes.

1. Deploy **vulnerable** `phpMyFAQ 4.1.1` (PHP + a supported database, e.g.
   SQLite or MySQL; complete the installer so the captcha table exists).
2. Send `GET /api/captcha` twice:
   - with a **benign** `User-Agent` — baseline timing,
   - with a **time-based payload** `User-Agent`, e.g. `' OR SLEEP(3)-- -`.
3. Deploy **fixed** `phpMyFAQ 4.1.2` and repeat both requests.

```bash
curl -s -o /dev/null -w '%{time_total}\n' \
  -H 'User-Agent: benign' 'http://localhost:<port>/api/captcha'
curl -s -o /dev/null -w '%{time_total}\n' \
  -H "User-Agent: ' OR SLEEP(3)-- -" 'http://localhost:<port>/api/captcha'
```

> Note: `SLEEP()` is MySQL/MariaDB syntax. If the instance uses SQLite,
> substitute an equivalent time-based primitive so the timing delta is still
> observable.

### Expected result

| Build | `GET /api/captcha` with `User-Agent: ' OR SLEEP(3)-- -'` | Response time |
|-------|----------------------------------------------------------|---------------|
| `phpMyFAQ 4.1.1` (vulnerable) | `User-Agent` interpolated into SQL; `SLEEP(3)` runs in the DB | **~3 s delay** vs benign |
| `phpMyFAQ 4.1.2` (fixed) | `User-Agent` parameterized/sanitized before the SQL | **no measurable delay** |

- **Vulnerable indicator**: the payload request is ~3 s slower than the benign
  request on `4.1.1`.
- **Fixed indicator**: payload and benign requests take comparable time on
  `4.1.2` — no injected delay.

### Expected artifacts

- `reproduction_steps.sh` — deploys both phpMyFAQ versions with a database,
  issues the benign and payload requests, and records response timings.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable
  (~3 s delta) and fixed (no delta) indicators captured.
- Logs capturing the timed `curl` output for `4.1.1` and `4.1.2`.
