Em 6 de setembro de 2026, a rede Liquid Network sofreu um ataque que explorou uma vulnerabilidade na base de código do Elements, usada para verificar transações confidenciais.
O problema surgiu da formação ambígua da chave de cache na verificação do rangeproof, a prova criptográfica que confirma que o valor oculto de uma transação é admissível. Por causa disso, dois conjuntos diferentes de dados de verificação podiam gerar a mesma entrada no cache.
Primeiro o atacante preparou transações especiais e preencheu o cache com elas e, em seguida, enviou uma transação maliciosa que aumentava o volume de ativos emitidos: ela reutilizou um resultado de verificação já armazenado e permitiu criar L-BTC não autorizados.
Depois disso, o atacante trocou esses ativos dentro da rede Liquid por bitcoins reais por meio do mecanismo de retirada de recursos para a rede principal do Bitcoin.
Contexto
Os bitcoins bloqueados em uma carteira Bitcoin específica, controlada pela federação Liquid, dão lastro aos L-BTC emitidos na Liquid. De acordo com o sistema de ancoragem previsto, um L-BTC deve corresponder a um BTC sob o controle da federação.
Em vez de publicar o valor, a transação pode conter um compromisso criptográfico de Pedersen. O próprio ativo também pode ser representado por um compromisso semelhante. Uma equação matemática específica confirma que os valores de entrada e de saída da transação estão equilibrados, enquanto o chamado rangeproof demonstra que cada valor oculto está dentro da faixa não negativa permitida.
Em particular, a verificação do rangeproof faz parte do mecanismo que deve garantir que o valor oculto é realmente correto e não pode ser usado para criar ativos acima da quantidade permitida. Ao mesmo tempo, a verificação efetiva da correção depende de vários elementos simultaneamente: o rangeproof, o compromisso pelo valor, o compromisso pelo ativo e o scriptPubKey.
O script é passado para secp256k1_rangeproof_verify como dados adicionais incluídos na verificação criptográfica. Uma prova válida para uma combinação (C, X, S) não se torna automaticamente válida para outra combinação.
Transações-chave
| Data/hora | TXID | Descrição |
|---|---|---|
| 2026-09-06 13:52:10 | 271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5 | Transação preparatória |
| 2026-09-06 13:53:10 | f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183f | A transação que criou volume adicional de L-BTC dentro da Liquid |
| 2026-09-06 13:54:10 | 3875a6d6ed4af708e6fd90d1c5504dc014e52c7093a566987252006d6cf1146b | A transação cria novas saídas confidenciais da Liquid controladas pelo atacante ou pela carteira seguinte |
| 2026-09-06 14:00:10 | c6ea588ac26f5838b6acbb2a444a33b325bbfeb39bf16dfe27b47215ffd72267 | A transação cria novas saídas confidenciais da Liquid controladas pelo atacante ou pela carteira seguinte |
| 2026-09-06 14:01:10 | 46f117c990580501a5156937a8c8affda551a38b3c0b99d29eb8469ee6beb3d2 | Retirada da Liquid no valor de 2.65138358 L-BTC |
| 2026-09-06 14:06:10 | ce4caece413cd9d444ce7ed9f54e5b328b3da5e4af301aff59a3571f76e988f2 | Retirada da Liquid no valor de 3,996.01834922 L-BTC |
| 2026-09-06 14:28:56 | 85d2ca15bea33a592e73ed40c6a5da887feecf1e77f58ec7f580e00841645043 | 3,996.01834922 BTC enviados para o endereço bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte |
| 2026-09-07 18:09:25 | a6d697a25266ce3c78774fd1d75f896b7af522ada209b0f6228ea497bc49a46d | 3,400 BTC devolvidos ao endereço bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxr |
Endereços-chave
| Endereço | Uso |
|---|---|
| ex1q7kgx4ptje7px48tn0nsmc6se5pngdp3smpqa2w | Endereço do atacante na rede Liquid |
| bc1qgslsydz56d0ed6827hdemfmk5w2f6ldyc6wt7p | Destinatário do pagamento da federação de 3,996.01834922 BTC |
| bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte | Destinatário do pagamento da federação de 2.65138358 BTC |
| bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte | Endereço do atacante para consolidar recursos e transmitir mensagens na blockchain |
| bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxr | Carteira Bitcoin da federação Liquid usada para a ancoragem e a retirada de recursos |
1. Falsificação do cache de provas
Para a análise, foram obtidos e decompostos os dados brutos de serialização do Elements, e não apenas os campos exibidos por um explorador de blockchain. As saídas :0 das duas transações preparatórias coincidem por completo em ativo, compromisso pelo valor, nonce, script, surjection proof e rangeproof.
Usando deslocamentos que começam em zero e um limite superior que não é incluído na faixa, a partir dos 8,882 bytes da transação bruta foi decodificado o output_0 da transação 271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5, o que resultou nos seguintes dados:
a. C0 = 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5
b. S0 = 6a|43|086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8|0a0a488 de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d|6a.
i. 6a significa OP_RETURN. ii. 43 indica que em seguida devem ser colocados 67 bytes.
Esses 67 bytes têm deliberadamente a seguinte estrutura: um C1 de 33 bytes, um X de 33 bytes e um 6a de um byte. Assim, o script S0 = 6a43 || C1 || X || 6a, em que X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d é o resultado da execução de secp256k1_generator_generate(L-BTC asset ID).
2. Preenchimento do cache no mempool ativo
Na conexão de um bloco valem regras de cache segundo as quais uma entrada existente é consumida, mas, se não houver entrada, uma nova não é armazenada. Por isso, a conexão do bloco 4,050,335 deveria ter removido a entrada de cache criada no momento em que as duas transações preparatórias visíveis foram aceitas pela primeira vez.
Consequentemente, para que a transação que criava L-BTC adicionais pudesse passar pela verificação após a conexão do bloco 4,050,335 e antes da verificação da saída incorreta, cada nó que aceitava a transação precisava de uma nova transação válida com a mesma sequência (P0, C0, X, S0).
O identificador dessa transação de preenchimento e seus bytes originais ainda não são conhecidos. Para encontrá-los podem ser necessários o arquivo debug.log de um dos functionaries, o mempool.dat vigente naquele momento, um registro da troca de dados entre nós ou uma cópia salva do estado do mempool.
3. Criação de L-BTC adicionais dentro da Liquid
A decodificação do output_1 da transação de inflação f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183f resultou nos seguintes dados:
a. C1 = 086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8.
São exatamente os primeiros 33 bytes dos dados transmitidos pelo script OP_RETURN da saída preparatória.
Vale notar que o rangeproof P1 é 68 bytes mais longo que o P0, e esses dados adicionais estão no final: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5
b. 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d6a43, o que dá o C0 da transação preparatória: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5
e X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d é o resultado da execução de secp256k1_generator_generate(L-BTC asset ID). A incorporação do gerador exato de L-BTC permitiu que os bytes do script da transação preparatória se alinhassem com o campo do gerador do ativo na transação atacante.
c. No conjunto
Transação preparatória válida: P0 || C0 || X || (6a43 || C1 || X || 6a)
Transação atacante inválida: (P0 || C0 || X || 6a43) || C1 || X || 6a
Ambas acabam se reduzindo ao mesmo fluxo de 4,301 bytes: P0 || C0 || X || 6a43 || C1 || X || 6a
4. Retirada dos recursos
Os L-BTC podem ser trocados de volta por BTC reais sob o controle da federação Liquid. A transação seguinte, 3875a6d6…f1146b, usou a saída f24…:0 da transação ligada ao ataque. Outras duas transações criaram saídas explícitas para retirar recursos para a rede principal do Bitcoin:
- 46f117c9…eb3d2: 2.65138358 L-BTC
- ce4caece…988f2: 3,996.01834922 L-BTC
A transação de Bitcoin 8db751a6…7b140, confirmada no bloco 965,783, pagou os dois valores exatos retirados em consequência do ataque.
Em que consistia a essência da vulnerabilidade?
O Elements mantém em cache os rangeproof verificados com sucesso para não executar duas vezes a mesma operação criptográfica custosa. Aqui importam duas construções.
Esse desenho não levava em conta o gerador do ativo nem o script, embora ambos os parâmetros influenciem a validade da prova. O commit c26d719 tentou vincular a entrada de cache a todo o contexto da verificação.
O patch com a implementação do cache
Transação preparatória válida: (P0, C0, X, 6a43 || C1 || X || 6a)
Transação atacante inválida: (P0 || C0 || X || 6a43, C1, X, 6a)
As saídas observadas da transação preparatória e da atacante não criam colisão quando é usada a chave mais antiga P || C. Seus dados de entrada para a chave antiga diferem tanto em comprimento quanto em conteúdo.
A construção do ataque, em contrapartida, corresponde exatamente à chave de quatro campos sem separadores introduzida por essa correção. Por isso, o fato de a transação ter passado com sucesso indica uma implementação semelhante à c26d719 combinada com um cache previamente preenchido. Ao mesmo tempo, as versões exatas dos binários implantados nos diferentes nós functionaries da Liquid não foram divulgadas publicamente.
Problema de sincronização: como o protocolo Verus perdeu $7.5M por causa das diferenças entre blockchainsA movimentação dos recursos
Endereço: bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte.
