HexBrain

MariaDB · Report #3871740

r2dbc-mariadb Parsec auth: server-controlled PBKDF2 iteration count causes CPU-exhaustion DoS ( regression from b7766de )

Validstatus
mediumseverity
6upvotes
12 Oct 2026disclosed

How it was found

In mariadb-connector-r2dbc 1.4.1 (HEAD a82d265, all released versions), the Parsec authentication plugin derives its key with PBKDF2 using an iteration count of 1024 << iterations, where iterations is a single byte read directly from the server's ext-salt response and bounded only by the check iterations > 20. A malicious or man-in-the-middle MariaDB server that selects the parsec plugin can set iterations = 20, forcing 1024 << 20 = 1,073,741,824 (~1.07e9) rounds of PBKDF2-HMAC-SHA512 during the handshake, pinning a client CPU core for ~25 minutes per connection attempt before authentication completes. This is a regression introduced by commit b7766de, which loosened the cap from > 3 (old maximum 1024 << 3 = 8192 rounds) to > 20.

Root cause

ParsecPasswordPlugin.java:

 64   if (buf.readableBytes() > 2) {
 65     firstByte = buf.readByte();
 66     iterations = buf.readByte();          // fully server-controlled
 67   }
 68
 69   if (firstByte != 0x50 || iterations > 20) {   // only upper bound
 70     throw new R2dbcNonTransientResourceException(...);
 71   }
 ...
104   PBEKeySpec spec = new PBEKeySpec(password, salt, 1024 << iterations, 256);
105   SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512").generateSecret(spec);

iterations is taken from the server's ext-salt reply (authMoreData) and is fully attacker-controlled. The parsec plugin is registered in META-INF/services, and the server chooses the authentication plugin during the handshake. restrictedAuth defaults to null (no client-side allow-list), so the driver runs parsec whenever the server asks. The exchange happens before authentication is established. The driver also defaults to sslMode=DISABLE (plaintext), exposing it to an active MITM.

Steps To Reproduce

Environment: JDK 21, the project's own build (mvn -o -DskipTests package producing r2dbc-mariadb-1.4.1.jar).

Proof 1 - End-to-end rogue server (RogueParsecServer.java attached):

A rogue MariaDB server speaks the wire protocol through the parsec ext-salt exchange, then sends a malicious iterations byte. It measures server-side how long the real MariadbConnectionFactory.create() is stuck computing PBKDF2. The same server differs only in that one byte:

  1. Compile and run the rogue server:
javac -cp r2dbc-mariadb-1.4.1.jar:reactor-core.jar:r2dbc-spi.jar RogueParsecServer.java
java -Djava.security.egd=file:/dev/urandom -cp .:r2dbc-mariadb-1.4.1.jar:reactor-core.jar:r2dbc-spi.jar:reactive-streams.jar:netty-*.jar RogueParsecServer
  1. Observe the output:
[neg control] ext-salt iterations=3 (1024<<3 = 8192 rounds):
    server saw client stuck 160 ms ; client create() returned in 1148 ms

[attack] ext-salt iterations=15 (1024<<15 = 33554432 rounds):
    server saw client stuck 47286 ms ; client create() returned in 47604 ms
    at the maximum allowed iterations=20 this is 1024<<20 = 1073741824 rounds -> ~25 min.
    (real driver logs "Authentication success" -- the full handshake completes after the delay)

A single attacker-chosen byte pinned the real driver's connect for ~47 seconds versus ~160 ms for the iterations=3 control (296x).

Proof 2 - Direct scaling measurement (ParsecPoc.java attached):

Measures the exact lines 104-105 computation across iteration exponents, confirming the 2x-per-increment law:

iterations   rounds           wall_ms
3            8192             231            <- negative control (old cap > 3)
10           1048576          1518
13           8388608          11745
15           33554432         45113
extrapolation to iterations=20 (rounds=1073741824): ~1503360 ms (~25.1 min)

Real ParsecPasswordPlugin.next() at iterations=13: 11753 ms (81x the iterations=3 control of 145 ms).

Supporting Material/References

  • Vulnerable file: src/main/java/org/mariadb/r2dbc/authentication/standard/ParsecPasswordPlugin.java:104
  • Regression commit: b7766de (loosened cap from > 3 to > 20)
  • Accepted sibling: H1 #3835450 (mariadb-connector-nodejs DoS, Medium)
  • Attachments: RogueParsecServer.java, ParsecPoc.java, poc_evidence.txt

Impact

A malicious or MITM MariaDB server pins a client CPU core for minutes per connection attempt, unauthenticated and repeatable. Behind a connection pool that retries failed connections, this becomes sustained whole-application CPU exhaustion and denial of service. The asymmetric amplification is extreme: a single attacker-chosen byte forces ~1.07 billion PBKDF2-HMAC-SHA512 rounds (~25 minutes of CPU). No valid credentials are needed, and the default sslMode=DISABLE exposes the handshake to network-level MITM. The fix is to restore a small hard cap on the iteration exponent (e.g. iterations > 3), matching the pre-b7766de behavior.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original