reported via email on 3 June 2026 - no response:
I am reporting a Cross-Site Request Forgery vulnerability in phpList 3.6.16 and earlier. An
attacker can delete any bounce rule by tricking an authenticated administrator into loading a
specially crafted URL, for example embedded in an image tag on an external page.
Affected files: public_html/lists/admin/bouncerules.php, bouncerule.php
Affected versions: all versions through 3.6.16
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L (5.4 Medium)
CWE-352
The deletion is performed by a GET request (?page=bouncerules&del=N) with no CSRF token check.
phpList's central CSRF check (verifyCsrfGetToken) uses enforce=false, meaning it only validates
the token when a "tk" parameter is actually present. Since a cross-origin image load does not
include the "tk" parameter, the check is skipped entirely.
I confirmed this by:
- Creating a bounce rule in phpList 3.6.15 (Docker install).
- Sending the GET deletion request with the admin session but without the "tk" parameter.
- Confirming the rule was deleted from phplist_bounceregex.
The fix is to call verifyCsrfGetToken() with enforcement (default enforce=1) in both
bouncerules.php and bouncerule.php before processing the "del" parameter, or to convert
deletions to POST-only actions protected by verifyToken().
This is similar to how bouncerule.php already handles its GET-based delete at line 15 but
without any token check.
reported via email on 3 June 2026 - no response:
I am reporting a Cross-Site Request Forgery vulnerability in phpList 3.6.16 and earlier. An
attacker can delete any bounce rule by tricking an authenticated administrator into loading a
specially crafted URL, for example embedded in an image tag on an external page.
Affected files: public_html/lists/admin/bouncerules.php, bouncerule.php
Affected versions: all versions through 3.6.16
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L (5.4 Medium)
CWE-352
The deletion is performed by a GET request (?page=bouncerules&del=N) with no CSRF token check.
phpList's central CSRF check (verifyCsrfGetToken) uses enforce=false, meaning it only validates
the token when a "tk" parameter is actually present. Since a cross-origin image load does not
include the "tk" parameter, the check is skipped entirely.
I confirmed this by:
The fix is to call verifyCsrfGetToken() with enforcement (default enforce=1) in both
bouncerules.php and bouncerule.php before processing the "del" parameter, or to convert
deletions to POST-only actions protected by verifyToken().
This is similar to how bouncerule.php already handles its GET-based delete at line 15 but
without any token check.