Hi Thorsten,
I'm reporting an account-takeover issue in phpMyFAQ's user tracking feature. A public tracking file records the full query string for every visited frontend page. Password reset links put the reset signature in the query string, so when any user (including admins) opens their own reset link, any unauthenticated user can read the link from /content/core/data/trackingDDMMYYYY and use it to set a password.
Overview
The installer enables main.enableUserTracking by default. When tracking is enabled, UserSession::userTracking() appends request metadata to content/core/data/trackingDDMMYYYY. The file is inside the web root and the default Apache rules only block ZIP files under content/, so unauthenticated HTTP clients can download it.
Password reset links are shaped like:
/index.php?action=resetpw&u=<user_id>&exp=<expires>&sig=<hmac>
Opening that link causes the frontend to log the raw query string. The reset token is not consumed by viewing the reset form; it remains valid until expiry or password change. An attacker who polls the public tracking file can replay the leaked u, exp, and sig against the password reset API and take over the account.
Who Can Trigger It
An unauthenticated attacker can exploit this if:
- User tracking is enabled (
main.enableUserTracking=true; installer default).
- The attacker can get or wait for the target user to open a valid password reset link.
- The attacker knows or can guess the target username and email to request the reset email, or otherwise the target initiates password reset themselves.
Root Cause
Admin opens reset URL with sig in query string
|
index.php frontend request
|
UserSession::userTracking()
|
writes raw QUERY_STRING to content/core/data/trackingDDMMYYYY
|
public unauthenticated GET reads tracking file
|
POST /api/index.php/user/password/reset changes victim password
Relevant code:
src/phpMyFAQ/User/UserSession.php writes $request->server->get('QUERY_STRING') into content/core/data/trackingDDMMYYYY.
src/phpMyFAQ/Controller/Frontend/UnauthorizedUserController.php builds reset links with u, exp, and sig in the URL and accepts them in resetPassword().
.htaccess does not deny non-ZIP files under content/core/data/.
Test Environment
| Component |
Version |
| OS |
Windows 11 |
| phpMyFAQ |
v4.1.6 |
| Web Server |
Apache 2.4 + PHP 8.3 (Docker) |
| Database |
MariaDB 11 |
| Lab URL |
http://127.0.0.1:8081 (docker-compose.yml maps 8081:80) |
| Mail capture |
Mailpit — web UI http://127.0.0.1:8025, SMTP mailpit:1025 inside Compose |
Steps to Reproduce
- Ensure that the “Enable user tracking” option is enabled.
- Our target user for this demo is as follows:
Username | kevin-admin
Password | LabPass123!
Email | kevin-admin@example.com
- Browse to http://127.0.0.1:8081/forgot-password and reset the target user’s password via the “Forgot password” function.
- Verify that the email receives the password reset link.
- Simulate the admin clicking on the reset link but do not proceed to submit the new password first. (The token is still unused.)
- Without authentication, browse to the following URL
GET /content/core/data/tracking<DDMMYYYY> , replacing today’s date to view the logs which contains the password reset URL of the target user.
- With the pwd reset URL details above, an attacker can simply append it to “/index.php” to access the password reset page to reset the victim’s password.
http://127.0.0.1:8081/index.php?action=resetpw&u=1&exp=1785766994&sig=308f52b5546849fae74be8ab15164c8c3645b849b825e03b595f67fc7fbf2ac6
Impact
- Unauthenticated super-admin account takeover after a viewed reset link is logged.
- The public tracking file can also leak other sensitive URL query material because it stores raw query strings and referers.
Credits
You may contact me at kevinpook7991@gmail.com if you have any questions or require further information regarding this report. If the vulnerability is confirmed and a CVE is assigned, I would appreciate being credited as the reporter. Thank you!
Hi Thorsten,
I'm reporting an account-takeover issue in phpMyFAQ's user tracking feature. A public tracking file records the full query string for every visited frontend page. Password reset links put the reset signature in the query string, so when any user (including admins) opens their own reset link, any unauthenticated user can read the link from
/content/core/data/trackingDDMMYYYYand use it to set a password.Overview
The installer enables
main.enableUserTrackingby default. When tracking is enabled,UserSession::userTracking()appends request metadata tocontent/core/data/trackingDDMMYYYY. The file is inside the web root and the default Apache rules only block ZIP files undercontent/, so unauthenticated HTTP clients can download it.Password reset links are shaped like:
Opening that link causes the frontend to log the raw query string. The reset token is not consumed by viewing the reset form; it remains valid until expiry or password change. An attacker who polls the public tracking file can replay the leaked
u,exp, andsigagainst the password reset API and take over the account.Who Can Trigger It
An unauthenticated attacker can exploit this if:
main.enableUserTracking=true; installer default).Root Cause
Relevant code:
src/phpMyFAQ/User/UserSession.phpwrites$request->server->get('QUERY_STRING')intocontent/core/data/trackingDDMMYYYY.src/phpMyFAQ/Controller/Frontend/UnauthorizedUserController.phpbuilds reset links withu,exp, andsigin the URL and accepts them inresetPassword()..htaccessdoes not deny non-ZIP files undercontent/core/data/.Test Environment
http://127.0.0.1:8081(docker-compose.ymlmaps8081:80)http://127.0.0.1:8025, SMTPmailpit:1025inside ComposeSteps to Reproduce
GET /content/core/data/tracking<DDMMYYYY>, replacing today’s date to view the logs which contains the password reset URL of the target user.Impact
Credits
You may contact me at kevinpook7991@gmail.com if you have any questions or require further information regarding this report. If the vulnerability is confirmed and a CVE is assigned, I would appreciate being credited as the reporter. Thank you!