Post-mortem COLDCARD: el fallo de entropía que permitió predecir wallets Bitcoin y robar 38 millones de dólares en BTC
Un cambio de firmware introducido en marzo de 2021 hizo que ciertas COLDCARD generaran secretos con mucha menos entropía de la esperada. Cinco años después, atacantes comenzaron a vaciar wallets de forma coordinada. No necesitaron malware, phishing ni acceso físico al dispositivo: el problema estaba en cómo se generaban las claves.
El 29–30 de julio de 2026 comenzaron a detectarse transacciones anómalas provenientes de wallets creadas con dispositivos COLDCARD.
La primera estimación ampliamente difundida habló de 594 BTC —aproximadamente USD $38 millones— extraídos de unas 500 wallets en apenas 25 minutos.
CoinDesk reportó que el movimiento ocurrió entre 01:31 y 01:56 UTC y que los fondos provenían de wallets single-signature.
Pero esa fotografía quedó rápidamente desactualizada.
Análisis posteriores atribuidos a Galaxy Research identificaron 1,196 wallets y más de 1,000 BTC movidos en una ventana de 41 minutos, con estimaciones cercanas a USD $70 millones para esa oleada. Investigaciones posteriores situaron las pérdidas potencialmente relacionadas con varias oleadas todavía más arriba.
Por eso en HackWise evitaríamos poner “594 BTC robados” como cifra definitiva.
Más de 1,000 BTC fueron drenados durante una de las principales oleadas; las estimaciones del incidente continuaron aumentando durante los días siguientes.
La vulnerabilidad llevaba ahí desde 2021
Aquí está la parte más interesante.
El 1 de marzo de 2021, un cambio de código modificó cómo COLDCARD obtenía la aleatoriedad necesaria para generar wallets.
El cambio apareció públicamente en firmware 4.0.0, publicado el 17 de marzo de 2021.
Antes, la generación utilizaba la ruta del RNG de hardware:
seed
↓
Hardware TRNG
↓
256 bits de entropía
↓
BIP-39 seed
↓
private keys
Después de la migración a libNgU, la generación pasó a utilizar:
ngu.random.bytes()
Y ahí comenzó el problema.
El bug técnico
COLDCARD tenía configurado:
#define MICROPY_HW_ENABLE_RNG (0)
La intención era que esa implementación de MicroPython no proporcionara el RNG.
Pero una comprobación utilizaba conceptualmente:
#ifndef MICROPY_HW_ENABLE_RNG
Esto comprueba:
“¿Está definida la macro?”
y no:
“¿Su valor es distinto de cero?”
Como la macro estaba definida —aunque valía 0— el build continuaba.
Después ocurrió algo todavía más delicado.
La implementación que COLDCARD esperaba utilizar no exportaba el símbolo global:
rng_get()
que libNgU buscaba.
El linker terminó resolviéndolo contra otra implementación disponible:
Yasmarang, un PRNG de propósito general incluido en MicroPython.
En términos simples:
Generar seed
↓
ngu.random.bytes()
↓
rng_get()
↓
¿Hardware TRNG?
✗
↓
MicroPython Yasmarang
↓
PRNG no criptográfico
Importante: Coinkite enfatiza que no fue un fallback intencional provocado porque fallara el TRNG físico.
El TRNG seguía existiendo.
El problema estaba en qué implementación terminaba alcanzando realmente la ruta de generación de seeds debido a la integración y resolución de símbolos.
¿De dónde salía entonces la “aleatoriedad”?
El análisis independiente de Block Engineering encontró que Yasmarang se inicializaba utilizando valores equivalentes a:
UID del microcontrolador
+
SysTick
+
RTC
↓
Yasmarang PRNG
↓
seed
Entre ellos estaban el identificador del MCU y registros relacionados con tiempo y temporización.
El problema fundamental es que esos valores no constituyen una fuente criptográfica de entropía.
- El UID es metadata fija del dispositivo.
- SysTick es un contador periódico.
- Los registros RTC están relacionados entre sí y con el momento de arranque.
Una vez determinado suficientemente el estado inicial y el historial de llamadas, la salida del PRNG se vuelve reproducible.
Y eso destruye una propiedad fundamental de una wallet:
La seed debe ser prácticamente imposible de adivinar.
De 128/256 bits a un espacio atacable
Aquí existe una discrepancia interesante que vale la pena conservar.
Estimación de Coinkite
Mk2 / Mk3
≈ 40 bits efectivos
Mk4 / Mk5 / Q
≈ 72 bits, bajo sus supuestos de ataque, debido a entropía adicional proveniente de secure elements.
Análisis de Block Engineering
Block es más pesimista.
Para Mk2/Mk3 v4, sostiene que una vez conocidos o restringidos:
- UID
- temporización
- historial de llamadas
no queda una entrada criptográficamente secreta que enumerar.
Para generaciones posteriores, Block encontró otro problema.
Aunque se incorporaba entropía proveniente de secure elements, solamente:
cuatro bytes del digest
terminaban llegando al reseed de Yasmarang.
Es decir, como máximo:
2³² estados criptográficamente diferenciados
bajo esas condiciones.
Eso es enormemente diferente del espacio esperado de una seed criptográfica.
Esto no significa que cualquier atacante pueda recuperar instantáneamente cualquier seed.
El costo real depende de cuánto pueda conocerse o restringirse:
- el UID;
- la temporización;
- las llamadas previas al RNG;
- y otros parámetros.
Esa distinción es importante para entender correctamente el alcance técnico del ataque.
¿Por qué SHA-256 no solucionó el problema?
Este punto es especialmente educativo.
Podrías pensar:
“Pero si después hago SHA-256, ¿no vuelvo aleatoria la seed?”
No.
Si únicamente existen:
2³² posibles entradas
aplicar:
SHA256(input)
produce como máximo:
2³² posibles resultados relevantes
El hash puede hacer que la salida parezca aleatoria, pero no crea entropía que nunca estuvo presente.
Block lo resume matemáticamente: aunque se aplique SHA256d, una familia de ≤2³² entradas continúa produciendo ≤2³² seeds posibles.
El checksum de BIP-39 tampoco agrega entropía.
Random-looking ≠ random
Un hash no puede recuperar la entropía que nunca existió.
¿Cómo pudieron robar Bitcoin sin tocar la wallet?
Esta es probablemente la parte más impactante del incidente.
El atacante no necesariamente necesitaba:
robar COLDCARD
↓
extraer físicamente la seed
↓
infectar la computadora
↓
engañar al usuario
El escenario técnico pasa a ser:
reconstruir posibles estados del PRNG
↓
generar seeds candidatas
↓
derivar claves/direcciones Bitcoin
↓
compararlas con información pública
↓
encontrar coincidencias
↓
obtener la clave privada
↓
firmar una transacción
↓
mover los BTC
Una dirección, xpub o clave pública conocida puede actuar como mecanismo para validar candidatos.
Si se recupera correctamente la seed, el atacante obtiene las claves necesarias para gastar los fondos.
La blockchain, paradójicamente, ayuda a comprobar si una hipótesis de seed es correcta.
¿Por qué tardaron cinco años?
El bug entró en la ruta de generación en marzo de 2021 y la explotación masiva se hizo visible en julio de 2026.
Eso produce una de las principales lecciones del incidente:
Una vulnerabilidad criptográfica puede permanecer dormida durante años.
Una seed débil creada en 2021 no se vuelve más segura porque hayan pasado cinco años.
Un atacante puede:
descubrir el bug en 2026
↓
reconstruir seeds creadas en 2021
↓
buscar sus direcciones
↓
descubrir que todavía tienen BTC
↓
vaciar las wallets
De hecho, CoinDesk observó wallets dormidas durante años y fondos correspondientes al periodo 2021–2026.
⚠️ Actualizar el firmware NO arregla las wallets existentes
Este es probablemente el punto más importante para los usuarios afectados.
Actualizar el firmware NO repara una seed vulnerable ya existente.
El firmware nuevo corrige la generación futura.
Pero:
seed débil creada en 2022
+
firmware actualizado en 2026
=
seed sigue siendo débil
Coinkite indica que las personas afectadas deben:
- Actualizar primero el firmware.
- Generar una seed completamente nueva.
- Migrar los fondos a la nueva wallet.
Versiones recomendadas
| Dispositivo | Firmware recomendado |
|---|---|
| Mk2 / Mk3 afectados | 4.2.0 o posterior |
| Mk4 / Mk5 | 5.6.0+ |
| Q | 1.5.0Q+ |
Para Mk2/Mk3, Coinkite identifica actualmente como afectado el rango:
4.0.1 – 4.1.9
También existen versiones Edge corregidas.
Bitcoin Optech igualmente recomienda tratar las seeds afectadas sin suficiente entropía externa como comprometidas.
No eran solamente wallets
El RNG vulnerable se utilizaba para generar otros secretos.
El análisis identifica exposición potencial en elementos como:
- paper-wallet private keys;
- seeds efímeras;
- mecanismos de clonación;
- otros secretos generados mediante esa ruta.
Por eso el fallo es conceptualmente más grave que una vulnerabilidad específica de BIP-39:
Falló una primitiva fundamental de generación de secretos.
¿Cómo pudo pasar una auditoría?
Este es quizá el mejor aprendizaje para profesionales de seguridad.
El código correcto del TRNG sí estaba presente dentro del firmware.
Una revisión podía comprobar:
¿Existe el hardware RNG?
Sí.
¿Existe código que lo utiliza?
Sí.
¿Compila correctamente?
Sí.
Y aun así no descubrir:
seed generation
↓
¿qué símbolo rng_get()
resuelve realmente el linker?
↓
Yasmarang
Coinkite reconoce que las revisiones anteriores verificaron la implementación prevista, pero no comprobaron end-to-end qué implementación alcanzaba realmente la ruta de generación de la wallet entre los diferentes submódulos.
La lección para seguridad de software es importante:
Auditar funciones no basta; hay que auditar el flujo real que ejecuta el binario final.
Timeline del incidente
| Fecha | Evento |
|---|---|
| 1 marzo 2021 | Commit cambia la generación desde rng_bytes() hacia random.bytes(). |
| 17 marzo 2021 | COLDCARD firmware 4.0.0 incorpora los grandes cambios criptográficos/libNgU. |
| 29 marzo 2021 | Aparece 4.0.1. La documentación actual de Coinkite sitúa el rango Mk2/Mk3 afectado en 4.0.1–4.1.9. |
| 29–30 julio 2026 | Usuarios comienzan a descubrir drenajes y se identifica el problema de entropía. |
| 30 julio 2026 | Coinkite publica su análisis técnico y recomendaciones de migración. |
| 31 julio 2026 | CoinDesk reporta inicialmente 594 BTC / ~500 wallets / 25 minutos. Bitcoin Optech publica una alerta técnica. |
| 31 julio–1 agosto 2026 | Análisis posteriores amplían significativamente la estimación y Block publica su análisis independiente del RNG. |
La criptografía no falló. Falló la aleatoriedad.
COLDCARD no fue vulnerada porque Bitcoin fallara.
Tampoco porque alguien rompiera:
- secp256k1;
- SHA-256;
- BIP-39.
Falló algo mucho más básico:
La aleatoriedad.
Una clave privada de Bitcoin es segura porque encontrarla dentro de un espacio criptográfico suficientemente grande es computacionalmente inviable.
Pero si el dispositivo encargado de crear ese secreto reduce accidentalmente ese universo a un conjunto suficientemente pequeño o predecible, toda la criptografía posterior puede seguir funcionando perfectamente y aun así el sistema completo estar roto.
El caso COLDCARD deja una lección incómoda para cualquier sistema criptográfico:
La seguridad de una clave nunca puede ser mayor que la entropía con la que fue creada.
Y quizá la enseñanza más importante para desarrolladores:
No basta con que el CSPRNG/TRNG correcto exista en el código. Hay que demostrar mediante pruebas del artefacto final que la ruta crítica realmente lo utiliza.
Referencias
Block Bitcoin Engineering and Security. (2026, 30 de julio). Predictable RNG fallback and 32-bit reseed in COLDCARD firmware. Block Engineering.
https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware
Bitcoin Optech. (2026, 31 de julio). Bitcoin Optech Newsletter #416.
https://bitcoinops.org/en/newsletters/2026/07/31/
Coinkite Inc. (2026, 30 de julio). Technical deep dive into the entropy issue. COINKITE Blog.
https://blog.coinkite.com/entropy-technical-backgrounder/
COLDCARD. (s. f.). Version history. COLDCARD Documentation. Recuperado el 11 de agosto de 2026.
https://coldcard.com/docs/version-history/
CoinDesk. (2026, 31 de julio). Major bitcoin wallet flaw drains 594 BTC in 25-minute sweep.
https://www.coindesk.com/tech/2026/07/31/major-bitcoin-wallet-flaw-drains-594-btc-in-25-minute-sweep
Únete para comentar
Comenta, participa y desbloquea todo HackWise: cursos completos, HackTube y WiseTalks sin restricciones y contenido exclusivo.