Authorized research notice: This vulnerability was discovered during authorized security research in an isolated lab environment against a local instance of phpMyFAQ (main @ 9435f34, 4.2.0-alpha.2; issues cross-checked against the 4.1 stable branch where applicable). No production systems or third-party data were accessed. I am reporting under coordinated disclosure and will not publish details for 90 days from this report to allow a fix. Please confirm receipt within 7 days.
Summary
The GDPR-style self-service export (POST /api/user/data/export) produces a ZIP whose userdata.json contains the account's live TOTP shared secret in plaintext. The export is a user-facing download that typically transits browsers, download folders, mailboxes and cloud backups — all of which then silently hold a working 2FA seed. Anyone obtaining the archive can generate valid one-time codes and defeat the account's second factor. Credential material must never appear in a data export; the password hash is correctly excluded, but the TOTP seed is not.
Credit: Ahmed Ibrahim (@skeletonsec)
Affected versions
- Verified on
main @ 9435f34 (4.2.0-alpha.2 nightly, phpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/UserController.php:176)
- Same defect verified on the
4.1 stable branch (phpmyfaq/src/phpMyFAQ/Controller/Frontend/UserController.php:161), affecting the latest stable release line.
Root cause
exportUserData() builds the export array including 'secret' => (string) $this->currentUser->getUserData('secret') — the raw TOTP seed from faquserdata — with no redaction or field allowlist.
Proof of concept
- Enable 2FA for a test account.
- Log in (full 2FA),
GET /user/ucp, take the export-userdata CSRF token.
curl -X POST https://target/api/user/data/export -b cookies -d 'pmf-csrf-token=T&userid=2' -o export.zip → 200 application/zip.
unzip -p export.zip userdata.json → contains "secret": "<LIVE-TOTP-SEED>" and "twofactor_enabled": 1. Codes derived from the exported seed were verified against the app's own TOTP library.
Impact
Persistent 2FA compromise for any user who has ever exported their data with 2FA enabled; the seed stays valid until 2FA is reset, long after the export is forgotten in a downloads folder or backup.
Suggested remediation
Remove secret from the export array and maintain an explicit allowlist of non-credential export fields (audit faquserdata columns such as secret and webauthnkeys against it).
CWE
CWE-200: Exposure of Sensitive Information
Summary
The GDPR-style self-service export (
POST /api/user/data/export) produces a ZIP whoseuserdata.jsoncontains the account's live TOTP shared secret in plaintext. The export is a user-facing download that typically transits browsers, download folders, mailboxes and cloud backups — all of which then silently hold a working 2FA seed. Anyone obtaining the archive can generate valid one-time codes and defeat the account's second factor. Credential material must never appear in a data export; the password hash is correctly excluded, but the TOTP seed is not.Credit: Ahmed Ibrahim (@skeletonsec)
Affected versions
main@9435f34(4.2.0-alpha.2 nightly,phpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/UserController.php:176)4.1stable branch (phpmyfaq/src/phpMyFAQ/Controller/Frontend/UserController.php:161), affecting the latest stable release line.Root cause
exportUserData()builds the export array including'secret' => (string) $this->currentUser->getUserData('secret')— the raw TOTP seed fromfaquserdata— with no redaction or field allowlist.Proof of concept
GET /user/ucp, take theexport-userdataCSRF token.curl -X POST https://target/api/user/data/export -b cookies -d 'pmf-csrf-token=T&userid=2' -o export.zip→ 200application/zip.unzip -p export.zip userdata.json→ contains"secret": "<LIVE-TOTP-SEED>"and"twofactor_enabled": 1. Codes derived from the exported seed were verified against the app's own TOTP library.Impact
Persistent 2FA compromise for any user who has ever exported their data with 2FA enabled; the seed stays valid until 2FA is reset, long after the export is forgotten in a downloads folder or backup.
Suggested remediation
Remove
secretfrom the export array and maintain an explicit allowlist of non-credential export fields (auditfaquserdatacolumns such assecretandwebauthnkeysagainst it).CWE
CWE-200: Exposure of Sensitive Information