Resumen
MariaDB Connector/J, el driver JDBC de MariaDB, confiaba ciegamente en un dato que llega del servidor: el número de columnas de un resultado. Ese valor se lee de la conexión como entero de longitud codificada y se usa tal cual para crear un array en Java, sin comprobar ningún límite. Es un fallo de consumo de recursos sin control (CWE-400).
El código afectado está en src/main/java/org/mariadb/jdbc/message/ClientMessage.java:
default:
int fieldCount = buf.readIntLengthEncodedNotNull(); // line 282: server-controlled
ColumnDecoder[] ci;
if (context.canSkipMeta() && this.canSkipMeta()) {
...
} else {
ci = new ColumnDecoder[fieldCount]; // line 302: unbounded allocation
for (int i = 0; i < fieldCount; i++) { ... }
}
La función readIntLengthEncodedNotNull() (ReadableByteBuf.java:148) devuelve el valor recibido sin tocarlo; en la forma 0xfe hace return (int) readLong(), así que quien controle la respuesta decide el entero de 32 bits completo. Según el valor elegido, el resultado cambia:
fieldCount = 0x10000000(unos 268 millones):OutOfMemoryError: Java heap spacefieldCount = 0x7fffffff:OutOfMemoryError: Requested array size exceeds VM limitfieldCount = 0xffffffff:NegativeArraySizeException
Ninguna de estas excepciones se captura dentro de readPacket. Basta un paquete de unos 10 bytes.
Un detalle agrava el problema: por defecto el driver usa sslMode=DISABLE (Configuration.java:309), es decir, tráfico en claro. No hace falta controlar la base de datos real; un atacante situado en la red entre cliente y servidor puede reescribir la respuesta.
El investigador señaló además otros puntos de la misma familia: Reader.java:133 (un acumulador sin límite) y OkPacket.java:160 (new byte[readIntLengthEncodedNotNull()]), y citó como precedente el reporte #3835450, un fallo equivalente aceptado en connector-nodejs con severidad media.
Pasos de reproducción
Entorno de la prueba: OpenJDK 21 y el driver compilado desde el commit c9377bc (mariadb-java-client-3.5.9.jar). El investigador adjuntó rogue_server.py, OomClient.java, run_poc.sh y poc_evidence.txt.
- Arrancar el servidor falso. Completa el saludo inicial en texto plano, responde OK a la consulta de configuración que lanza el driver y, cuando recibe
SELECT 1, contesta con una cabecera de resultado que declarafieldCount = 0x10000000:
python3 rogue_server.py positive 0 &
- Lanzar un cliente JDBC corriente contra ese servidor, con el heap limitado a 256 MB:
java -Xmx256m -cp .:mariadb-java-client-3.5.9.jar OomClient "jdbc:mariadb://127.0.0.1:<port>/test?user=app&password=[REDACTADO]"
La JVM muere al intentar reservar el array:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at org.mariadb.jdbc.message.ClientMessage.readPacket(ClientMessage.java:302)
at org.mariadb.jdbc.client.impl.StandardClient.readPacket(StandardClient.java:1438)
at org.mariadb.jdbc.client.impl.StandardClient.readResults(StandardClient.java:1377)
at org.mariadb.jdbc.Statement.executeQuery(Statement.java:137)
at OomClient.main(OomClient.java:22)
client exit code = 1
- Variante en la conexión: con el servidor en modo
connect, la respuesta manipulada se inyecta en la propia consulta de configuración del driver. El fallo salta dentro deDriverManager.getConnection(), antes de que la aplicación ejecute nada y sin necesidad de credenciales válidas:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at org.mariadb.jdbc.message.ClientMessage.readPacket(ClientMessage.java:302)
at org.mariadb.jdbc.client.impl.StandardClient.postConnectionQueries(StandardClient.java:816)
at org.mariadb.jdbc.Driver.connect(Driver.java:75)
- Control negativo: mismo driver, misma URL y mismo
SELECT 1, pero con una respuesta legítima de una columna. Todo funciona:
connected; running SELECT 1
RESULT=1
CLEAN_EXIT
client exit code = 0
Lo único que cambia entre ambos casos es el número de columnas que anuncia el servidor.
Impacto
Con un solo paquete pequeño, un servidor malicioso o un intermediario en la red hace caer la JVM del cliente. Un OutOfMemoryError no afecta solo a una consulta: tumba la aplicación entera, con todas las conexiones del pool y todos los hilos de trabajo.
Como la variante de conexión se dispara dentro de DriverManager.getConnection(), no hacen falta credenciales válidas ni consultas de la aplicación. Cualquier servicio que abra una conexión JDBC hacia un destino en el que el atacante pueda influir queda expuesto: cadenas de conexión controlables al estilo SSRF, un MITM sobre un enlace sin cifrar, un servidor de base de datos comprometido o una réplica hostil en una topología con conmutación por error.
La única condición es que el atacante controle los bytes que recibe el cliente; con el sslMode=DISABLE por defecto, basta con estar en la red.
El reporte se envió el 18 de julio de 2026, quedó resuelto con severidad media y se hizo público el 30 de septiembre de 2026.