Post

CVE-2026-48773: pre-auth first-packet heap overflow in ProxySQL

CVE-2026-48773: pre-auth first-packet heap overflow in ProxySQL

I reported a pre-authentication memory corruption issue in ProxySQL that was published as CVE-2026-48773 / GHSA-58ww-865x-grpr.

The issue affected ProxySQL’s first-packet handling for MySQL and PostgreSQL frontend connections. A remote unauthenticated client could declare an oversized packet length before authentication, and ProxySQL would pass that attacker-controlled length to recv() while writing into a fixed 32 KB input queue.

That made the vulnerable write happen before protocol authentication or later packet validation could reject the connection.

Advisory

  • CVE: CVE-2026-48773
  • GitHub Advisory: GHSA-58ww-865x-grpr
  • Package: proxysql
  • Affected versions: >= 2.0.18, <= 3.0.8
  • Patched version: 3.0.9
  • Severity: Critical 9.8/10
  • CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
  • CWE: CWE-787: Out-of-bounds Write

Root Cause

ProxySQL uses fixed-size queues for protocol input buffers. The relevant queue size is 32768 bytes.

The vulnerable pattern was simple:

  1. Read the small protocol header into the queue.
  2. Parse a length controlled by the unauthenticated client.
  3. Call recv() again with that declared length.
  4. Write the result at an offset inside the same fixed queue.

For the MySQL frontend path, the second read used the 24-bit MySQL packet length and wrote at queueIN + 4.

For the PostgreSQL frontend path, the second read used the parsed 32-bit startup packet length and wrote at queueIN + 5.

The parser and authentication logic ran too late to be a defense. By the time later code could reject the packet, the oversized network read had already crossed the queue boundary.

Attack Shape

The attacker does not need ProxySQL credentials.

The minimal shape is:

  1. Connect to an exposed ProxySQL frontend listener.
  2. Send a first packet with a declared length larger than the remaining queue capacity.
  3. Keep the socket open long enough for ProxySQL to read attacker-controlled bytes into the fixed queue.

The issue was reproduced against real ProxySQL daemon listeners under AddressSanitizer:

  • MySQL frontend path: heap-buffer-overflow in MySQL_Data_Stream::read_from_net().
  • PostgreSQL frontend path: heap-buffer-overflow in PgSQL_Data_Stream::read_from_net().

Supporting stock Docker tests also showed a crash through an optional ClickHouse/MySQL-style listener, which reused the same MySQL data-stream path.

Validation Notes

The strongest part of the report was that it did not rely only on a small harness.

I validated the primitive in three layers:

  • real ProxySQL daemon with AddressSanitizer on the MySQL frontend listener 6033;
  • real ProxySQL daemon with AddressSanitizer on the PostgreSQL frontend listener used in the test, 6133;
  • a minimal ASAN harness that reproduced the queue boundary deterministically.

The boundary matrix was useful during triage because it showed the transition from in-bounds to out-of-bounds behavior around the 32 KB queue size. For the tested MySQL shape, declared lengths 32763 and 32764 stayed in-bounds, while 32765, 65535, and 100000 crossed the queue boundary.

I also used stock Docker smoke tests as supporting evidence. Those tests were not the primary proof, but they helped show that the bug class was not only an artifact of a local ASAN build.

Why This Was Subtle

There was partial public overlap with an older PROXY-protocol issue, but that did not fix the broader bug class.

The older case involved the literal PROX prefix being interpreted as a MySQL packet header. The vulnerable behavior here was more general: ordinary non-PROXY MySQL first packets still used the declared packet length directly as a receive size, and the PostgreSQL path had the same unbounded first-read pattern independently.

So the important security boundary was not the PROXY-protocol special case. It was the first unauthenticated packet read itself.

Impact

This is pre-authentication network memory corruption in ProxySQL protocol listeners.

Impacted deployments include instances exposing:

  • the normal MySQL frontend listener;
  • a configured PostgreSQL frontend listener;
  • other enabled listeners that reuse the same MySQL or PostgreSQL data-stream classes before authentication.

The directly demonstrated impact was AddressSanitizer-confirmed heap out-of-bounds write on real daemon listeners. Because the write is reachable before authentication and uses attacker-controlled length and payload, it should be treated as potentially exploitable memory corruption unless proven otherwise.

Fix Direction

The fix should keep queue bounds ahead of protocol parsing assumptions.

Defensive principles:

  • read only the protocol header first;
  • validate declared packet length before reading the full packet;
  • never call recv() with a length greater than the remaining queue capacity;
  • validate against protocol limits such as mysql-max_allowed_packet and pgsql-max_allowed_packet;
  • if a valid packet is larger than the current queue, read it incrementally or allocate a bounded buffer only after validation;
  • add pre-auth regression tests around queue boundary values.

References

This post is licensed under CC BY 4.0 by the author.