基于Go编写一个可视化Navicat本地密码解析器

前提

开发小组在测试环境基于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/NoPaddingGo的原生类库中刚好缺少了ECB解码器,只能仔细翻阅how-does-navicat-encrypt-passwordJava版本代码并且强行转换为Go实现:

golang 复制代码
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一步一步解析即可。这个解析过程的伪代码如下:

golang 复制代码
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{...})
}

小结

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

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

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

相关推荐
千叶寻-26 分钟前
正则表达式
前端·javascript·后端·架构·正则表达式·node.js
小咕聊编程1 小时前
【含文档+源码】基于SpringBoot的过滤协同算法之网上服装商城设计与实现
java·spring boot·后端
追逐时光者8 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_8 小时前
敏捷开发流程-精简版
前端·后端
苏打水com9 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
间彧10 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧10 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧10 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧10 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧10 小时前
Spring Cloud Gateway详解与应用实战
后端