Cifrar y descifrar contraseñas por fuerza bruta con Python

En esta entrada vamos a ver una técnica para descifrar contraseñas por fuerza bruta a partir de su HASH en cualquiera de los formatos MD5, SHA1, SHA224, SHA256, SHA384 y SHA512.

Primero vamos a obtener el HASH de una contraseña en texto plano con todos estos formatos para poder realizar la prueba, que será el que usaremos para hacer la comparación entre credenciales.

Esta entrada está creada únicamente con fines educativos y no para el uso malintencionado o delictivo de su información. Es responsabilidad del lector hacer buen uso de esta información.


Recursos

Para identificar la contraseña haremos uso de la información de SecList publicada en el repositorio oficial de GitHub, en concreto haremos uso de la sección de passwords Common-Credentials y descargaremos el archivo 10-million-password-list-top-1000000.txt.

Esta lista se encuentra como una herramienta de Kali Linux para hacer pruebas de seguridad.

También usaremos la librería de Python hashlib.


Cifrar contraseña

Primero vamos a ejecutar los comandos en una consola para ver como funcionaría el programa paso a paso.

Consola

Para cifrar la contraseña en los diferentes formatos usaremos como base esta en texto plano. Importamos la librería y codificamos la contraseña en formato UTF-8:

>>> import hashlib
>>> pwd = 'q1w2e3r4t5'        
>>> pwd
'q1w2e3r4t5'
>>> clave = pwd.encode('utf-8')
>>> clave
b'q1w2e3r4t5'

Ahora ya podemos obtener el HASH de la contraseña en cualquiera de los formatos indicados a partir del objeto creado con la librería hashlib:

>>> hashlib.md5(clave)
<md5 HASH object @ 0x010A3EC0>

>>> dir(hashlib.md5(clave).hexdigest())
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__',
 '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__',
 '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
 '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs',
 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal',
 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle',
 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace',
 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines',
 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

Estos son los HASH de la credencial:

>>> hashlib.md5(clave).hexdigest()
'42d8aa7cde9c78c4757862d84620c335'
>>> len(hashlib.md5(clave).hexdigest()) 
32

>>> hashlib.sha1(clave).hexdigest() 
'5d70c3d101efd9cc0a69f4df2ddf33b21e641f6a'
>>> len(hashlib.sha1(clave).hexdigest()) 
40

>>> hashlib.sha224(clave).hexdigest() 
'b05843cf74926ed0dfb6af2b2c6494eeb947203bac2ce5ff1d26f617'
>>> len(hashlib.sha224(clave).hexdigest()) 
56

>>> hashlib.sha256(clave).hexdigest() 
'23b5ed29a1e8409f70644e44faebae79ae687318efd719d9af29f8496b016a81'
>>> len(hashlib.sha256(clave).hexdigest()) 
64

>>> hashlib.sha384(clave).hexdigest() 
'2b998455bbe8636fc17547414259c1b3a643e4d01f9da1d08c37ce89dfaa66b77faf532337c336e200ffd9f517a23f19'
>>> len(hashlib.sha384(clave).hexdigest()) 
96

>>> hashlib.sha512(clave).hexdigest() 
'8f225ddd400f8a0d6b36b85c6ccecc0436cea6a8e32f203fc5cef7932ffe5a0788eef1a1faf4acb307c5f831292574d6d05d3cad23f2468577b41c4c31ffc37a'
>>> len(hashlib.sha512(clave).hexdigest()) 
128

Programa

El programa a partir del cual obtenemos el HASH de una contraseña introducida en texto plano podría ser el siguiente:

import hashlib

def main():
    clave = str(input("Introduce la contraseña a cifrar: ")).encode('utf-8')

    md5 = hashlib.md5(clave).hexdigest()
    print("Hash MD5: %s" % str(md5))

    sha1 = hashlib.sha1(clave).hexdigest()
    print("Hash SHA1: %s" % str(sha1))

    sha224 = hashlib.sha224(clave).hexdigest()
    print("Hash SHA224: %s" % str(sha224))

    sha256 = hashlib.sha256(clave).hexdigest()
    print("Hash SHA256: %s" % str(sha256))

    sha384 = hashlib.sha384(clave).hexdigest()
    print("Hash SHA384: %s" % str(sha384))

    sha512 = hashlib.sha512(clave).hexdigest()
    print("Hash SHA512: %s" % str(sha512))

if __name__ == '__main__':
    main()

Esta sería la salida:

> python cifrarClave.py
Introduce la contraseña a cifrar: q1w2e3r4t5
Hash: q1w2e3r4t5
Hash MD5: 42d8aa7cde9c78c4757862d84620c335
Hash SHA1: 5d70c3d101efd9cc0a69f4df2ddf33b21e641f6a
Hash SHA224: b05843cf74926ed0dfb6af2b2c6494eeb947203bac2ce5ff1d26f617
Hash SHA256: 23b5ed29a1e8409f70644e44faebae79ae687318efd719d9af29f8496b016a81
Hash SHA384: 2b998455bbe8636fc17547414259c1b3a643e4d01f9da1d08c37ce89dfaa66b77faf532337c336e200ffd9f517a23f19
Hash SHA512: 8f225ddd400f8a0d6b36b85c6ccecc0436cea6a8e32f203fc5cef7932ffe5a0788eef1a1faf4acb307c5f831292574d6d05d3cad23f2468577b41c4c31ffc37a

Descifrar la contraseña

Para descifrar la contraseña lo que haremos es recorrer el archivo de texto descargado de SecList e iremos una a una cifrándola y comparando su HASH con el obtenido a partir de nuestra contraseña. Para ello indicaremos el tipo de encriptación que vamos a utilizar para hacer la comparación.

Este sería el programa:

import hashlib

def main():
    try:
        resolverhash = str(input("Hash a resolver: "))
        type = input("Indica el tipo de encriptación: ")

        resolvedor = open("10-million-password-list-top-1000000.txt", 'r')
        for x in resolvedor.readlines():
            a = x.strip("\n").encode('utf-8')
            if type == 'md5':
                a = hashlib.md5(a).hexdigest()
            elif type == 'sha1':
                a = hashlib.sha1(a).hexdigest()
            elif type == 'sha224':
                a = hashlib.sha224(a).hexdigest()
            elif type == 'sha256':
                a = hashlib.sha256(a).hexdigest()
            elif type == 'sha384':
                a = hashlib.sha384(a).hexdigest()
            elif type == 'sha512':
                a = hashlib.sha512(a).hexdigest()
            else:
                raise Exception('El tipo de encriptación %s no es válido.' %str(type))

            if a == resolverhash:
                print("Contraseña: %s - Has resuelto: %s - Encriptado con: %s" %(str(x.rstrip()),str(a),str(type)))
                break

    except Exception as e:
        print("Error: {}".format(e))

if __name__ == '__main__':
    main()

El resultado saría el siguiente:

>python descifrarClave.py
Hash a resolver: 42d8aa7cde9c78c4757862d84620c335
Indica el tipo de encriptación: md5
Contraseña: q1w2e3r4t5 - Has resuelto: 42d8aa7cde9c78c4757862d84620c335 - Encriptado con: md5

Conclusiones

Si dispones de un archivo de credenciales y quieres comprobar si tus contraseñas son robustas y no corren riesgo de ser obtenidas por fuerza bruta, puedes usar este método sencillo y rápido, en el que en pocos segundos comparamos entre un millón de posibles contraseñas.


Enlaces

Proyecto SecList.

Lista de credenciales comunes SecList-Common-Credentials.

SecList como herramienta de Kali Linux.