PatchSiren

PatchSiren cyber security CVE debrief

CVE-2026-7820 pgadmin.org CVE debrief

pgAdmin 4 versions prior to 9.15 contain an authentication bypass vulnerability in the account lockout mechanism. The application enforces MAX_LOGIN_ATTEMPTS only within its custom /authenticate/login view, while Flask-Security's default /login view—automatically registered and reachable on every server—fails to consult the User.locked field. This occurs because pgAdmin's User model relied on Flask-Security's UserMixin.is_locked() (which always returns 'not locked') and Flask-Login's is_active (which only checks the active column, not locked). An attacker who triggers an account lockout via /authenticate/login can obtain a session by re-submitting valid credentials directly to /login, defeating brute-force protection for INTERNAL authentication source accounts. Additionally, login attempts via /login are never rate-limited, enabling unbounded online password-guessing attacks against INTERNAL accounts regardless of MAX_LOGIN_ATTEMPTS. The fix overrides User.is_active and User.is_locked() to ensure the locked column is enforced on every authentication path. LDAP, OAuth2, Kerberos, and Webserver users are not affected by this bypass as they have no local password and are rejected by Flask-Security's LoginForm.validate before the locked check; the lockout itself is also internal-only.

Vendor
pgadmin.org
Product
pgAdmin 4
CVSS
MEDIUM 6.9
CISA KEV
Not listed in stored evidence
Original CVE published
2026-05-11
Original CVE updated
2026-05-26
Advisory published
2026-05-11
Advisory updated
2026-05-26

Who should care

Organizations running pgAdmin 4 with INTERNAL authentication enabled; security teams responsible for database administration tool hardening; compliance officers monitoring brute-force protection controls

Technical summary

The vulnerability stems from inconsistent enforcement of account lockout state across authentication endpoints in pgAdmin 4. The custom /authenticate/login view implements MAX_LOGIN_ATTEMPTS and sets User.locked, but Flask-Security's default /login view—registered via security.init_app()—bypasses this check because User.is_locked() inherited from UserMixin always returns False and User.is_active only checks the active column. The fix modifies the User model to override both methods, ensuring locked status is evaluated regardless of which endpoint processes the authentication request. This is a logic flaw in the integration between pgAdmin's custom authentication layer and Flask-Security's default behavior, not a vulnerability in Flask-Security itself.

Defensive priority

HIGH

Recommended defensive actions

  • Upgrade pgAdmin 4 to version 9.15 or later to obtain the fix that enforces account lockout on all authentication paths
  • If immediate patching is not possible, restrict network access to pgAdmin 4 administrative interfaces to trusted source IP addresses
  • Monitor authentication logs for anomalous patterns including rapid sequential login attempts followed by successful authentication from the same source
  • Review user accounts configured with INTERNAL authentication source for unauthorized access indicators
  • Consider implementing additional network-layer rate limiting on pgAdmin 4 endpoints as a compensating control
  • Verify that external authentication sources (LDAP, OAuth2, Kerberos, Webserver) remain properly configured and are not inadvertently fallback options

Evidence notes

Vulnerability description and fix details derived from NVD record published 2026-05-11 and modified 2026-05-26. Affected versions confirmed as pgAdmin 4 before 9.15 per CPE criteria. Vendor advisory and patch available via GitHub issue #9904.

Official resources

2026-05-11