Tutorial

PgBouncer 1.26.1: fix CVE-2026-107350 (SCRAM bypass)

Security & Monitoring12 min read14 steps

On October 14, 2026, PgBouncer 1.26.1 fixed a SCRAM authentication bypass, CVE-2026-107350, along with two denial-of-service flaws. The packages in Debian's stable releases were still listed as vulnerable when we checked, so a plain `apt upgrade` does not settle the matter. It happens that nobody can say which PgBouncer version runs on a server, or who can reach its port. This guide gives the commands to check the version in minutes, upgrade according to your install (PGDG repository, RHEL, Docker, source), apply workarounds in the meantime, then review the logs.

Contents· What PgBouncer 1.26.1 fixes1/13
  1. 01What PgBouncer 1.26.1 fixes
  2. 02Affected versions and package status
  3. 03"My PgBouncer is not exposed": an insufficient criterion
  4. 04What determines your real exposure
  5. 05Check the version and exposure in a few minutes
  6. 06Which upgrade route: the state of Debian packages
  7. 07Upgrade according to your installation method
  8. 08Restarting without cutting clients, and the search_path case
  9. 09Workarounds until the fix is in
  10. 10Review the traces of an impersonated connection
  11. 11Closing the file: from version to follow-up
  12. 12On a VPS of your own, you hold the three levers
  13. 13The official documentation

What PgBouncer 1.26.1 fixes

On October 14, 2026, the PgBouncer project released version 1.26.1, which fixes three flaws. The most serious, CVE-2026-107350, is an authentication bypass: a client that knows the password of one SCRAM user can log in as any other SCRAM user, including PgBouncer administrators and PostgreSQL superusers. The mechanism is a second startup message, the StartupMessage, processed during SCRAM authentication when the connection targets a non-existent database. According to Mend's description, the SCRAM exchange is still verified against the keys of the first user, while the session opens under the identity of the second. The changelog states that the attack requires SCRAM for both users.

The other two flaws are denial-of-service issues, triggerable without authentication but only under certain configuration options: CVE-2026-107349 and CVE-2026-107348. Mend gives CVE-2026-107350 a CVSS v4 score of 8.7. The score matters less than a practical question: which version runs on your machines, and who can reach port 6432?

Affected versions and package status

Scroll the table

CaseStatusAction
PgBouncer 1.17.0 to 1.26.0Affected by CVE-2026-107350 when both accounts use SCRAMUpgrade to 1.26.1
PgBouncer 1.20.0 to 1.26.0Affected by CVE-2026-107349 if `ignore_startup_parameters` is non-empty without `options`Upgrade to 1.26.1
PgBouncer 1.21.0 to 1.26.0Affected by CVE-2026-107348 only if `max_packet_size` exceeds 2147483648Upgrade to 1.26.1
Debian bookworm, package 1.18.0-1+deb12u1Listed as vulnerable by the Debian security trackerPGDG repository or build from source
Debian trixie, package 1.24.1-1+deb13u2Listed as vulnerable by the Debian security trackerPGDG repository or build from source
Debian forky and sid, package 1.26.1-1FixedUpgrade through apt
Third-party Docker imageDepends on the embedded versionCheck `pgbouncer -V` inside the container
Version older than 1.17.0Outside the range announced for CVE-2026-107350Do not settle for it, plan the upgrade

"My PgBouncer is not exposed": an insufficient criterion

The objection is fair: a PgBouncer listening only on a private address cannot be reached from the Internet. But the attack needs no brute force. It is enough to know the password of one valid SCRAM user and to reach the port. An application password lives in a .env file, a CI variable, a developer workstation, sometimes a log. A compromised application provides both the password and the network position. From there, the flaw turns a limited application account into an administration account, or even a superuser on the PostgreSQL server behind it.

The deciding criterion is therefore not "Internet or not" but two questions: who can open a connection to port 6432, and how many valid passwords are in circulation. A vulnerable version behind a strict firewall is a reduced risk, not a removed one.

What determines your real exposure

  • Listen address — listen_addr has no default value: without it, PgBouncer only accepts Unix sockets. The value * opens every interface.
  • Authentication type — the attack requires SCRAM for both users. Look at auth_type and at the nature of the entries in the file named by auth_file.
  • Number of accounts — every valid password is an entry point to all the other SCRAM accounts. Ten application accounts are ten keys to the same door.
  • Administration accounts — admin_users opens the console, and the role used to reach PostgreSQL may be a superuser.
  • Docker and firewall — a port published by Docker goes through its own network rules, which often bypass ufw. Check how the port is published, not only the firewall.
  • Options tied to the denial-of-service flaws — ignore_startup_parameters non-empty without options, or max_packet_size raised above its default.
  • Containers and bundled stacks — a PgBouncer shipped in a Compose stack or a chart keeps its image's version, whatever the host server runs.

Check the version and exposure in a few minutes

  1. Query the admin console

    psql -h 127.0.0.1 -p 6432 -U <admin_user> pgbouncer -c "SHOW VERSION;"
    Only users listed in admin_users or stats_users can connect. The SHOW VERSION command prints PgBouncer's version string: after the fix, it must mention 1.26.1.

  2. Read the binary version

    pgbouncer -V
    which -a pgbouncer
    Several binaries sometimes coexist, the package one and another under /usr/local after a build. The first in the list is not always the one the service runs.

  3. Read the package version

    Debian and Ubuntu:
    dpkg -l pgbouncer
    apt-cache policy pgbouncer
    RHEL, AlmaLinux, Rocky:
    rpm -q pgbouncer
    dnf info pgbouncer
    Compare with the table above. A distribution package can receive a fix without changing its upstream version number: trust the distribution's security tracker more than the number alone.

  4. Reread the configuration actually loaded

    ps -eo pid,args | grep '[p]gbouncer'
    grep -nE '^(listen_addr|listen_port|auth_type|auth_file|admin_users|ignore_startup_parameters|max_packet_size)' /etc/pgbouncer/pgbouncer.ini
    The .ini file to read is the one passed as an argument to the process, not necessarily the usual path.

  5. See who can reach the port

    sudo ss -ltnp | grep 6432
    sudo ufw status numbered
    nc -vz <server_ip> 6432
    The last command, run from another machine, tells you whether the port answers outside the intended network. An unexpected answer calls for an immediate firewall decision.

  6. Count SCRAM accounts without printing secrets

    grep -c 'SCRAM-SHA-256' /etc/pgbouncer/userlist.txt
    Replace the path with that of auth_file. Never paste the file contents into a ticket or a chat: it holds the fingerprints of your passwords.

  7. Inventory the containers

    docker ps
    docker port <container>
    docker exec <container> pgbouncer -V
    A running container keeps the image it started with. The docker port output says whether the port is published on the host.

Which upgrade route: the state of Debian packages

At the time of writing, the Debian security tracker lists bookworm (1.18.0-1+deb12u1) and trixie (1.24.1-1+deb13u2) as vulnerable to CVE-2026-107350, with only forky and sid carrying the fixed package 1.26.1-1. Until Debian publishes a fix for your release, apt upgrade alone does not protect you. Recheck the tracker on the day you act: the state may have changed.

Three routes exist. The first: the PGDG repository, apt.postgresql.org for Debian and Ubuntu, yum.postgresql.org for RHEL and derivatives, which the PgBouncer download page describes as builds that may be newer than those in distributions. The second: an up-to-date Docker image or tag, knowing that the project mentions no official image, so you are using a third-party one. The third: build the source archive of version 1.26.1. Before switching, confirm with apt-cache policy pgbouncer or dnf info pgbouncer that the repository really offers the fixed version.

Upgrade according to your installation method

  1. Back up the configuration

    sudo cp -a /etc/pgbouncer /root/pgbouncer-etc-backup
    This copy lets you roll back if an upgrade replaces a file. Also note the current version before you start.

  2. Debian and Ubuntu: enable the PGDG repository

    sudo apt install -y postgresql-common ca-certificates
    sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
    apt-cache policy pgbouncer
    The official script configures the apt.postgresql.org repository. The last command must show a 1.26.1 candidate; if it is missing, go no further with this route.

  3. Debian and Ubuntu: install

    sudo apt update
    sudo apt install --only-upgrade pgbouncer
    pgbouncer -V
    If dpkg offers to replace your configuration file, keep your local version. pgbouncer -V confirms the installed binary.

  4. RHEL, AlmaLinux and Rocky

    Add the PGDG repository with the commands generated by the yum.postgresql.org selector for your distribution, then:
    sudo dnf makecache
    dnf info pgbouncer
    sudo dnf upgrade pgbouncer
    Check that the proposed version is 1.26.1 before confirming.

  5. Docker and Compose

    docker compose pull pgbouncer
    docker compose up -d pgbouncer
    docker compose exec pgbouncer pgbouncer -V
    If the image is pinned to an old tag or digest, first update that reference in your Compose file. If no 1.26.1 tag exists, build the image from source.

  6. Build from source

    curl -LO https://www.pgbouncer.org/downloads/files/1.26.1/pgbouncer-1.26.1.tar.gz
    sha256sum pgbouncer-1.26.1.tar.gz
    Compare the checksum you get with the one published on the download page before continuing.
    tar xzf pgbouncer-1.26.1.tar.gz
    cd pgbouncer-1.26.1
    ./configure --prefix=/usr/local
    make
    sudo make install
    The build needs libevent 2.0 or later, OpenSSL 1.0.1 or later and a C11 compiler. The documentation recommends Autoconf for production.

  7. Restart, then verify

    sudo systemctl restart pgbouncer
    ps -o lstart= -p $(pgrep -o pgbouncer)
    sudo readlink -f /proc/$(pgrep -o pgbouncer)/exe
    A reload is not enough: the process must restart on the new binary. The start time and the executable path prove it. Finally, run SHOW VERSION on the console again.

Restarting without cutting clients, and the search_path case

A restart drops open connections. The documentation describes an alternative: several processes on the same port with so_reuseport, then SHUTDOWN WAIT_FOR_CLIENTS on one process at a time while clients reconnect. Another point of attention: since version 1.26.0, search_path is tracked by default, and 1.26.1 makes ignore_startup_parameters take precedence over tracked parameters. If an application sent search_path at startup and you were counting on ignoring it, add ignore_startup_parameters = search_path to the configuration to get the 1.25 behavior back.

Workarounds until the fix is in

For CVE-2026-107350, the announcement gives no configuration workaround: the upgrade is the only real answer. You can, however, reduce who gets to try. Limit port 6432 to the application network:
sudo ufw allow from <app_subnet> to any port 6432 proto tcp
sudo ufw status numbered
sudo ufw delete <n>
Here <n> is the number of a broader rule already present, for example one opening port 6432 to everyone, which you delete once the restricted rule is in place. On RHEL and derivatives, the same rule is set with firewalld. If listen_addr is *, replace it with the server's private address. Then audit the auth_file file: remove unused accounts, rotate passwords that have circulated, and shrink admin_users to the bare minimum.

For the two denial-of-service flaws, what follows is our reading of the changelog's conditions, not a vendor instruction. CVE-2026-107349 assumes ignore_startup_parameters is non-empty and lacks options: adding options to the list removes the condition; emptying the setting also removes it, but may cause clients that send startup parameters to be rejected (see the search_path case above). CVE-2026-107348 assumes a max_packet_size above 2147483648: keeping the default value, 2147483647, rules it out.

Review the traces of an impersonated connection

By default PgBouncer logs successful connections and disconnections, with their reason, thanks to log_connections and log_disconnections. Read the period that follows the publication of the fixes, and further back if your logs allow:
sudo journalctl -u pgbouncer --since "2026-10-14"
Look for attempts against database names that do not exist in your [databases] section, then a session opened just afterwards under a different user name from the same address. On the console, these two commands show the current state:
psql -h 127.0.0.1 -p 6432 -U <admin_user> pgbouncer -c "SHOW DATABASES;"
psql -h 127.0.0.1 -p 6432 -U <admin_user> pgbouncer -c "SHOW CLIENTS;"
On the PostgreSQL side, list the roles and their recent changes with psql -c "\du".

We do not have an exact signature of this attack in the logs, and we do not invent one. An absence of traces does not prove an absence of intrusion, especially if log_connections was turned off or logs have rotated. If you have serious doubts, change the passwords of all SCRAM accounts and of the privileged roles on the PostgreSQL server.

Closing the file: from version to follow-up

A successful upgrade is proven, not assumed. For each server, record in a table the machine, the installation method, the version before and after, the firewall rule applied, and the date of the check. Verify that SHOW VERSION reports 1.26.1, that the process restarted after the installation, and that port 6432 is reachable only from the intended network. Also confirm that your two denial-of-service settings respect the conditions described above, or that they have become moot.

Finally, do not count on automatic updates: an unfixed distribution package does not update itself, and neither does a pinned Docker image. Add a reminder to recheck your distribution's security tracker, then switch to its package once it is fixed, or keep the PGDG repository deliberately.

On a VPS of your own, you hold the three levers

Fixing PgBouncer takes three kinds of access: choosing the installed version, setting the firewall on port 6432 and reading the logs. On a VPS Cloud with root access, these three levers are yours, without depending on a shared host's schedule. The guides linked below this article detail the firewall and the PostgreSQL setup. This guide is provided without support: the upgrade remains your responsibility. If you would rather delegate server administration, the VPS administration option exists, to be assessed against your needs.

The official documentation

Announcement of version 1.26.1: https://www.postgresql.org/about/news/pgbouncer-1261-released-fixes-three-cves-3403/

PgBouncer changelog: https://www.pgbouncer.org/changelog.html

PgBouncer downloads: https://www.pgbouncer.org/downloads/

Usage and admin console: https://www.pgbouncer.org/usage.html

PgBouncer configuration: https://www.pgbouncer.org/config.html

Debian security tracker for CVE-2026-107350: https://security-tracker.debian.org/tracker/CVE-2026-107350

Mend entry for CVE-2026-107350: https://www.mend.io/vulnerability-database/CVE-2026-107350/

PostgreSQL apt repository: https://wiki.postgresql.org/wiki/Apt

PostgreSQL yum repository: https://yum.postgresql.org/

Docker and firewalls: https://docs.docker.com/engine/network/packet-filtering-firewalls/

A server whose version and firewall you control

On a VPS Cloud with root access, you choose the PgBouncer version, close port 6432 to the intended machines only and read your own logs. The VPS administration option lets you delegate that follow-up.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab