Este tipo de Ataque tiene ciertas condiciones iniciales para ser llevado acabo.
El cliente solo puede saber si su paquete fue aceptado o no.
Esto es debido al check que hace el servidor sobre el mensaje recibido
El servidor no Cambia de KEY utilizada durante el proceso de cifrado y descifrado.
Esto es debido a una mala implementación, ya que el servidor debería de renovar el KEY cada X tiempo y con cada cliente distinto.
El servidor tiene algún leak de información ya sea por error o mediante otro tipo de ataque.
El cliente solo podrá descifrar Una parte de la información, excepto por el Bloque inicial
Dejo a continuación una imagen de prueba y el código, proximamente subire un video hablando del tema.
Codigo, este codigo ejemplifica el cliente y servidor mediante un hilo distinto, lo hice de esta manera para no complicarme con el protocolo en RED de los mismo, se puede hacer sin hilos, y solo con llamadas a funcion, pero la idea es garantizar que el cliente no tiene acceso al servidor.
No sabemos qué pasará en el futuro, es decir no sabemos si nos robaran la cartera o la perderemos en algún USB y alguien la encontrara. Tampoco sabemos la cantidad de poder de computo que existira.
Pero además de tener cifrada nuestra cartera con un buen passphrase de más de 40 caracteres, se puede lograr una mejor seguridad, para en el caso de que sea robada sea inviable que por medio de fuerza bruta directo contra el passphrase.
Nuestra Cartera está protegida con el resultado de un hash sha512 derivado N veces a partir de nuestro Passphrase y un salt.
Normalmente el cliente bitcoin-core calcula ese N de tal forma que la Operación en total no lleve más de 1 Segundo
¿Por que? ¿Por que ponérsela fácil a los crackers de wallets?
Se puede editar el código fuente del bitcoin-core de tal forma que cuando nosotros cambiemos el passphrase este utilice un número N tal que N el proceso completo de PBKDF2_algo lleve al menos uno o dos minutos por passphare, es un pequeño precio de espera. Y el resultado será sumamente desalentador para quien se robe o encuentre la cartera.
¿Como hacer esta modificacion?
Si revisamos la versión estable y actual al dia de hoy, el bitcoin core 0.20 el archivo wallet.cpp tiene la solución:
Si quitamos todo lo relacionado con el calculo del tiempo y agregamos la linea:
Código
pMasterKey.second.nDeriveIterations=133707331;
En mi caso con el bitcoin-core oficial recompilado y con un procesador Intel Xeon CPU E3-1271 v3 @ 3.60GHzcambiar el passphrase toma alrededor de un minuto
Bueno esta investigación comenzó a manera de broma y solo por hobby. En las platicas que tenemos en la comunidad de elhacker.net en Telegram ( https://t.me/elhackerdotnet )
Se menciono hace tiempo la existencia de una cartera de bitcoin con 69 Mil BTC, al tipo de cambio actual hoy 4 de Noviembre de 2020 serian unos 995 millones de dólares, entre bromas se menciono que teníamos que crackearla con algún computador cuántico.
En fin, el proceso "normal" para abrir una cartera cifrada utilizando las aplicaciones oficiales de Bitcoin-Core es el siguiente.
Primero cargamos la cartera
Código:
bitcoin-cli loalwallet "wallet.dat"
"wallet.dat" es un archivo que debe de existir en el directorio ~/.bitcoin/wallets/
Se puede llamar de otro modo, el punto es que debe de exisitir en ese path
Segundo desbloqueamos la cartera con nuestra passphrase o mas comúnmente contraseña
Código:
bitcoin-cli walletpassphrase "passphrase o password" 60
Entre comillas tenemos nuestro password y el 60 a continuación indica que desbloqueamos la cartera por 60segundos
Si el password es correcto no marcara error, de lo contrario lo indicará.
Código:
error code: -14 error message: Error: The wallet passphrase entered was incorrect.
Ahora la forma lenta e ineficiente de intentar crackearla por fuerza fruta probando directamente distintos passphrase desde la misma terminal. Sigue siendo lento incluso aunque se utilice algún script bash por que pasar del bash al programa y de regreso es ineficiente.
Necesitamos saber que hace internamente la aplicación, para tratar de reproducirlo en un programa en C y ejecutarlo por aparte posiblemente con multihilo para mejorar la eficiencia
¿Que hace el bitcoin core con nuestro passphrase?
Nuestro passphrase es combinado con un salt que se encuentra almacenado en el archivo, mediante un algoritmo estilo PBKDF y con mas de 25000 iteraciones generan un hash sha512.
De estos 64 bytes generados mediante PBKDF, se utilizan los primeros 32 bytes como KEY para inicializar el contexto de AES y los siguientes 16 bytes como IV para el algoritmo CBC
A este algoritmo AES256CBC se le pasa como parámetro para descifrar el mKey (master KEY) cifrado y se obtiene un mKey Descifrado
El master key ya descifrado se le hacen varias pruebas, se utiliza este valor como KEY para otros descifrados y en este caso como IV en todas las pruebas se utilizan 16 bytes del public Key de la cartera bitcoin obtenidos por una función interna llamada GetHash que desconozco que valores devuelva exactamente, solamente me limite a llamarla.
Y si el master key ya descifrado pasa todas las pruebas se almacena en memoria para sea utilizado en el futuro.
Fin de la triste Historia.
Resumen en pseudo codigo
El siguiente codigo en Pseudo C esta solo para representare que hace la aplicación internamente.
Código
prekey = PBKDF(passphrase,IV, N Iteraciones,"sha512");
Entonces si nuestro plan es un ataque por fuerza bruta podemos saltarnos el PBKDF que se ejecuta N Iteraciones (Mínimo 25000) y saltar directamente al Decrypt de AES.
Tenemos 2 opciones:
1.- Generar un par (KEY,IV) 48 bytes random o secuencial y empezar con el primer AES256CBC_decrypt y utilizar el valor generado para continuar con los AES256CBC_decrypt dentro del for.
2.- O generar solamente un KEY de 32 bytes random o secuencial y pasar directamente a los AES256CBC_decrypt dentro del For.
Asi con 32 bytes solo tenemos 1 de 115792089237316195423570985008687907853269984665640564039457584007913129639936 posibilidades de dar con la KEY correcto. xD xD xD
Resultados vistos en la práctica.
Con la opción 1 solamente el 0.4% de los valores aleatorios generados pasaban el primer AES256CBC_decrypt posteriormente solo el 0.4% de esos valores pasaban el primer AES256CBC_decrypt dentro del FOR
Para la cartera que cree con el propósito de realizar las pruebas los challenge dentro del FOR eran sobre 500.
Sin embargo para la cartera lackeada con 69K BTC solo está disponible un solo challenge dentro del FOR, al ser solo dos AES256CBC_decrypt me dio bastantes KEY "Falsos positivos" aproximadamente uno de cada millón de valores random generados (KEY,IV) pasaban ambas pruebas
En conclusión
La aproximación por Fuerza Bruta a AES256CBC es improbable que funcione ojo, improbable no imposible, Tal vez en el futuro con mejor poder de computo disponible, o tal vez con Múltiples equipos trabajando de forma sincronizada con alguna RED tipo BOINC o algún tipo de BOTNET
Tengo el código utilizado para realizar este proceso, un poco de manera hardcodeada Ya que los Valores cifrados los saque directamente mediante Hexedit, si alguien esta interesado en la estructura del archivo no dude en comentarlo.
Anteriormente no me había fijado la salida del descifrado por AES 256 CBC con Pad habilitado.
Normalmente uno solo espera la data original descifrada, vamos que si ciframos la palabra "hola" esperamos de vuelta la misma palabra "hola" ya descifrada.
Cuando encriptas sin padding cada bloque del tamaño "AES_BLOCKSIZE" devuelve la misma cantidad de bloques. Pero el cifrar con Padding agrega cierta cantidad mas de datos.
Por ejemplo si ciframos 32 bytes con AES256CBC con Padding nos devuelve un buffer con 48 bytes de data
Y cuando desciframos esos 48 bytes, nos devuelve la data original de 32 bytes + un buffer "sucio" es decir que hay mas datos en el buffer, en mi caso he comprobado que para este ejemplo siempre devuelve un buffer sucio de 16 bytes y cada uno de esos bytes tiene valor de uno.
Mi pregunta es ¿Es normal esto, o solo es la forma en la que trabaja la librería ctaes?
hice un programa que muestra que independientemente del key y del IV utilizados siempre pasa lo mismo
Código:
albertobsd $ ./test_aes256cbc key: fd792d4458dbc9bfee589482273ae061a37e24a72e95a0a5fba17109e4cb1daf iv: 7d5559d5e50e340bb66618ceaad7ed1b Data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA len 48 cipher data 1117fcb2cbb27ee2f735ce4083d0aea743b51f6b7f61f59ce5a27a78bb5d454eab8b6a1733a5ad1d07b0b08ba1732e04 len 32 decipher data 414141414141414141414141414141414141414141414141414141414141414110101010101010101010101010101010 albertobsd $ ./test_aes256cbc key: 1049354727fa2affd4410da40870f1757e211efeb96349b8576157c101fe5ab0 iv: 49547b6aac189b8487f60157d13185df Data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA len 48 cipher data e980ef82804a6fe5bec15dda0ad50064457c65259cd810055c38eb7c55e1d40071646c7c792e6d5a7ac6597057868267 len 32 decipher data 414141414141414141414141414141414141414141414141414141414141414110101010101010101010101010101010 albertobsd $ ./test_aes256cbc key: 6d40ce0be48da5fcc7ede6531dae1b3613e5931a808e1ae99928ab74f74f3685 iv: ec1cebfa7894563a8329aa797610841c Data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA len 48 cipher data 395976ca9f00ace59ba64e8a1ee5dbaf55f45e786fada6520148d82a84c298e15b2854763a2fc82e7a62164936bf8f1f len 32 decipher data 414141414141414141414141414141414141414141414141414141414141414110101010101010101010101010101010
De ser normal esto se podría tomar ese buffer sucio como una comprobación de que la key y el iv utilizados son los correctos?
Publico este tema aquí, ya que no se en qué sección queda mejor publicarlo.
Estoy intentando extraer la información Cifrada de una cartera bitcoin, en específico la información del Key para posteriormente intentar descifrarlo por algún ataque de fuerza bruta.
Quiero extraerla por que no quiero depender del bitcoin core ya que si se realiza el ataque por fuerza bruta mediante algún script bash o algun otro metodo NO compilado, el ataque puede ser realmente ineficiente.
También quiero brincarme algunos pasos innecesarios de funciones tipo rehash o PBKDF2 (key derivation) ya que aunque no consumen mucho CPU si quitan algunos miles de ciclos de procesamiento que a la larga pueden hacer aun mas eterno el proceso de crackeo.
Segun he leido la informacion del Key cifrado el salt y el numero de iteraciones esta contenida en un campo de 64 bytes del wallet.dat llamado mkey.
mkey - a size prefixed string denoting the start of the master key data.
nID - an integer whose purpose I have been unable to determine.
encrypted_key - a size prefixed buffer containing the encrypted master key.
salt - a size prefixed buffer containing the salt data used when encrypting the key.
derivation_method - an integer containing either 0 or 1 for the method used to encrypt the master key using the wallet password.
derivation_rounds – an integer containing the number of rounds used to encrypt the master key.
other_derivation_parameters
mkey es solo el ascii "mkey", se que el encrypted_key debe de ser de 32 bytes y el salt de 16, restando solo 12 bytes para los otros datos, sin embargo no estoy seguro del orden de los mismos.
Si vemos la siguiente imagen:
Vemos que ahí esta la data, escribí un pequeño programa que la encuentra y la extrae, pero no se en que orden este, con ver los datos de esta cartera y otras me doy cuenta de algunos datos que pueden ser números como los primeros 8 bytes después del mkey, pueden ser números, al igual que los últimos 4 antes de ckey.
Edit
Solucionado el master key cifrado son 48 bytes que se encuentran a un offset de -72 bytes de la primera coincidencia del string mkey.
Para esta cartera en cuestión el Master Key Cifrado es:
Todo esto viene del análisis que estoy realizando al código fuente del Bitcoin Core.
Hoy hablaré sobre la función BytesToKeySHA512AES la cual es una de las primeras que se utilizan cuando intentas desbloquear una cartera bitcoin protegida con un passphrase.
BytesToKeySHA512AES básicamente toma las passphrase ingresada + un salt proporcionado por el mismo archivo wallet.dat y los transforma en un (key, iv) para posteriormente utilizarlos como material para descifrar la llave cifrada.
El código fuente de BytesToKeySHA512AES, lo pueden encontrar en
Tiene 3 parámetros de entrada y 2 de salida Entrada: Salt Passphrase Count
Salida: Key IV
La parte interesante del código es la siguiente:
Código
for(int i =0; i != count -1; i++)
di.Reset().Write(buf,sizeof(buf)).Finalize(buf);
Básicamente obtiene el hash sha512 de si mismo (count -1) veces ya la que primera vez fue el hash sha512 de (passphrase + IV) como muestra el siguiente código:
Lo escribí en PHP ya que es mas fácil darle seguimiento a las funciones hash, solo que no estoy 100% seguro de como manejar las copia de los bytes del hash resultante al key y al IV respectivamente.
Muy buena dia, estoy con una pequeña duda ya que estoy tratando de automatizar un proceso de crackeo por fuerza bruta de una key de 256 bits (Se que es improbable, sin embargo la clave esta ahi del 0 al 2^256)
La función original recibe 2 parametros un un KeyCifrada, y el candidato a Key como segundo parametro:
Código
Decrypt(KeyCifrada,KeyCandidato);
Dicha función realiza un llamado internamente a AES256CBCDecrypt
y si nLen es igual a 0 entonces retorna false indicando que Key Candidato no es un Key valido.
Mi duda es la siguiente como determina internamente AES256CBCDecrypt que la KeyCandidato es valida.
Saludos Edit Platicando con Kub0x me corrigio el KeyCandidato no es parámetro de entrada si no de salida
Si vemos el link
Código:
https://fabcoin.pro/aes_8cpp_source.html#l00176
en la linea 112 hay se muestra que las comprobaciones las hace directamente sobre el texto descifrado y si alguna de ellas no pasa simplemente regresa 0.
Me comenta kub0x que existen varios valores Key y IV de entrada que podrian dar una salida valida, siendo esto un falso positivo, sin embargo me gustaría investigar cuantas veces pasa eso en realidad.
Saludos
Edit 2 Tal como menciono Kub0x, el proceso arroja muchos falsos positivos.
Programe un programa multihilo para crackear un key
Con solo 4 Hilos y solo probando 1554 hashes random me dio 4 resultados que la función AES256CBCDecrypt considera válidos, pense que serian mucho menos el ratio de falsos positivos, pero asi no me da tiempo de probarlos manualmente, tendrás que optimizar ese proceso también.
De momento funciona bien cuando hago peticiones mediante curl incluso soporta un ciclo sin fin de peticiones:
Código
whiletrue; do curl -i http://localhost:3002/ ; done
Sin embargo cuando le hago las peticiones desde cualquier otro navegador que procese todos los archivos y CIERRO el navegador a media carga el programa simplemente se sale sin mensaje de error.
OJO que solo es cuando cierro a media carga, si espero a que termine de cargar la pagina por completo esto no sucede.
Consideraciones:
Estoy totalmente seguro de que la mayoría de las funciones que pueden dar error están correctamente procesadas y muestran el error correspondiente:
Código
s = pthread_create(&tid,&attr,thread_process,(void*)tothread);
También estoy seguro que no es un problema de memoria ya que tengo una libreria propia con la que me he asegurado tras varias pruebas de que todos los apuntadores asignados con malloc/calloc son liberados y no se vuelven a utilizar, ademas de indicarme la cantidad de memoria dinamica actualmente utilizada.
De igual manera todos los sockets son cerrados mediante:
Código
shutdown(hsc->client_fd,SHUT_RDWR);
close(hsc->client_fd);
Se que el problema tiene que ver con Sockets ya que solo cuando cierro el navegador a media carga el programa termina.
Me he quedado un poco estancado, ya que siento que no avanzo si no soluciono ese error.
No les pido que depuren mi codigo, ya que no esta 100% documentado.
Pregunto: ¿Alguien a tenido el mismo problema?, ¿Que otros motivos hacen que el programa se cierre y no caiga error en ninguna función?
Y asi le sigue por unos cuantos Kilobytes de codigo minificado.
Entonces esa es la pregunta, cual es la mejor forma de depurar que es lo que esta haciendo, pregunto por que javascript es bastante permisivo con la sintaxis.
Obvio primer paso: -- Utilizar alguna de esas paginas para que el código se vea indentado y tabulado