
Este puede sonar a posteo para nardos, pero les aseguro que, aunque no sepas nada de programación, tiene que ver con la seguridad de las contraseñas de todos.
Hace muchos, muchos años me había comprado un libro de programación en PHP 3 y aprendí qué eran los hashes MD5, una idea genial que permitía evitar guardar contraseñas en una base de datos (error muy común de principiantes) y guardar una "firma" que dejara esa contraseña.
Para comienzos de los 2000 parecía una idea excelente, pero para 2004 ya se habían registrado "colisiones" de forma regular y para 2013 el algoritmo estaba completamente roto, se había vuelto extremadamente fácil identificar contraseñas a partir del hash de apenas 128 bits.
En este mismo blog, en 2002, comencé guardando así las contraseñas, en aquellos tiempos los otros algoritmos eran muy pesados para el servidor, así que usé ese método sin estar enterado de las colisiones.
Años después cambié de método, pero muchas contraseñas de usuarios que nunca volvieron a entrar quedaron en MD5. ¿Cuan resistentes son? Respuesta rápida: nada.
Si te has logueado en el blog en los últimos meses tu contraseña está tan segura como es posible, uso el algoritmo bcrypt como muchos otros y obviamente en algún futuro deberá actualizarse a lo que se use en ese momento.
En la base de datos conviven tres métodos, los viejos MD5, que apenas entran son convertidos a la nueva versión, los del medio con sha512 (que no era lo ideal para contraseñas, no es "salteable", mala elección mía) y ahora los bcrypt que es mucho más resistente a ataques.
Ahora bien ¿Cuántos usuarios quedaron en MD5? un montón, en una época en la que los blogs eran la única red social aquí convivían varios miles de personas a diario, así que tengo unos 2275 usuarios (la mitad) que permanecen con esas contraseñas viejas y no creo que vuelvan nunca más o seguramente se olvidaron su usuario.
Así que me puse manos a la obra para ver cuánto tiempo tardaría en crackearlas ¿Por qué? Pues bien, leí un artículo que comentaba que el 60% de las contraseñas guardadas como hash MD5 se podían identificar en menos de una hora. Suena a desafío inclusive, aunque ahí hablaban de una placa de video RTX 5090 para hacerlo, yo no tengo tanto.
La aplicación a utilizar se llama Hashcat (hay muchos otros) y es perfecta para este tipo de ataques, de hecho, está hecha para esto mismo que vamos a hacer😁.
Primero vamos a verificar qué GPUs tengo disponibles y qué capacidad brindan.
En mi caso la notebook trae un GPU integrado al CPU AMD que es una Radeon super limitada y luego una NVidia RTX 3060 con 6GB de VRAM que es la que, obviamente, hará el trabajo pesado. Hashcat igualmente usa las dos y las combina, 100% de tu capacidad para hackear tus hashes 😅
Para testear:
.\hashcat.exe -b -m 0El resultado:
OpenCL API (OpenCL 3.0 CUDA 13.0.97) - Platform #1 [NVIDIA Corporation] ======================================================================= * Device #01: NVIDIA GeForce RTX 3060 Laptop GPU, 6143/6143 MB (1535 MB allocatable), 30MCU OpenCL API (OpenCL 2.1 AMD-APP (3628.0)) - Platform #2 [Advanced Micro Devices, Inc.] ===================================================================================== * Device #02: AMD Radeon(TM) Graphics, 7789/15579 MB (6403 MB allocatable), 6MCU Benchmark relevant options: =========================== * --backend-devices-virtmulti=1 * --backend-devices-virthost=1 * --optimized-kernel-enable ------------------- * Hash-Mode 0 (MD5) ------------------- Speed.#01........: 21084.0 MH/s (71.15ms) @ Accel:192 Loops:1024 Thr:256 Vec:8 Speed.#02........: 4977.2 MH/s (89.93ms) @ Accel:288 Loops:1024 Thr:256 Vec:1 Speed.#*.........: 26061.2 MH/sBien, esto me permitiría, entre ambos GPU, procesar 26.000 millones de hashes por segundo, es una barbaridad, pero tengan en cuenta que hablamos de fuerza bruta, lo que puede parecer mucho, en realidad, puede ser poco para ciertos trabajos.
Bueno, ahora necesitamos dos listados, por un lado creé una "hashes.txt" con unos 2275 hash MD5 heredados de la historia del blog, por el otro una lista que se puede descargar de Seclists , en mi caso usé una vieja y conocida "rockyou.txt" que pesa unos 136Mb descomprimida. Tiene unos 8 años de vieja, así que ni siquiera son passwords modernos.
Así que ejecutamos la primera pasada para ver qué sucede:
.\hashcat.exe -m 0 -a 0 hashes.txt rockyou.txt -r rules\best66.rule Session..........: hashcat Status...........: Exhausted Hash.Mode........: 0 (MD5) Hash.Target......: hashes.txt Time.Started.....: Thu May 28 01:11:14 2026 (4 secs) Time.Estimated...: Thu May 28 01:11:18 2026 (0 secs) Kernel.Feature...: Pure Kernel (password length 0-256 bytes) Guess.Base.......: File (rockyou.txt) Guess.Mod........: Rules (rules\best66.rule) Guess.Queue......: 1/1 (100.00%) Speed.#01........: 155.9 MH/s (6.54ms) @ Accel:146 Loops:64 Thr:64 Vec:1 Speed.#02........: 86261.4 kH/s (8.44ms) @ Accel:229 Loops:64 Thr:64 Vec:1 Speed.#*.........: 242.1 MH/s Recovered........: 1027/2275 (45.14%) Digests (total), 1016/2275 (44.66%) Digests (new) Remaining........: 1248 (54.86%) Digests Recovered/Time...: CUR:N/A,N/A,N/A AVG:N/A,N/A,N/A (Min,Hour,Day) Progress.........: 946729344/946729344 (100.00%) Rejected.........: 0/946729344 (0.00%) Restore.Point....: 14103190/14344384 (98.32%) Restore.Sub.#01..: Salt:0 Amplifier:64-66 Iteration:0-64 Restore.Sub.#02..: Salt:0 Amplifier:64-66 Iteration:0-64 Candidate.Engine.: Device Generator Candidates.#01...: 0889j -> amosamos Candidates.#02...: 44498 -> 889j889j Hardware.Mon.#01.: Temp: 60c Util: 25% Core:1425MHz Mem:7001MHz Bus:8 Hardware.Mon.#02.: Temp: 0c Fan: 0% Util: 0% Core: 400MHz Mem:2400MHz Bus:16Éxito: Logré romper unas 1027/2275 (45.14%) en tan sólo 4 segundos 🤪 así que no fue precisamente difícil, ahora bien, lo bueno de Hashcat es que podemos usar "reglas" distintas para filtrar de otra manera y ver si salen más.
Por esto dije "primera" pasada porque lo ideal es ir poniendo reglas cada vez más estrictas para extraer el mayor "jugo" posible de los hashes que estamos violentando.
Decidí ir por una de las reglas más fuertes, rockyou-30000.rule, que es una barbaridad de cambios que hace sobre el diccionario usado.
Por ejemplo para que se entienda qué hacen las reglas, si tenemos la contraseña "password" esta se puede escribir de numerosas formas:
password (original)
Password (capitalize)
PASSWORD (uppercase)
password1 (append 1)
password123 (append 123)
p@ssw0rd (leetspeak)
P@ssw0rd (capitalize + leetspeak)
password! (append symbol)
drowssap (reverse)
password2024 (append year)
Password1! (combo típica corporativa)
... y cientos más
Las reglas indican cómo hacer todas estas permutaciones y por cada palabra que hay en el diccionario probar miles de combinaciones más, así que no vamos a tardar cuatro segundos sino varios minutos y hasta horas, como en mi caso son apenas 2275 hashes a investigar obviamente será un trabajo relativamente corto, pero aun con mis GPUs disponibles esto lo pone todo al 100% (ni se imaginan como se pusieron los ventiladores de la notebook).
El resultado fue definitivamente mejor:
Session..........: hashcat Status...........: Exhausted Hash.Mode........: 0 (MD5) Hash.Target......: hashes.txt Time.Started.....: Thu May 28 01:18:53 2026 (4 mins, 30 secs) Time.Estimated...: Thu May 28 01:23:23 2026 (0 secs) Kernel.Feature...: Pure Kernel (password length 0-256 bytes) Guess.Base.......: File (rockyou.txt) Guess.Mod........: Rules (rules\rockyou-30000.rule) Guess.Queue......: 1/1 (100.00%) Speed.#01........: 1263.2 MH/s (12.75ms) @ Accel:36 Loops:256 Thr:64 Vec:1 Speed.#02........: 302.8 MH/s (18.67ms) @ Accel:91 Loops:256 Thr:64 Vec:1 Speed.#*.........: 1566.0 MH/s Recovered........: 1498/2275 (65.85%) Digests (total), 471/2275 (20.70%) Digests (new) Remaining........: 777 (34.15%) Digests Recovered/Time...: CUR:32,N/A,N/A AVG:104.74,N/A,N/A (Min,Hour,Day) Progress.........: 430331520000/430331520000 (100.00%) Rejected.........: 0/430331520000 (0.00%) Restore.Point....: 14281931/14344384 (99.56%) Restore.Sub.#01..: Salt:0 Amplifier:29952-30000 Iteration:0-256 Restore.Sub.#02..: Salt:0 Amplifier:29952-30000 Iteration:0-256 Candidate.Engine.: Device Generator Candidates.#01...: bteamosunlaw.86 -> $HEX[042a0369c2a156616d6f7321033832] Candidates.#02...: b0253413386 -> .teian.82 Hardware.Mon.#01.: Temp: 83c Util: 70% Core:1905MHz Mem:7001MHz Bus:8 Hardware.Mon.#02.: Temp: 0c Fan: 0% Util: 0% Core: 400MHz Mem:2400MHz Bus:16Unas 1498/2275 (65.85%) fueron identificadas, una mejora notable que me llevó cuatro minutos y medio. Del 45% al 66%, apenas un tercio se salvó y superé lo que decía el artículo.
El programa es tan completo que en varias ocasiones me avisó por la temperatura del GPU:
"Driver temperature threshold met on GPU #1. Expect reduced performance."
Una pasada más con la regla "Dive" y conseguí otras tantas llegando a 1574/2275 (69.19%) y me cansé de esperar cuando ya iba por 13 minutos con esa última regla.
Tengan en cuenta que Hashcat retoma donde se quedó, por ende cada nueva exploración la hace sobre los hashes que no consiguió resolver del archivo que le pasé.
Como ya era exagerar mucho el último intento lo corté a la mitad (me quería ir a dormir y no fundir mi PC), pero con más paciencia y mejor GPU se puede llegar a más del 80% sin problemas, con mejores diccionarios de contraseñas filtradas, mejor.
No voy a ponerme a analizar cuánta gente usó 123456 como contraseña 😁 (124bd1296bec0d9d93c7b52a71ad8d5b es el hash de 0123456) porque es una obviedad, son contraseñas para el usuario de un blog, no una plataforma muy importante, pero sí hay algo en común intersante: la mayoría de la gente tiene contraseñas relativamente pequeñas.
Dato curioso: los usuarios más "hardcore" del blog no pude descularlos, así que seguro usaron claves más complejas y con símbolos, no es que no sean crackeables, sólo que usé un diccionario limitado.
Algunos usan de 12 caracteres o más, pero muchos otros apenas 8 y esas son muy fáciles de crackear. Aun así hasta contraseñas de 15 caracteres fueron identificadas, no se salva nadie porque MD5 es extremadamente inseguro.
Todo esto hecho con un GPU de una notebook gamer común y corriente que ni siquiera es de última generación.
Por motivos más que obvios no voy a compartirle a nadie esos hashes viejos, debería, en cambio, enviarle un correo a cada usuario para que renueve.