基于Go编写一个可视化Navicat本地密码解析器
作者:Throwable
前提
开发小组在测试环境基于docker
构建和迁移一个MySQL8.x
实例,过程中大意没有记录对应的用户密码,然后发现某开发同事本地Navicat
记录了根用户,于是搜索是否能够反解析Navicat
中的密码掩码(这里可以基本断定Navicat
对密码是采用了对称加密算法),于是发现了这个仓库:
密码的解密算法显然是被泄露了,那么就可以利用起来。加之笔者之前花了一点点时间入门了一下Go
,于是业余花了点时间编写了一个GUI
工具。这个工具主要功能是:在Windows
系统下,自动读取Navicat
在注册列表中写入的所有(数据库)服务器连接数据作为列表展示,对于每个服务器连接数据的密码尝试进行解密。效果如下:
大致原理
参考how-does-navicat-encrypt-password仓库,因为Navicat
两种版本的对称加密算法的具体算法、秘钥和加密向量都被泄露了,得知:
- 版本一(
Low
):使用Blowfish/ECB/NoPadding
模式 - 版本二(
High
):使用AES/CBC/PKCS5Padding
模式
其中AES/CBC/PKCS5Padding
实现是比较简单的,Blowfish/ECB/NoPadding
在Go
的原生类库中刚好缺少了ECB
解码器,只能仔细翻阅how-does-navicat-encrypt-password
的Java
版本代码
func (l *LowVersionCipher) Decrypt(input string) (string, error) { ciphertext, err := hex.DecodeString(input) if err != nil { return "", err } if len(ciphertext)%8 != 0 { return "", errors.New("ciphertext length must be a multiple of 8") } plaintext := make([]byte, len(ciphertext)) cv := make([]byte, len(l.iv)) copy(cv, l.iv) blocksLen := len(ciphertext) / blowfish.BlockSize leftLen := len(ciphertext) % blowfish.BlockSize decrypter := NewECBDecrypter(l.cipher) for i := 0; i < blocksLen; i++ { temp := make([]byte, blowfish.BlockSize) copy(temp, ciphertext[i*blowfish.BlockSize:(i+1)*blowfish.BlockSize]) if err != nil { panic(err) } decrypter.CryptBlocks(temp, temp) xorBytes(temp, cv) copy(plaintext[i*blowfish.BlockSize:(i+1)*blowfish.BlockSize], temp) for j := 0; j < len(cv); j++ { cv[j] ^= ciphertext[i*blowfish.BlockSize+j] } } if leftLen != 0 { decrypter.CryptBlocks(cv, cv) temp := make([]byte, leftLen) copy(temp, ciphertext[blocksLen*blowfish.BlockSize:]) xorBytes(temp, cv[:leftLen]) copy(plaintext[blocksLen*blowfish.BlockSize:], temp) } return string(plaintext), nil } func xorBytes(a []byte, b []byte) { for i := 0; i < len(a); i++ { aVal := int(a[i]) & 0xff // convert byte to integer bVal := int(b[i]) & 0xff a[i] = byte(aVal ^ bVal) // xor aVal and bVal and typecast to byte } }
接着基于golang.org/x/sys/windows/registry
加载Windows
系统注册列表下的服务器连接数据列表,Navicat
多个版本测试发现服务器连接数保存在注册列表的Software\PremiumSoft\Navicat\Servers
目录下,只需要全量读取出来并且按照每个服务器连接数据的明细k-v
一步一步解析即可。这个解析过程的伪代码如下:
const NsPath = `Software\PremiumSoft\Navicat\Servers` nsp, _ := registry.OpenKey(registry.CURRENT_USER, NsPath, registry.READ) subKeys, _ := nsp.ReadSubKeyNames(999) var servers []*Server for _, subKey := range subKeys { serverPath := strings.Join([]string{NsPath, subKey}, `\`) sp, _ := registry.OpenKey(registry.CURRENT_USER, serverPath, registry.READ) // 数据库的版本 serverVersion, _, _ := sp.GetIntegerValue("ServerVersion") // host host, _, _ := sp.GetStringValue("Host") // 用户名 username, _, _ := sp.GetStringValue("UserName") // 密码密文 pwd, _, _ := sp.GetStringValue("Pwd") // 端口,一般是3306 port, _, _ := sp.GetIntegerValue("Port") realPwd := pwd if (len(pwd) > 0){ // 解密得到密码明文 realPwd, _ = cipher.Decrypt(pwd) } servers = append(servers, &Server{...}) }
小结
提醒 - 这个项目仅仅是提供参考和学习,供个人本地开发时候使用,切勿用于窃取他人的数据库密码。项目仓库:
顺带一提使用fyne做GUI
开发效果还可以,不过目前这个库还存在比较多BUG
,性能高的同时占用的资源也比较高。
以上就是基于Go编写一个可视化Navicat本地密码解析器的详细内容,更多关于Go编写Navicat密码解析器的资料请关注脚本之家其它相关文章!