经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
基于Go编写一个可视化Navicat本地密码解析器
来源:cnblogs  作者:throwable  时间:2023/8/4 10:07:04  对本文有异议

前提

开发小组在测试环境基于docker构建和迁移一个MySQL8.x实例,过程中大意没有记录对应的用户密码,然后发现某开发同事本地Navicat记录了根用户,于是搜索是否能够反解析Navicat中的密码掩码(这里可以基本断定Navicat对密码是采用了对称加密算法),于是发现了这个仓库:

密码的解密算法显然是被泄露了,那么就可以利用起来。加之笔者之前花了一点点时间入门了一下Go,于是业余花了点时间编写了一个GUI工具。这个工具主要功能是:在Windows系统下,自动读取Navicat在注册列表中写入的所有(数据库)服务器连接数据作为列表展示,对于每个服务器连接数据的密码尝试进行解密。效果如下:

navicat-watcher-1.png

大致原理

参考how-does-navicat-encrypt-password仓库,因为Navicat两种版本的对称加密算法的具体算法、秘钥和加密向量都被泄露了,得知:

  • 版本一(Low):使用Blowfish/ECB/NoPadding模式
  • 版本二(High):使用AES/CBC/PKCS5Padding模式

其中AES/CBC/PKCS5Padding实现是比较简单的,Blowfish/ECB/NoPaddingGo的原生类库中刚好缺少了ECB解码器,只能仔细翻阅how-does-navicat-encrypt-passwordJava版本代码并且强行转换为Go实现:

  1. func (l *LowVersionCipher) Decrypt(input string) (string, error) {
  2. ciphertext, err := hex.DecodeString(input)
  3. if err != nil {
  4. return "", err
  5. }
  6. if len(ciphertext)%8 != 0 {
  7. return "", errors.New("ciphertext length must be a multiple of 8")
  8. }
  9. plaintext := make([]byte, len(ciphertext))
  10. cv := make([]byte, len(l.iv))
  11. copy(cv, l.iv)
  12. blocksLen := len(ciphertext) / blowfish.BlockSize
  13. leftLen := len(ciphertext) % blowfish.BlockSize
  14. decrypter := NewECBDecrypter(l.cipher)
  15. for i := 0; i < blocksLen; i++ {
  16. temp := make([]byte, blowfish.BlockSize)
  17. copy(temp, ciphertext[i*blowfish.BlockSize:(i+1)*blowfish.BlockSize])
  18. if err != nil {
  19. panic(err)
  20. }
  21. decrypter.CryptBlocks(temp, temp)
  22. xorBytes(temp, cv)
  23. copy(plaintext[i*blowfish.BlockSize:(i+1)*blowfish.BlockSize], temp)
  24. for j := 0; j < len(cv); j++ {
  25. cv[j] ^= ciphertext[i*blowfish.BlockSize+j]
  26. }
  27. }
  28. if leftLen != 0 {
  29. decrypter.CryptBlocks(cv, cv)
  30. temp := make([]byte, leftLen)
  31. copy(temp, ciphertext[blocksLen*blowfish.BlockSize:])
  32. xorBytes(temp, cv[:leftLen])
  33. copy(plaintext[blocksLen*blowfish.BlockSize:], temp)
  34. }
  35. return string(plaintext), nil
  36. }
  37. func xorBytes(a []byte, b []byte) {
  38. for i := 0; i < len(a); i++ {
  39. aVal := int(a[i]) & 0xff // convert byte to integer
  40. bVal := int(b[i]) & 0xff
  41. a[i] = byte(aVal ^ bVal) // xor aVal and bVal and typecast to byte
  42. }
  43. }

接着基于golang.org/x/sys/windows/registry加载Windows系统注册列表下的服务器连接数据列表,Navicat多个版本测试发现服务器连接数保存在注册列表的Software\PremiumSoft\Navicat\Servers目录下,只需要全量读取出来并且按照每个服务器连接数据的明细k-v一步一步解析即可。这个解析过程的伪代码如下:

  1. const NsPath = `Software\PremiumSoft\Navicat\Servers`
  2. nsp, _ := registry.OpenKey(registry.CURRENT_USER, NsPath, registry.READ)
  3. subKeys, _ := nsp.ReadSubKeyNames(999)
  4. var servers []*Server
  5. for _, subKey := range subKeys {
  6. serverPath := strings.Join([]string{NsPath, subKey}, `\`)
  7. sp, _ := registry.OpenKey(registry.CURRENT_USER, serverPath, registry.READ)
  8. // 数据库的版本
  9. serverVersion, _, _ := sp.GetIntegerValue("ServerVersion")
  10. // host
  11. host, _, _ := sp.GetStringValue("Host")
  12. // 用户名
  13. username, _, _ := sp.GetStringValue("UserName")
  14. // 密码密文
  15. pwd, _, _ := sp.GetStringValue("Pwd")
  16. // 端口,一般是3306
  17. port, _, _ := sp.GetIntegerValue("Port")
  18. realPwd := pwd
  19. if (len(pwd) > 0){
  20. // 解密得到密码明文
  21. realPwd, _ = cipher.Decrypt(pwd)
  22. }
  23. servers = append(servers, &Server{...})
  24. }

小结

提醒 - 这个项目仅仅是提供参考和学习,供个人本地开发时候使用,切勿用于窃取他人的数据库密码。项目仓库:

顺带一提使用fyneGUI开发效果还可以,不过目前这个库还存在比较多BUG,性能高的同时占用的资源也比较高。

(本文完 c-1-d e-a-20230802)

原文链接:https://www.cnblogs.com/throwable/p/17605155.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号