基于Go編寫一個(gè)可視化Navicat本地密碼解析器
前提
開(kāi)發(fā)小組在測(cè)試環(huán)境基于docker構(gòu)建和遷移一個(gè)MySQL8.x實(shí)例,過(guò)程中大意沒(méi)有記錄對(duì)應(yīng)的用戶密碼,然后發(fā)現(xiàn)某開(kāi)發(fā)同事本地Navicat記錄了根用戶,于是搜索是否能夠反解析Navicat中的密碼掩碼(這里可以基本斷定Navicat對(duì)密碼是采用了對(duì)稱加密算法),于是發(fā)現(xiàn)了這個(gè)倉(cāng)庫(kù):
密碼的解密算法顯然是被泄露了,那么就可以利用起來(lái)。加之筆者之前花了一點(diǎn)點(diǎn)時(shí)間入門了一下Go,于是業(yè)余花了點(diǎn)時(shí)間編寫了一個(gè)GUI工具。這個(gè)工具主要功能是:在Windows系統(tǒng)下,自動(dòng)讀取Navicat在注冊(cè)列表中寫入的所有(數(shù)據(jù)庫(kù))服務(wù)器連接數(shù)據(jù)作為列表展示,對(duì)于每個(gè)服務(wù)器連接數(shù)據(jù)的密碼嘗試進(jìn)行解密。效果如下:

大致原理
參考how-does-navicat-encrypt-password倉(cāng)庫(kù),因?yàn)?code>Navicat兩種版本的對(duì)稱加密算法的具體算法、秘鑰和加密向量都被泄露了,得知:
- 版本一(
Low):使用Blowfish/ECB/NoPadding模式 - 版本二(
High):使用AES/CBC/PKCS5Padding模式
其中AES/CBC/PKCS5Padding實(shí)現(xiàn)是比較簡(jiǎn)單的,Blowfish/ECB/NoPadding在Go的原生類庫(kù)中剛好缺少了ECB解碼器,只能仔細(xì)翻閱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系統(tǒng)注冊(cè)列表下的服務(wù)器連接數(shù)據(jù)列表,Navicat多個(gè)版本測(cè)試發(fā)現(xiàn)服務(wù)器連接數(shù)保存在注冊(cè)列表的Software\PremiumSoft\Navicat\Servers目錄下,只需要全量讀取出來(lái)并且按照每個(gè)服務(wù)器連接數(shù)據(jù)的明細(xì)k-v一步一步解析即可。這個(gè)解析過(guò)程的偽代碼如下:
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)
// 數(shù)據(jù)庫(kù)的版本
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{...})
}小結(jié)
提醒 - 這個(gè)項(xiàng)目?jī)H僅是提供參考和學(xué)習(xí),供個(gè)人本地開(kāi)發(fā)時(shí)候使用,切勿用于竊取他人的數(shù)據(jù)庫(kù)密碼。項(xiàng)目倉(cāng)庫(kù):
順帶一提使用fyne做GUI開(kāi)發(fā)效果還可以,不過(guò)目前這個(gè)庫(kù)還存在比較多BUG,性能高的同時(shí)占用的資源也比較高。
以上就是基于Go編寫一個(gè)可視化Navicat本地密碼解析器的詳細(xì)內(nèi)容,更多關(guān)于Go編寫Navicat密碼解析器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang動(dòng)態(tài)創(chuàng)建類的示例代碼
這篇文章主要介紹了golang動(dòng)態(tài)創(chuàng)建類的實(shí)例代碼,本文通過(guò)實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下2023-06-06
Go語(yǔ)言普通指針unsafe.Pointer?uintpt之間的關(guān)系及指針運(yùn)算
這篇文章主要為大家介紹了Go語(yǔ)言普通指針unsafe.Pointer?uintpt之間的關(guān)系及指針運(yùn)算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Go語(yǔ)言開(kāi)源庫(kù)實(shí)現(xiàn)Onvif協(xié)議客戶端設(shè)備搜索
這篇文章主要為大家介紹了Go語(yǔ)言O(shè)nvif協(xié)議客戶端設(shè)備搜索示例實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
提升Golang應(yīng)用性能:深入理解Context的應(yīng)用
本文將深入探討如何通過(guò)深入理解和正確應(yīng)用Go語(yǔ)言中的Context來(lái)提升應(yīng)用性能。需要的朋友可以參考下2023-09-09
Golang使用gin框架實(shí)現(xiàn)一個(gè)完整的聊天室功能
由于我們項(xiàng)目的需要,我就研究了一下關(guān)于websocket的相關(guān)內(nèi)容,去實(shí)現(xiàn)一個(gè)聊天室的功能,經(jīng)過(guò)幾天的探索,現(xiàn)在使用Gin框架實(shí)現(xiàn)了一個(gè)完整的聊天室+消息實(shí)時(shí)通知系統(tǒng),感興趣的小伙伴歡迎閱讀本文2023-08-08
基于Go語(yǔ)言對(duì)浮點(diǎn)數(shù)進(jìn)行比較的方法
在 Go 語(yǔ)言(以及大多數(shù)遵循 IEEE 754 標(biāo)準(zhǔn)的編程語(yǔ)言)中,?差值容限(Epsilon) ? 是指在進(jìn)行浮點(diǎn)數(shù)比較時(shí),允許兩個(gè)數(shù)值之間存在的最大微小誤差范圍,本文給大家介紹了基于Go語(yǔ)言對(duì)浮點(diǎn)數(shù)進(jìn)行比較的方法,需要的朋友可以參考下2026-05-05
Go interface接口聲明實(shí)現(xiàn)及作用詳解
這篇文章主要為大家介紹了Go interface接口聲明實(shí)現(xiàn)及作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03

