忘记设备登录密码,用python破解securecrt的会话配置文件记录的加密密码

客户一台H3C CN3360B 光纤交换机忘记管理口登录密码,但是securecrt记住了登录密码,通过crt的session配置文件可以看到加密后的密码,但是不知道明文密码。如要修改设备登录密码必须重启设备进系统单用户来修改密码,因为是生产业务不能停机,只能尝试破解crt记录的加密密码。

我参考了一篇用python破解的博客,这是链接https://www.modb.pro/db/376525?utm_source=index_ai

我们先下载两个py包,一个是securecrt_cipher.py破解包,可以在github中下载,链接如下https://github.com/HyperSine/how-does-SecureCRT-encrypt-password/blob/master/python3/securecrt_cipher.py
在这里插入图片描述
点击下载到本地。

还有个包是pycryptodome,可以直接用pip下载
在这里插入图片描述
两个包安装好后就可以开始破解了

找到crt的对应设备连接的会话配置文件
在这里插入图片描述
找到那串加密密码字符串,字符串为u开头,破解事需要把u去掉

先测试一下securecrt_cipher.py包是否可用

第一种,要把包放在执行目录,也就是当前目录,不然会提示在当前目录找不到包
PS C:\Users\ZongYi> python  securecrt_cipher.py -h
usage: securecrt_cipher.py [-h] {enc,dec} ...

positional arguments:
  {enc,dec}
    enc       perform encrypt operation
    dec       perform decrypt operation

options:
  -h, --help  show this help message and exit
第二种,指定执行包的所在目录。
PS C:\Users\ZongYi> python D:\桌面\1\securecrt_cipher.py -h
usage: securecrt_cipher.py [-h] {enc,dec} ...

positional arguments:
  {enc,dec}
    enc       perform encrypt operation
    dec       perform decrypt operation

options:
  -h, --help  show this help message and exit

执行破解

C:\Users\ZongYi>python securecrt_cipher.py dec a150a139b0c4e09e927c20d8525afb870b3d3a32c07743eb7d41a69b8169137e
Password

这里执行结束,密码为Password

securecrt_cipher.py包的内容如下:

#!/usr/bin/env python3
import os, struct
from Crypto.Hash import SHA256, SHA512
from Crypto.Cipher import AES, Blowfish
from Crypto.Protocol import KDF

def bcrypt_hash(password: bytes, salt: bytes) -> bytes:
    password = SHA512.new(password).digest()
    salt = SHA512.new(salt).digest()

    digest = KDF._bcrypt_hash(password, 6, salt, b'OxychromaticBlowfishSwatDynamite', False)
    digest = b''.join(digest[i:i + 4][::-1] for i in range(0, len(digest), 4))

    return digest

def bcrypt_pbkdf2(password: bytes, salt: bytes, key_length: int, rounds: int) -> bytes:
    BCRYPT_BLOCKS = 8
    BCRYPT_HASHSIZE = BCRYPT_BLOCKS * 4

    out_len = (key_length + BCRYPT_HASHSIZE - 1) // BCRYPT_HASHSIZE * BCRYPT_HASHSIZE
    out = KDF.PBKDF2(password, salt, out_len, rounds, prf = bcrypt_hash)

    stride_n = (key_length + BCRYPT_HASHSIZE - 1) // BCRYPT_HASHSIZE
    return bytes(out[sum(a * b for a, b in zip(divmod(i, stride_n), (1, BCRYPT_HASHSIZE)))] for i in range(0, key_length))

class SecureCRTCrypto:

    def __init__(self) -> None:
        '''
        Initialize SecureCRTCrypto object.
        '''
        self._iv = b'\x00' * Blowfish.block_size
        self._key1 = b'\x24\xA6\x3D\xDE\x5B\xD3\xB3\x82\x9C\x7E\x06\xF4\x08\x16\xAA\x07'
        self._key2 = b'\x5F\xB0\x45\xA2\x94\x17\xD9\x16\xC6\xC6\xA2\xFF\x06\x41\x82\xB7'

    def encrypt(self, plaintext: str) -> str:
        '''
        Encrypt `plaintext` and return the corresponding ciphertext.

        Args:
            plaintext (str): An ASCII string to encrypt.

        Returns:
            str: The hexlified ciphertext string.
        '''
        plaintext_bytes = plaintext.encode('utf-16-le')
        plaintext_bytes += b'\x00\x00'

        plaintext_bytes_padded = \
            plaintext_bytes + os.urandom(Blowfish.block_size - len(plaintext_bytes) % Blowfish.block_size)

        cipher1 = Blowfish.new(self._key1, Blowfish.MODE_CBC, iv = self._iv)
        cipher2 = Blowfish.new(self._key2, Blowfish.MODE_CBC, iv = self._iv)
        return cipher1.encrypt(os.urandom(4) + cipher2.encrypt(plaintext_bytes_padded) + os.urandom(4)).hex()

    def decrypt(self, ciphertext: str) -> str:
        '''
        Decrypt `ciphertext` and return the corresponding plaintext.

        Args:
            ciphertext (str): A hex string to decrypt.

        Returns:
            str: The plaintext string.
        '''
        cipher1 = Blowfish.new(self._key1, Blowfish.MODE_CBC, iv = self._iv)
        cipher2 = Blowfish.new(self._key2, Blowfish.MODE_CBC, iv = self._iv)

        ciphertext_bytes = bytes.fromhex(ciphertext)
        if len(ciphertext_bytes) <= 8:
            raise ValueError('Bad ciphertext: too short!')

        plaintext_bytes_padded = cipher2.decrypt(cipher1.decrypt(ciphertext_bytes)[4:-4])

        null_terminator_index = -1
        for i in range(0, len(plaintext_bytes_padded), 2):
            if plaintext_bytes_padded[i] == 0 and plaintext_bytes_padded[i + 1] == 0:
                null_terminator_index = i
                break
        if null_terminator_index < 0:
            raise ValueError('Bad ciphertext: null terminator is not found.')
        else:
            padding_len = len(plaintext_bytes_padded) - (null_terminator_index + 2)
            assert(padding_len >= 0)

            if padding_len != Blowfish.block_size - (null_terminator_index + 2) % Blowfish.block_size:
                raise ValueError('Bad ciphertext: incorrect padding.')

        plaintext_bytes = plaintext_bytes_padded[0:null_terminator_index]

        try:
            return plaintext_bytes.decode('utf-16-le')
        except UnicodeDecodeError:
            raise ValueError('Bad ciphertext: not UTF16-LE encoded.')

class SecureCRTCryptoV2:

    def __init__(self, config_passphrase: str = ''):
        '''
        Initialize SecureCRTCryptoV2 object.

        Args:
            config_passphrase (str): The config passphrase that SecureCRT uses. Leave it empty if config passphrase is not set.
        '''
        self._config_passphrase = config_passphrase.encode('utf-8')

    def encrypt(self, plaintext: str, **kwargs) -> str:
        '''
        Encrypt `plaintext` and return the corresponding ciphertext.

        Args:
            plaintext (str): An ASCII string to encrypt.
            **kwargs: Some keyword arguments.

        Returns:
            str: The hexlified ciphertext string.
        '''
        plaintext_bytes = plaintext.encode('utf-8')
        prefix = kwargs.get('prefix', '03')

        if len(plaintext_bytes) > 0xffffffff:
            raise OverflowError('Bad plaintext: too long!')

        if prefix == '02':
            cipher = AES.new(SHA256.new(self._config_passphrase).digest(), AES.MODE_CBC, iv = b'\x00' * AES.block_size)
        elif prefix == '03':
            salt = os.urandom(16)
            kdf_bytes = bcrypt_pbkdf2(self._config_passphrase, salt, 32 + AES.block_size, 16)
            cipher = AES.new(kdf_bytes[:32], mode = AES.MODE_CBC, iv = kdf_bytes[32:])
        else:
            raise NotImplementedError('Unknown prefix: {}'.format(prefix))

        # lvc: l -> length, v -> value, c -> checksum
        lvc_bytes = struct.pack('<I', len(plaintext_bytes)) + plaintext_bytes + SHA256.new(plaintext_bytes).digest()

        padding_len = AES.block_size - len(lvc_bytes) % AES.block_size
        if padding_len < AES.block_size // 2:
            padding_len += AES.block_size

        ciphertext_bytes = cipher.encrypt(lvc_bytes + os.urandom(padding_len))
        if prefix == '03':
            ciphertext_bytes = salt + ciphertext_bytes

        return ciphertext_bytes.hex()

    def decrypt(self, ciphertext: str, **kwargs) -> str:
        '''
        Decrypt `ciphertext` and return the corresponding plaintext.

        Args:
            ciphertext (str): A hex string to be decrypt.
            **kwargs: Some keyword arguments.

        Returns:
            str: The plaintext string.
        '''
        ciphertext_bytes = bytes.fromhex(ciphertext)
        prefix = kwargs.get('prefix', '03')

        if prefix == '02':
            cipher = AES.new(SHA256.new(self._config_passphrase).digest(), AES.MODE_CBC, iv = b'\x00' * AES.block_size)
        elif prefix == '03':
            if len(ciphertext_bytes) < 16:
                raise ValueError('Bad ciphertext: too short!')
            salt, ciphertext_bytes = ciphertext_bytes[:16], ciphertext_bytes[16:]
            kdf_bytes = bcrypt_pbkdf2(self._config_passphrase, salt, 32 + AES.block_size, 16)
            cipher = AES.new(kdf_bytes[:32], mode = AES.MODE_CBC, iv = kdf_bytes[32:])
        else:
            raise NotImplementedError('Unknown prefix: {}'.format(prefix))

        padded_bytes = cipher.decrypt(ciphertext_bytes)

        plaintext_len, = struct.unpack('<I', padded_bytes[0:4])
        if len(padded_bytes) < 4 + plaintext_len:
            raise ValueError('Bad ciphertext: incorrect plaintext length.')

        plaintext_bytes = padded_bytes[4:][:plaintext_len]
        if len(padded_bytes) < 4 + plaintext_len + SHA256.digest_size:
            raise ValueError('Bad ciphertext: missing sha256 checksum.')

        checksum_bytes = padded_bytes[4 + plaintext_len:][:SHA256.digest_size]
        padding_bytes = padded_bytes[4 + plaintext_len + SHA256.digest_size:]

        expected_padding_len = AES.block_size - (4 + plaintext_len + SHA256.digest_size) % AES.block_size
        if expected_padding_len < AES.block_size // 2:
            expected_padding_len += AES.block_size

        if len(padding_bytes) != expected_padding_len:
            raise ValueError('Bad ciphertext: incorrect padding.')

        if SHA256.new(plaintext_bytes).digest() != checksum_bytes:
            raise ValueError('Bad ciphertext: incorrect sha256 checksum.')

        return plaintext_bytes.decode('utf-8')

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest = 'OPERATION', required = True)

    enc_subparser = subparsers.add_parser('enc', help = 'perform encrypt operation')
    dec_subparser = subparsers.add_parser('dec', help = 'perform decrypt operation')

    enc_subparser.add_argument(
        '-2', '--v2',
        action = 'store_true',
        help = 'encrypt/decrypt with "Password V2" algorithm'
    )
    enc_subparser.add_argument(
        '--prefix',
        action = 'store',
        type = str,
        choices = ['02', '03'],
        default = '03',
        help = 'the prefix of encrypted passwords generated with "Password V2" algorithm'
    )
    enc_subparser.add_argument(
        '-p', '--passphrase',
        action = 'store',
        type = str,
        help = 'the config passphrase that SecureCRT uses'
    )
    enc_subparser.add_argument(
        'PASSWORD',
        type = str,
        help = 'the plain password to encrypt'
    )

    dec_subparser.add_argument(
        '-2', '--v2',
        action = 'store_true',
        help = 'encrypt/decrypt with "Password V2" algorithm'
    )
    dec_subparser.add_argument(
        '--prefix',
        action = 'store',
        type = str,
        choices = ['02', '03'],
        default = '03',
        help = 'the prefix of encrypted passwords generated with "Password V2" algorithm'
    )
    dec_subparser.add_argument(
        '-p', '--passphrase',
        action = 'store',
        type = str,
        help = 'the config passphrase that SecureCRT uses'
    )
    dec_subparser.add_argument(
        'PASSWORD',
        type = str,
        help = 'the encrypted password to reveal'
    )

    args = parser.parse_args()

    if args.OPERATION == 'enc':
        operation = 'encrypt'
    elif args.OPERATION == 'dec':
        operation = 'decrypt'
    else:
        raise NotImplementedError('Unknown operation: {}'.format(args.OPERATION))

    if args.v2:
        cipher = SecureCRTCryptoV2() if args.passphrase is None else SecureCRTCryptoV2(args.passphrase)
        print(getattr(cipher, operation)(args.PASSWORD, prefix = args.prefix))
    else:
        cipher = SecureCRTCrypto()
        print(getattr(cipher, operation)(args.PASSWORD))
  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值