Un error de unos pocos bytes: análisis del ataque a Liquid Network

Un error de unos pocos bytes: análisis del ataque a Liquid Network

Una vulnerabilidad en el código de Liquid Network permitió crear cerca de 3,998.5 L-BTC aprovechando un fallo en el mecanismo de caché de las comprobaciones criptográficas.

9 sept 2026

El 6 de septiembre de 2026, la red Liquid Network sufrió un ataque que aprovechó una vulnerabilidad en la base de código de Elements, que se utiliza para verificar transacciones confidenciales.

El problema surgió por la formación ambigua de la clave de caché al verificar el rangeproof, la prueba criptográfica que confirma que el importe oculto de una transacción es admisible. Por este motivo, dos conjuntos distintos de datos de verificación podían dar lugar a una misma entrada en la caché.

Primero el atacante preparó transacciones especiales y llenó la caché con ellas, y después envió una transacción maliciosa que aumentaba el volumen de activos emitidos: reutilizó un resultado de verificación ya almacenado y permitió crear L-BTC no autorizados.

Después de eso, el atacante cambió esos activos dentro de la red Liquid por bitcoins reales mediante el mecanismo de retirada de fondos a la red principal de Bitcoin.

Antecedentes

Los bitcoins bloqueados en una cartera de Bitcoin específica, controlada por la federación Liquid, respaldan los L-BTC emitidos en Liquid. Según el sistema de anclaje previsto, un L-BTC debe corresponder a un BTC que se encuentre bajo el control de la federación.

En lugar de publicar el importe, la transacción puede contener un compromiso criptográfico de Pedersen. El propio activo también puede representarse mediante un compromiso similar. Una ecuación matemática específica confirma que los valores de entrada y de salida de la transacción están equilibrados, mientras que el llamado rangeproof demuestra que cada importe oculto se encuentra dentro del rango no negativo permitido.

En particular, la verificación del rangeproof forma parte del mecanismo que debe garantizar que el importe oculto sea realmente correcto y no pueda utilizarse para crear activos por encima de la cantidad permitida. Al mismo tiempo, la comprobación real de la corrección depende de varios elementos a la vez: el rangeproof, el compromiso por el valor, el compromiso por el activo y el scriptPubKey.

El script se transmite a secp256k1_rangeproof_verify como datos adicionales incluidos en la comprobación criptográfica. Una prueba válida para una combinación (C, X, S) no pasa a ser válida automáticamente para otra combinación.

Transacciones clave

Fecha/horaTXIDDescripción
2026-09-06 13:52:10271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5Transacción preparatoria
2026-09-06 13:53:10f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183fLa transacción que creó un volumen adicional de L-BTC dentro de Liquid
2026-09-06 13:54:103875a6d6ed4af708e6fd90d1c5504dc014e52c7093a566987252006d6cf1146bLa transacción crea nuevas salidas confidenciales de Liquid controladas por el atacante o por la siguiente cartera
2026-09-06 14:00:10c6ea588ac26f5838b6acbb2a444a33b325bbfeb39bf16dfe27b47215ffd72267La transacción crea nuevas salidas confidenciales de Liquid controladas por el atacante o por la siguiente cartera
2026-09-06 14:01:1046f117c990580501a5156937a8c8affda551a38b3c0b99d29eb8469ee6beb3d2Retirada de Liquid por un importe de 2.65138358 L-BTC
2026-09-06 14:06:10ce4caece413cd9d444ce7ed9f54e5b328b3da5e4af301aff59a3571f76e988f2Retirada de Liquid por un importe de 3,996.01834922 L-BTC
2026-09-06 14:28:5685d2ca15bea33a592e73ed40c6a5da887feecf1e77f58ec7f580e008416450433,996.01834922 BTC enviados a la dirección bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte
2026-09-07 18:09:25a6d697a25266ce3c78774fd1d75f896b7af522ada209b0f6228ea497bc49a46d3,400 BTC devueltos a la dirección bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxr

Direcciones clave

DirecciónUso
ex1q7kgx4ptje7px48tn0nsmc6se5pngdp3smpqa2wDirección del atacante en la red Liquid
bc1qgslsydz56d0ed6827hdemfmk5w2f6ldyc6wt7pReceptor del pago de la federación por 3,996.01834922 BTC
bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlteReceptor del pago de la federación por 2.65138358 BTC
bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlteDirección del atacante para consolidar fondos y transmitir mensajes en la blockchain
bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxrCartera de Bitcoin de la federación Liquid utilizada para el anclaje y la retirada de fondos

1. Falsificación de la caché de pruebas

Para el análisis se obtuvieron y descompusieron los datos brutos de serialización de Elements, y no solo los campos que muestra un explorador de blockchain. Las salidas :0 de las dos transacciones preparatorias coinciden por completo en activo, compromiso por el valor, nonce, script, surjection proof y rangeproof.

Utilizando desplazamientos que empiezan en cero y un límite superior que no se incluye en el rango, a partir de los 8,882 bytes de la transacción bruta se decodificó el output_0 de la transacción 271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5, lo que dio los siguientes datos:

a. C0 = 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

b. S0 = 6a|43|086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8|0a0a488 de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d|6a.

i. 6a significa OP_RETURN. ii. 43 indica que a continuación deben colocarse 67 bytes.

Esos 67 bytes tienen deliberadamente la siguiente estructura: un C1 de 33 bytes, una X de 33 bytes y un 6a de un solo byte. Así, el script S0 = 6a43 || C1 || X || 6a, donde X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d es el resultado de ejecutar secp256k1_generator_generate(L-BTC asset ID).

2. Llenado de la caché en el mempool activo

Al conectar un bloque se aplican reglas de caché según las cuales una entrada existente se consume, pero si no hay entrada, no se guarda una nueva. Por eso, la conexión del bloque 4,050,335 debería haber eliminado la entrada de caché creada en el momento en que se aceptaron por primera vez las dos transacciones preparatorias visibles.

En consecuencia, para que la transacción que creaba L-BTC adicionales pudiera pasar la verificación después de conectarse el bloque 4,050,335 y antes de comprobarse la salida incorrecta, en cada nodo que aceptaba la transacción se necesitaba una nueva transacción válida con la misma secuencia (P0, C0, X, S0).

El identificador de esta transacción de relleno y sus bytes originales aún no se conocen. Para encontrarlos podrían hacer falta el archivo debug.log de uno de los functionaries, el mempool.dat vigente en ese momento, un registro del intercambio de datos entre nodos o una copia guardada del estado del mempool.

3. Creación de L-BTC adicionales dentro de Liquid

La decodificación del output_1 de la transacción de inflación f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183f dio los siguientes datos:

a. C1 = 086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8.

Son exactamente los primeros 33 bytes de los datos transmitidos por el script OP_RETURN de la salida preparatoria.

Cabe señalar que el rangeproof P1 es 68 bytes más largo que P0, y esos datos adicionales se encuentran al final: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

b. 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d6a43, lo que da el C0 de la transacción preparatoria: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

y X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d es el resultado de ejecutar secp256k1_generator_generate(L-BTC asset ID). La incorporación del generador exacto de L-BTC permitió que los bytes del script de la transacción preparatoria se alinearan con el campo del generador del activo en la transacción atacante.

c. En conjunto

Transacción preparatoria válida: P0 || C0 || X || (6a43 || C1 || X || 6a)

Transacción atacante no válida: (P0 || C0 || X || 6a43) || C1 || X || 6a

Ambas se reducen finalmente al mismo flujo de 4,301 bytes: P0 || C0 || X || 6a43 || C1 || X || 6a

4. Retirada de los fondos

Los L-BTC pueden cambiarse de nuevo por BTC reales que se encuentran bajo el control de la federación Liquid. La transacción siguiente, 3875a6d6…f1146b, utilizó la salida f24…:0 de la transacción vinculada al ataque. Otras dos transacciones crearon salidas explícitas para retirar fondos a la red principal de Bitcoin:

  • 46f117c9…eb3d2: 2.65138358 L-BTC
  • ce4caece…988f2: 3,996.01834922 L-BTC

La transacción de Bitcoin 8db751a6…7b140, confirmada en el bloque 965,783, pagó los dos importes exactos retirados como resultado del ataque.

¿En qué consistía la esencia de la vulnerabilidad?

Elements almacena en caché los rangeproof verificados con éxito para no ejecutar dos veces la misma operación criptográfica costosa. Aquí importan dos construcciones.

Ese diseño no tenía en cuenta el generador del activo ni el script, aunque ambos parámetros influyen en la validez de la prueba. El commit c26d719 intentó vincular la entrada de caché con todo el contexto de verificación.

El parche con la implementación de la caché

Transacción preparatoria válida: (P0, C0, X, 6a43 || C1 || X || 6a)

Transacción atacante no válida: (P0 || C0 || X || 6a43, C1, X, 6a)

Las salidas observadas de la transacción preparatoria y de la atacante no generan una colisión si se utiliza la clave más antigua P || C. Sus datos de entrada para la clave antigua difieren tanto en longitud como en contenido.

La construcción del ataque, en cambio, se corresponde exactamente con la clave de cuatro campos sin separadores introducida por esa corrección. Por eso, el hecho de que la transacción pasara con éxito apunta a una implementación similar a c26d719 combinada con una caché llenada previamente. Al mismo tiempo, las versiones exactas de los binarios desplegados en los distintos nodos functionaries de Liquid no se han divulgado públicamente.

Problema de sincronización: cómo el protocolo Verus perdió $7.5M por las diferencias entre blockchains

El movimiento de los fondos

Un autoproclamado «white hat» devolvió 3,400 BTC, cuyo valor era de $268M, y se quedó con 598.5 BTC por valor de $47M - el 15% del total - como recompensa por haber encontrado la vulnerabilidad.

Dirección: bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte.

#Hackeo
Liquid Network fue hackeada por culpa de una caché: ¿qué pasó con 4,000 BTC?