Xshell 密码解密
所需信息
- Xshell 密文密码
- Xshell 版本信息(不同版本的密钥有些差异)
- 操作系统用户名(不包含计算机名)
- 用户 SID
密文密码
密文密码存在于 .xsh 后缀的 Xshell 配置文件中。
不同版本的 Xshell 在 Windows 上的配置文件(.xsh)所在目录不同,并且电脑上可以同时存在多个不同版本的 Xshell:
版本 | 目录路径 |
---|---|
Xshell 5 | %userprofile%\Documents\NetSarang\Xshell\Sessions |
Xshell 6 | %userprofile%\Documents\NetSarang Computer\6\Xshell\Sessions |
Xshell 7 | %userprofile%\Documents\NetSarang Computer\7\Xshell\Sessions |
注:由于部分版本(Xshell5)的 UserDataPath 路径在安装时可以指定,会导致
*.xsh
文件位置产生变化。建议使用读取注册表的方式获取路径,比较准确(注册表位置没有改变)。
Xshell 用户数据路径(UserDataPath)相关的注册表位置:
注册表路径 | 描述 | 示例值 |
---|---|---|
HKEY_CURRENT_USER\Software\NetSarang\Common\ | 子目录名为 NetSarang 产品的大版本号信息 | 如:7 |
HKEY_CURRENT_USER\Software\NetSarang\Common\大版本号\UserData\ | 键 UserDataPath 的值为所有 NetSarang 产品的用户数据目录 | 如:C:\Users\username\Documents\NetSarang Computer\7 |
配置文件就在 Windows 文件目录 UserDataPath键值\产品名(Xshell)\Sessions
下,每个 .xsh 文件就是一台主机的连接配置。
使用如下命令可以快速查看并过滤出 .xsh 配置文件中的关键信息:
1
type *.xsh | findstr /c:"Host=" /c:"Port=" /c:"UserName=" /c:"Password=" /c:"Version="
用户名和 SID
使用 WMIC 命令获取用户名和 SID:
1
2
3
PS C:\Users\hp\Desktop> WMIC useraccount get name,sid
Name SID
hp S-1-5-21-2029295174-2268103727-2717561949-1000
用户名和 SID 的注册表位置:
注册表路径 | 描述 | 示例值 |
---|---|---|
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Builtin\Aliases\Members\ | 本地帐号的所有 SID 列表 | 如:S-1-5-21-2029295174-2268103727-2717561949-1000 |
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\SID 值 | 键 ProfileImagePath 的值为 SID 所关联的用户名 | 如:C:\Users\hp |
使用 Java 代码(jna-platform 包)获取操作系统用户名和 SID:
1
2
3
4
5
6
7
8
9
10
11
Netapi32Util.User[] users = Netapi32Util.getUsers();
System.out.println("用户数量: " + users.length);
System.out.println();
for (Netapi32Util.User user : users) {
System.out.println("账户名称:" + user.name);
Advapi32Util.Account account = Advapi32Util
.getAccountByName(user.name);
System.out.println("账户全名:" + account.fqn);
System.out.println("账户SID:" + account.sidString);
System.out.println();
}
加密
任何版本的 Xshell 密码均使用 RC4 加密算法,但不同版本的密钥有些变化。
版本 < 5.1 生成密码方式:使用固定密钥,对明文进行 RC4 加密,再进行 base64 编码。
版本 >= 5.1 生成密码的方式为:"明文进行 RC4 加密(密码)+ 明文的 sha256 摘要(校验和)"
再进行 Base64 编码。
版本 < 5.1
Xshell 5.1 以前使用固定字符串 !X@s#c$e%l^l&
的 MD5 值作为密钥
1
2
3
4
5
6
from hashlib import *
from Crypto.Cipher import ARC4
from base64 import *
cipher = ARC4.new(md5(b'!X@s#h$e%l^l&').digest())
print(b64encode(cipher.encrypt(b'hello')).decode())
版本 = 5.1 or 5.2
直接使用 SID 的 32 个字节的 sha256 摘要作为密钥
版本 > 5.2
使用 "用户名" + "SID"
的 sha256 摘要作为密钥
1
2
3
4
5
6
7
8
// Windows 用户名
String winUserName = "hp";
// Windows SID
String winSID = "S-1-5-21-2029295174-2268103727-2717561949-1000";
// XShell 密文密码 (明文为 test@!@#)
String encryptedPasswd = "MtrGl/ZA5JU/PqCczXcPOXyCXJ0lwQZaHeRKIzPV7P6xvfaw2sUscA==";
// 创建密钥 Key (密钥为 "操作系统帐户名称 + SID" 的 sha256 摘要数组)
byte[] sha256hex = org.apache.commons.codec.digest.DigestUtils.sha256(winUserName + winSID);
版本 7
使用 "倒序的SID + 用户名"
的 sha256 摘要数组作为密钥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Windows 用户名
String winUserName = "hp";
// Windows SID
String winSID = "S-1-5-21-2029295174-2268103727-2717561949-1000";
// 明文密码
String plaintextPasswd = "test@!@#";
// 创建密钥 Key (密钥为 “倒序的SID + 操作系统帐户名称” 的 sha256 摘要数组)
byte[] sha256hex = DigestUtils.sha256(StringUtils.reverse(winSID) + winUserName);
Key key = new SecretKeySpec(sha256hex, "RC4"); // 指定该数组为密钥
// 创建 RC4加密器
Cipher cipher = Cipher.getInstance("RC4");
// 初始化加密器,指定加密密钥 Key
cipher.init(Cipher.ENCRYPT_MODE, key);
// 对数据进行加密
byte[] cipherBytes = cipher.doFinal(plaintextPasswd.getBytes());
// 在加密后的字节数组后面添加明文密码的sha256摘要(校验和),再进行 base64 编码
String encryptedPasswd = new String(org.apache.commons.codec.binary.Base64.encodeBase64(ArrayUtils.addAll(cipherBytes, DigestUtils.sha256(plaintextPasswd))));
System.out.println(encryptedPasswd);
// 密文密码 6WgMEKQvz4Y/PqCczXcPOXyCXJ0lwQZaHeRKIzPV7P6xvfaw2sUscA==
解密
需要注意的是,在 XShell >= 5.1 明文密码的 sha256 摘要(32 个字节)将被视为校验和,附加到加密的密码后,所以在解密时需要将其从末尾去除。
XShell 7 解密过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Windows 用户名
String winUserName = "hp";
// Windows SID
String winSID = "S-1-5-21-2029295174-2268103727-2717561949-1000";
// XShell 密文密码
String encryptedPasswd = "6WgMEKQvz4Y/PqCczXcPOXyCXJ0lwQZaHeRKIzPV7P6xvfaw2sUscA==";
// base64 解码后的字节数组
byte[] originalBytes = org.apache.commons.codec.binary.Base64.decodeBase64(encryptedPasswd);
// 从密文数组中去除校验和 (校验和,是 32 个字节长的 SHA-256 摘要,有的脚本里写的减 0x20 这是 32 的 16 进制值)
byte[] newBytes = new byte[originalBytes.length - 32];
System.arraycopy(originalBytes, 0, newBytes, 0, originalBytes.length - 32);
// 创建密钥 Key (密钥为 "倒序的SID + 操作系统帐户名称" 的 sha256 摘要数组)
byte[] sha256hex = org.apache.commons.codec.digest.DigestUtils.sha256(StringUtils.reverse(winSID) + winUserName);
Key key = new SecretKeySpec(sha256hex, "RC4");
// 创建RC4解密器
Cipher cipher = Cipher.getInstance("RC4");
// 初始化解密器,并指定 Key
cipher.init(Cipher.DECRYPT_MODE, key);
// 解密后的密码
String decryptedPasswd = new String(cipher.doFinal(newBytes)); // 解密后结果为字节数组,使用 new String() 将其转化为字符串
System.out.println(decryptedPasswd);
// 明文密码为 test@!@#