在Windows 10环境下使用Python恢复SecureCRT保存的密码

作为一名经常需要远程连接服务器的运维工程师,SecureCRT无疑是日常工作中不可或缺的工具。但你是否遇到过这样的情况:某天需要紧急连接一台许久未登录的服务器,却发现当初保存在SecureCRT中的密码已经记不清了?这种时刻总是让人焦虑不已。本文将详细介绍如何通过Python脚本找回SecureCRT中保存的密码,让你在紧急情况下能够快速恢复连接。

1. 准备工作与环境配置

1.1 安装Python运行环境

首先需要确保系统中安装了正确版本的Python。SecureCRT的密码解密脚本需要Python 3.x环境,Python 2.x将无法正常运行。

# 检查Python版本
python --version

如果尚未安装Python 3.x,可以从 Python官网 下载最新版本。安装时务必勾选"Add Python to PATH"选项,这样可以直接在命令行中使用python命令。

1.2 安装必要的加密库

SecureCRT使用Blowfish和AES算法加密存储的密码,因此我们需要安装pycryptodome库来处理这些加密算法。

pip install pycryptodome

如果遇到pip命令不可用的情况,可能是因为Python的Scripts目录没有添加到系统环境变量中。可以手动添加,路径通常类似于:

C:\Users\你的用户名\AppData\Local\Programs\Python\Python310\Scripts

2. 获取SecureCRT配置文件

2.1 定位配置文件位置

SecureCRT将每个会话的连接信息保存在单独的配置文件中,这些文件通常位于:

C:\Users\你的用户名\AppData\Roaming\VanDyke\Config\Sessions\

每个会话对应一个.ini文件,文件名通常是服务器IP地址或你设置的会话名称。

2.2 查找加密密码

打开对应的.ini文件,查找包含"Password"或"Password V2"的字段。加密后的密码通常以"S:"或"02:"开头的一长串字符。

例如:

Password V2=02:7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000

3. 使用Python脚本解密密码

3.1 解密脚本解析

SecureCRT使用两种加密方式:

  • 旧版本使用Blowfish算法(标记为"S:")
  • 新版本使用AES-256算法(标记为"02:")

以下是完整的解密脚本:

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

class SecureCRTCrypto:
    def __init__(self):
        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 Decrypt(self, Ciphertext : str):
        cipher1 = Blowfish.new(self.Key1, Blowfish.MODE_CBC, iv = self.IV)
        cipher2 = Blowfish.new(self.Key2, Blowfish.MODE_CBC, iv = self.IV)
        ciphered_bytes = bytes.fromhex(Ciphertext)
        if len(ciphered_bytes) <= 8:
            raise ValueError('Invalid Ciphertext.')
        padded_plain_bytes = cipher2.decrypt(cipher1.decrypt(ciphered_bytes)[4:-4])
        i = 0
        for i in range(0, len(padded_plain_bytes), 2):
            if padded_plain_bytes[i] == 0 and padded_plain_bytes[i + 1] == 0:
                break
        plain_bytes = padded_plain_bytes[0:i]
        try:
            return plain_bytes.decode('utf-16-le')
        except UnicodeDecodeError:
            raise(ValueError('Invalid Ciphertext.'))

class SecureCRTCryptoV2:
    def __init__(self, ConfigPassphrase : str = ''):
        self.IV = b'\x00' * AES.block_size
        self.Key = SHA256.new(ConfigPassphrase.encode('utf-8')).digest()

    def Decrypt(self, Ciphertext : str):
        cipher = AES.new(self.Key, AES.MODE_CBC, iv = self.IV)
        padded_plain_bytes = cipher.decrypt(bytes.fromhex(Ciphertext))
        plain_bytes_length = int.from_bytes(padded_plain_bytes[0:4], 'little')
        plain_bytes = padded_plain_bytes[4:4 + plain_bytes_length]
        if len(plain_bytes) != plain_bytes_length:
            raise ValueError('Invalid Ciphertext.')
        plain_bytes_digest = padded_plain_bytes[4 + plain_bytes_length:4 + plain_bytes_length + SHA256.digest_size]
        if len(plain_bytes_digest) != SHA256.digest_size:
            raise ValueError('Invalid Ciphertext.')
        if SHA256.new(plain_bytes).digest() != plain_bytes_digest:
            raise ValueError('Invalid Ciphertext.')
        return plain_bytes.decode('utf-8')

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3 or sys.argv[1].lower() not in ['dec']:
        print('Usage: python SecureCRTCipher.py dec [-v2] [-p ConfigPassphrase] <ciphertext>')
        sys.exit(1)
    
    use_v2 = '-v2' in sys.argv
    passphrase = ''
    if '-p' in sys.argv:
        passphrase = sys.argv[sys.argv.index('-p') + 1]
    
    ciphertext = sys.argv[-1]
    if ciphertext.startswith('S:'):
        ciphertext = ciphertext[2:]
        print(SecureCRTCrypto().Decrypt(ciphertext))
    elif ciphertext.startswith('02:'):
        ciphertext = ciphertext[3:]
        print(SecureCRTCryptoV2(passphrase).Decrypt(ciphertext))
    else:
        print('Error: Unrecognized ciphertext format')

3.2 执行解密操作

将上述脚本保存为SecureCRTCipher.py文件,然后在命令行中执行:

# 对于旧版加密(S:开头)
python SecureCRTCipher.py dec "S:7F454C46020101000000000000000000"

# 对于新版加密(02:开头)
python SecureCRTCipher.py dec -v2 "02:7F454C46020101000000000000000000"

如果SecureCRT设置了配置密码(Config Passphrase),需要添加-p参数:

python SecureCRTCipher.py dec -v2 -p "your_passphrase" "02:7F454C46020101000000000000000000"

4. 常见问题与解决方案

4.1 脚本执行错误排查

错误现象 可能原因 解决方案
ModuleNotFoundError: No module named 'Crypto' pycryptodome未正确安装 执行 pip install pycryptodome
python: can't open file 'SecureCRTCipher.py' 脚本不在当前目录 使用cd命令切换到脚本所在目录
Invalid Ciphertext 密码格式不正确 确保只传入"S:"或"02:"后面的部分
UnicodeDecodeError 使用了错误的解密方式 确认加密版本并使用对应的解密方式

4.2 安全注意事项

  1. 仅限合法用途 :此方法仅应用于恢复自己遗忘的密码,未经授权解密他人密码是违法行为
  2. 保护配置文件 :SecureCRT配置文件包含敏感信息,操作完成后应及时删除临时文件
  3. 密码管理建议 :考虑使用专业的密码管理工具,避免依赖客户端软件的密码记忆功能

提示:定期导出重要服务器的连接信息并安全存储,可以避免因密码遗忘导致的服务中断。

5. 进阶技巧与自动化

5.1 批量解密多个会话密码

如果需要恢复多个会话的密码,可以编写一个简单的批处理脚本:

import os
import configparser

def decrypt_all_sessions(sessions_dir):
    crypto = SecureCRTCrypto()
    crypto_v2 = SecureCRTCryptoV2()
    
    for filename in os.listdir(sessions_dir):
        if filename.endswith('.ini'):
            config = configparser.ConfigParser()
            config.read(os.path.join(sessions_dir, filename))
            
            if 'Session' in config and 'Password' in config['Session']:
                password = config['Session']['Password']
                if password.startswith('S:'):
                    print(f"{filename}: {crypto.Decrypt(password[2:])}")
                elif password.startswith('02:'):
                    print(f"{filename}: {crypto_v2.Decrypt(password[3:])}")

decrypt_all_sessions(r'C:\Users\你的用户名\AppData\Roaming\VanDyke\Config\Sessions')

5.2 与SecureCRT集成

可以将解密功能集成到SecureCRT的脚本系统中,创建一个自定义菜单项,在需要时直接显示密码:

# SecureCRT Python脚本
import os
from SecureCRT import *

def OnConnect():
    tab = crt.GetScriptTab()
    config = tab.Session.Config
    if "Password" in config:
        password = config.GetOption("Password")[1]
        if password.startswith("S:"):
            decrypted = SecureCRTCrypto().Decrypt(password[2:])
            crt.Dialog.MessageBox(f"Password: {decrypted}")
        elif password.startswith("02:"):
            decrypted = SecureCRTCryptoV2().Decrypt(password[3:])
            crt.Dialog.MessageBox(f"Password: {decrypted}")

crt.ScriptOnConnect += OnConnect

在实际工作中,我多次遇到需要恢复旧服务器密码的情况。这种方法不仅节省了大量时间,更重要的是避免了因无法连接服务器而导致的服务中断。记得有一次,一台关键业务服务器突然需要紧急维护,但负责该服务器的同事已经离职,密码也没有交接。正是通过这种方法,我们才得以在短时间内恢复访问权限,避免了更严重的事故发生。

更多推荐