SecureCRT作为一款广泛使用的终端仿真软件,其密码存储机制经历了多次迭代升级。当前主流版本采用两种加密格式:
加密后的密码会存储在会话配置文件中,通常位于:
%APPDATA%\VanDyke\Config\Sessions\~/Library/Application Support/VanDyke/SecureCRT/Config/Sessions/~/.vandyke/SecureCRT/Config/Sessions/配置文件中的密码字段表现为以下两种形式之一:
ini复制S:"Password"=u[十六进制字符串] # 传统格式
S:"Password V2"=02:[十六进制字符串] # V2格式
解密脚本需要Python 3.6+环境,推荐使用Miniconda管理Python环境:
bash复制# 创建专用环境
conda create -n securecrt python=3.8
conda activate securecrt
# 验证Python版本
python --version
必须安装pycryptodome库(非旧版pycrypto):
bash复制pip install pycryptodome
常见安装问题解决方案:
| 错误类型 | 可能原因 | 解决方案 |
|---|---|---|
| ModuleNotFoundError | 未安装pycryptodome | 使用pip安装正确版本 |
| ImportError | 存在冲突的Crypto包 | 先卸载所有相关包:pip uninstall pycrypto pycryptodome |
| 编译错误 | 缺少开发依赖 | Windows安装VC++构建工具,Linux/macOS安装python-dev |
以下脚本支持两种加密格式,并添加了错误处理和日志功能:
python复制#!/usr/bin/env python3
import os
import sys
import logging
from Crypto.Hash import SHA256
from Crypto.Cipher import AES, Blowfish
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class SecureCRTCrypto:
"""处理传统Blowfish加密格式"""
BLOWFISH_BLOCK_SIZE = 8
def __init__(self):
self.IV = b'\x00' * self.BLOWFISH_BLOCK_SIZE
self.Key1 = bytes.fromhex('24A63DDE5BD3B3829C7E06F40816AA07')
self.Key2 = bytes.fromhex('5FB045A29417D916C6C6A2FF064182B7')
def decrypt(self, ciphertext_hex: str) -> str:
try:
cipher1 = Blowfish.new(self.Key1, Blowfish.MODE_CBC, iv=self.IV)
cipher2 = Blowfish.new(self.Key2, Blowfish.MODE_CBC, iv=self.IV)
cipher_bytes = bytes.fromhex(ciphertext_hex)
if len(cipher_bytes) <= 8:
raise ValueError("密文过短")
padded_plain = cipher2.decrypt(cipher1.decrypt(cipher_bytes)[4:-4])
# 寻找UTF-16LE字符串终止符
end_pos = 0
while end_pos < len(padded_plain) - 1:
if padded_plain[end_pos] == 0 and padded_plain[end_pos+1] == 0:
break
end_pos += 2
return padded_plain[:end_pos].decode('utf-16le')
except Exception as e:
logger.error(f"传统格式解密失败: {str(e)}")
raise
class SecureCRTCryptoV2:
"""处理V2 AES加密格式"""
AES_BLOCK_SIZE = 16
def __init__(self, config_passphrase: str = ''):
self.IV = b'\x00' * self.AES_BLOCK_SIZE
self.Key = SHA256.new(config_passphrase.encode('utf-8')).digest()
def decrypt(self, ciphertext_hex: str) -> str:
try:
cipher = AES.new(self.Key, AES.MODE_CBC, iv=self.IV)
cipher_bytes = bytes.fromhex(ciphertext_hex)
padded_plain = cipher.decrypt(cipher_bytes)
# 解析数据格式:4字节长度 + 明文 + 32字节SHA256摘要
plain_len = int.from_bytes(padded_plain[:4], 'little')
plain_bytes = padded_plain[4:4+plain_len]
digest = padded_plain[4+plain_len:4+plain_len+32]
if SHA256.new(plain_bytes).digest() != digest:
raise ValueError("摘要校验失败")
return plain_bytes.decode('utf-8')
except Exception as e:
logger.error(f"V2格式解密失败: {str(e)}")
raise
def main():
if len(sys.argv) < 3:
print("用法:")
print(f" {sys.argv[0]} dec <密文> [-v2] [-p 配置口令]")
print("示例:")
print(f" {sys.argv[0]} dec 30d93758e1ea303c... -v2 -p mypass")
return
try:
mode = sys.argv[1].lower()
if mode != 'dec':
raise ValueError("仅支持解密模式")
ciphertext = sys.argv[2]
use_v2 = '-v2' in sys.argv
passphrase = ''
if '-p' in sys.argv:
idx = sys.argv.index('-p')
if idx + 1 < len(sys.argv):
passphrase = sys.argv[idx+1]
if use_v2:
result = SecureCRTCryptoV2(passphrase).decrypt(ciphertext)
else:
result = SecureCRTCrypto().decrypt(ciphertext)
print(f"解密结果: {result}")
except Exception as e:
logger.error(f"操作失败: {str(e)}")
sys.exit(1)
if __name__ == '__main__':
main()
传统格式解密:
bash复制python securecrt_decrypt.py dec c71bd1c86f3b804e42432f53247c50d9287f410c7e59166969acab69daa6eaadbe15c0c54c0e076e945a6d82f9e13df2
V2格式解密:
bash复制python securecrt_decrypt.py dec 30d93758e1ea303c18235301266dc93a5d69070b40668251a2c75de132a5a034b018dab1a172bd73da69151d6fd4099824f2a4fb5f57c5f854e9a9012ff0e6c1 -v2
带配置口令的V2格式:
bash复制python securecr_decrypt.py dec [密文] -v2 -p 您的配置口令
以下脚本可自动扫描会话目录,提取并解密所有保存的密码:
python复制import configparser
import re
from pathlib import Path
def find_securecrt_sessions():
"""查找各平台下的会话文件位置"""
paths = []
if sys.platform == 'win32':
paths.append(Path(os.environ['APPDATA']) / 'VanDyke' / 'Config' / 'Sessions')
elif sys.platform == 'darwin':
paths.append(Path.home() / 'Library' / 'Application Support' / 'VanDyke' / 'SecureCRT' / 'Config' / 'Sessions')
paths.append(Path.home() / '.vandyke' / 'SecureCRT' / 'Config' / 'Sessions')
for path in paths:
if path.exists():
return path
return None
def decrypt_session_password(file_path):
"""解密单个会话文件中的密码"""
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# 匹配密码字段
password_v2 = re.search(r'S:"Password V2"=\d+:(.*)', content)
password_v1 = re.search(r'S:"Password"=u(.*)', content)
results = []
crypto = SecureCRTCrypto()
crypto_v2 = SecureCRTCryptoV2()
if password_v2:
try:
decrypted = crypto_v2.decrypt(password_v2.group(1))
results.append(('V2', decrypted))
except Exception as e:
logger.warning(f"解密V2密码失败: {str(e)}")
if password_v1:
try:
decrypted = crypto.decrypt(password_v1.group(1))
results.append(('V1', decrypted))
except Exception as e:
logger.warning(f"解密V1密码失败: {str(e)}")
return results
def batch_decrypt_sessions():
"""批量解密所有会话密码"""
sessions_dir = find_securecrt_sessions()
if not sessions_dir:
logger.error("未找到SecureCRT会话目录")
return
print(f"扫描会话目录: {sessions_dir}")
for session_file in sessions_dir.glob('*.ini'):
print(f"\n会话文件: {session_file.name}")
passwords = decrypt_session_password(session_file)
if passwords:
for ver, pwd in passwords:
print(f" {ver}格式密码: {pwd}")
else:
print(" 未找到可解密的密码")
解密后的密码应妥善保存,推荐以下安全实践:
在企业环境中,可以使用以下方法集中管理SecureCRT密码:
python复制import pandas as pd
def export_to_excel(sessions_dir, output_file):
"""将会话信息导出到Excel"""
data = []
for session_file in sessions_dir.glob('*.ini'):
with open(session_file, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# 提取会话信息
hostname = re.search(r'S:"Hostname"=([^\r\n]*)', content)
username = re.search(r'S:"Username"=([^\r\n]*)', content)
port = re.search(r'D:"\[SSH2\] Port"=([0-9a-f]{8})', content)
passwords = decrypt_session_password(session_file)
decrypted_pwd = passwords[0][1] if passwords else ''
data.append({
'Session': session_file.stem,
'Hostname': hostname.group(1) if hostname else '',
'Username': username.group(1) if username else '',
'Port': int(port.group(1), 16) if port else 22,
'Password': decrypted_pwd
})
df = pd.DataFrame(data)
df.to_excel(output_file, index=False)
print(f"已导出到 {output_file}")
结合企业密码策略,可以自动检查解密后的密码强度:
python复制import zxcvbn
def check_password_strength(password):
"""评估密码强度"""
result = zxcvbn.zxcvbn(password)
return {
'score': result['score'], # 0-4
'warning': result['feedback']['warning'],
'suggestions': result['feedback']['suggestions']
}
def audit_passwords(sessions_dir):
"""审计所有会话密码强度"""
weak_passwords = []
for session_file in sessions_dir.glob('*.ini'):
passwords = decrypt_session_password(session_file)
if not passwords:
continue
pwd = passwords[0][1]
strength = check_password_strength(pwd)
if strength['score'] < 3: # 中等强度以下
weak_passwords.append({
'session': session_file.name,
'password': pwd,
'strength': strength
})
print(f"\n发现{len(weak_passwords)}个弱密码:")
for item in weak_passwords:
print(f"\n会话: {item['session']}")
print(f"密码: {item['password']}")
print(f"强度评分: {item['strength']['score']}/4")
print(f"警告: {item['strength']['warning']}")
print("建议:")
for suggestion in item['strength']['suggestions']:
print(f" - {suggestion}")
密码存储策略
系统防护措施
应急准备
实际工作中,我曾遇到一个案例:某企业管理员离职后,交接文档中遗漏了部分服务器的密码。通过分析SecureCRT保存的会话,我们成功恢复了所有关键系统的访问权限,同时借此机会完善了企业的密码管理体系。这提醒我们,既要掌握密码恢复技术,更要建立规范的密码管理制度。