From 2d12e86b08fd262676d2833b5b57483f438a5186 Mon Sep 17 00:00:00 2001 From: moond4rk Date: Wed, 10 Feb 2021 05:53:49 +0800 Subject: [PATCH] fix: firefox password decryption failure, Close #70 --- core/data/parse.go | 29 +++--- core/decrypt/decrypt.go | 157 ++++++++++++++++++++++++++++-- core/decrypt/decrypt_darwin.go | 103 +------------------- core/decrypt/decrypt_linux.go | 164 +------------------------------- core/decrypt/decrypt_windows.go | 92 ------------------ 5 files changed, 168 insertions(+), 377 deletions(-) diff --git a/core/data/parse.go b/core/data/parse.go index 0398320..75f73c8 100644 --- a/core/data/parse.go +++ b/core/data/parse.go @@ -598,29 +598,28 @@ func (p *passwords) FirefoxParse() error { return err } keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} - meta, err := decrypt.DecodeMeta(metaBytes) + metaPBE, err := decrypt.NewASN1PBE(metaBytes) if err != nil { log.Error("decrypt meta data failed", err) return err } + // default master password is empty var masterPwd []byte - m, err := decrypt.Meta(globalSalt, masterPwd, meta) + k, err := metaPBE.Decrypt(globalSalt, masterPwd) if err != nil { - log.Error("decrypt firefox meta failed", err) + log.Error("decrypt firefox meta bytes failed", err) return err } - if bytes.Contains(m, []byte("password-check")) { + if bytes.Contains(k, []byte("password-check")) { log.Debug("password-check success") m := bytes.Compare(nssA102, keyLin) if m == 0 { - var nss interface{} - nss, err = decrypt.DecodeNss(nssA11) + nssPBE, err := decrypt.NewASN1PBE(nssA11) if err != nil { log.Error("decode firefox nssA11 bytes failed", err) return err } - - finallyKey, err := decrypt.Nss(globalSalt, masterPwd, nss) + finallyKey, err := nssPBE.Decrypt(globalSalt, masterPwd) finallyKey = finallyKey[:24] if err != nil { log.Error("get firefox finally key failed") @@ -631,13 +630,19 @@ func (p *passwords) FirefoxParse() error { return err } for _, v := range allLogins { - userPBE, _ := decrypt.DecodeLogin(v.encryptUser) - pwdPBE, _ := decrypt.DecodeLogin(v.encryptPass) - user, err := decrypt.Des3Decrypt(finallyKey, userPBE.Iv, userPBE.Encrypted) + userPBE, err := decrypt.NewASN1PBE(v.encryptUser) + if err != nil { + log.Error("decode firefox user bytes failed", err) + } + pwdPBE, err := decrypt.NewASN1PBE(v.encryptPass) + if err != nil { + log.Error("decode firefox password bytes failed", err) + } + user, err := userPBE.Decrypt(finallyKey, masterPwd) if err != nil { log.Error(err) } - pwd, err := decrypt.Des3Decrypt(finallyKey, pwdPBE.Iv, pwdPBE.Encrypted) + pwd, err := pwdPBE.Decrypt(finallyKey, masterPwd) if err != nil { log.Error(err) } diff --git a/core/decrypt/decrypt.go b/core/decrypt/decrypt.go index 74ca22a..c8407ab 100644 --- a/core/decrypt/decrypt.go +++ b/core/decrypt/decrypt.go @@ -4,18 +4,160 @@ import ( "crypto/aes" "crypto/cipher" "crypto/des" + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" "encoding/asn1" "errors" "hack-browser-data/log" + + "golang.org/x/crypto/pbkdf2" ) var ( errSecurityKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal") errPasswordIsEmpty = errors.New("password is empty") errDecryptFailed = errors.New("decrypt failed, password is empty") + errDecodeASN1Failed = errors.New("decode ASN1 data failed") ) +type ASN1PBE interface { + Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) +} + +func NewASN1PBE(b []byte) (pbe ASN1PBE, err error) { + var ( + n NssPBE + m MetaPBE + l LoginPBE + ) + if _, err := asn1.Unmarshal(b, &n); err == nil { + return n, nil + } + if _, err := asn1.Unmarshal(b, &m); err == nil { + return m, nil + } + if _, err := asn1.Unmarshal(b, &l); err == nil { + return l, nil + } + return nil, errDecodeASN1Failed +} + +/* NSS Struct +SEQUENCE (2 elem) + SEQUENCE (2 elem) + OBJECT IDENTIFIER + SEQUENCE (2 elem) + OCTET STRING (20 byte) + INTEGER 1 + OCTET STRING (16 byte) +*/ +type NssPBE struct { + NssSequenceA + Encrypted []byte +} + +type NssSequenceA struct { + DecryptMethod asn1.ObjectIdentifier + NssSequenceB +} + +type NssSequenceB struct { + EntrySalt []byte + Len int +} + +func (n NssPBE) Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) { + // byte[] GLMP; // GlobalSalt + MasterPassword + // byte[] HP; // SHA1(GLMP) + // byte[] HPES; // HP + EntrySalt + // byte[] CHP; // SHA1(HPES) + // byte[] PES; // EntrySalt completed to 20 bytes by zero + // byte[] PESES; // PES + EntrySalt + // byte[] k1; + // byte[] tk; + // byte[] k2; + // byte[] k; // final value containing key and iv + glmp := append(globalSalt, masterPwd...) + hp := sha1.Sum(glmp) + s := append(hp[:], n.EntrySalt...) + chp := sha1.Sum(s) + pes := PaddingZero(n.EntrySalt, 20) + tk := hmac.New(sha1.New, chp[:]) + tk.Write(pes) + pes = append(pes, n.EntrySalt...) + k1 := hmac.New(sha1.New, chp[:]) + k1.Write(pes) + tkPlus := append(tk.Sum(nil), n.EntrySalt...) + k2 := hmac.New(sha1.New, chp[:]) + k2.Write(tkPlus) + k := append(k1.Sum(nil), k2.Sum(nil)...) + iv := k[len(k)-8:] + log.Debug("get firefox pbe key and iv success") + return des3Decrypt(k[:24], iv, n.Encrypted) +} + +/* META Struct +SEQUENCE (2 elem) + SEQUENCE (2 elem) + OBJECT IDENTIFIER + SEQUENCE (2 elem) + SEQUENCE (2 elem) + OBJECT IDENTIFIER + SEQUENCE (4 elem) + OCTET STRING (32 byte) + INTEGER 1 + INTEGER 32 + SEQUENCE (1 elem) + OBJECT IDENTIFIER + SEQUENCE (2 elem) + OBJECT IDENTIFIER + OCTET STRING (14 byte) + OCTET STRING (16 byte) +*/ +type MetaPBE struct { + MetaSequenceA + Encrypted []byte +} + +type MetaSequenceA struct { + PKCS5PBES2 asn1.ObjectIdentifier + MetaSequenceB +} +type MetaSequenceB struct { + MetaSequenceC + MetaSequenceD +} + +type MetaSequenceC struct { + PKCS5PBKDF2 asn1.ObjectIdentifier + MetaSequenceE +} + +type MetaSequenceD struct { + AES256CBC asn1.ObjectIdentifier + IV []byte +} + +type MetaSequenceE struct { + EntrySalt []byte + IterationCount int + KeySize int + MetaSequenceF +} + +type MetaSequenceF struct { + HMACWithSHA256 asn1.ObjectIdentifier +} + +func (m MetaPBE) Decrypt(globalSalt, masterPwd []byte) (key2 []byte, err error) { + k := sha1.Sum(globalSalt) + key := pbkdf2.Key(k[:], m.EntrySalt, m.IterationCount, m.KeySize, sha256.New) + iv := append([]byte{4, 14}, m.IV...) + return aes128CBCDecrypt(key, iv, m.Encrypted) +} + func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { @@ -35,7 +177,7 @@ func PKCS5UnPadding(src []byte) []byte { } // Des3Decrypt use for decrypt firefox PBE -func Des3Decrypt(key, iv []byte, src []byte) ([]byte, error) { +func des3Decrypt(key, iv []byte, src []byte) ([]byte, error) { block, err := des.NewTripleDESCipher(key) if err != nil { log.Error(err) @@ -69,20 +211,15 @@ SEQUENCE (3 elem) */ type LoginPBE struct { CipherText []byte - SequenceLogin + LoginSequence Encrypted []byte } -type SequenceLogin struct { +type LoginSequence struct { asn1.ObjectIdentifier Iv []byte } -func DecodeLogin(decodeItem []byte) (pbe LoginPBE, err error) { - _, err = asn1.Unmarshal(decodeItem, &pbe) - if err != nil { - log.Error(err) - return - } - return pbe, nil +func (l LoginPBE) Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) { + return des3Decrypt(globalSalt, l.Iv, l.Encrypted) } diff --git a/core/decrypt/decrypt_darwin.go b/core/decrypt/decrypt_darwin.go index 06ca5dd..beb694b 100644 --- a/core/decrypt/decrypt_darwin.go +++ b/core/decrypt/decrypt_darwin.go @@ -1,116 +1,17 @@ package decrypt -import ( - "crypto/hmac" - "crypto/sha1" - "encoding/asn1" - - "hack-browser-data/log" -) - -var ( - chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} -) - func ChromePass(key, encryptPass []byte) ([]byte, error) { if len(encryptPass) > 3 { if len(key) == 0 { return nil, errSecurityKeyIsEmpty } - m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) - return m, err + var chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} + return aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) } else { return nil, errDecryptFailed } } -/* -SEQUENCE (2 elem) - SEQUENCE (2 elem) - OBJECT IDENTIFIER - SEQUENCE (2 elem) - OCTET STRING (20 byte) - INTEGER 1 - OCTET STRING (16 byte) -*/ - -type NssPBE struct { - SequenceA - Encrypted []byte -} - -type MetaPBE struct { - SequenceA - Encrypted []byte -} - -type SequenceA struct { - DecryptMethod asn1.ObjectIdentifier - SequenceB -} - -type SequenceB struct { - EntrySalt []byte - Len int -} - -func DecodeMeta(metaBytes []byte) (pbe MetaPBE, err error) { - _, err = asn1.Unmarshal(metaBytes, &pbe) - if err != nil { - log.Error(err) - return - } - return -} - func DPApi(data []byte) ([]byte, error) { return nil, nil } - -func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) { - _, err = asn1.Unmarshal(nssA11Bytes, &pbe) - if err != nil { - log.Error(err) - return - } - return -} - -func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { - return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) -} - -func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { - return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) -} - -func decryptPBE(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, error) { - // byte[] GLMP; // GlobalSalt + MasterPassword - // byte[] HP; // SHA1(GLMP) - // byte[] HPES; // HP + EntrySalt - // byte[] CHP; // SHA1(HPES) - // byte[] PES; // EntrySalt completed to 20 bytes by zero - // byte[] PESES; // PES + EntrySalt - // byte[] k1; - // byte[] tk; - // byte[] k2; - // byte[] k; // final value containing key and iv - glmp := append(globalSalt, masterPwd...) - hp := sha1.Sum(glmp) - s := append(hp[:], entrySalt...) - chp := sha1.Sum(s) - pes := PaddingZero(entrySalt, 20) - tk := hmac.New(sha1.New, chp[:]) - tk.Write(pes) - pes = append(pes, entrySalt...) - k1 := hmac.New(sha1.New, chp[:]) - k1.Write(pes) - tkPlus := append(tk.Sum(nil), entrySalt...) - k2 := hmac.New(sha1.New, chp[:]) - k2.Write(tkPlus) - k := append(k1.Sum(nil), k2.Sum(nil)...) - iv := k[len(k)-8:] - key := k[:24] - log.Debug("get firefox pbe key and iv success") - return Des3Decrypt(key, iv, encrypted) -} diff --git a/core/decrypt/decrypt_linux.go b/core/decrypt/decrypt_linux.go index 2ca2db8..ad543ff 100644 --- a/core/decrypt/decrypt_linux.go +++ b/core/decrypt/decrypt_linux.go @@ -1,29 +1,12 @@ package decrypt -import ( - "crypto/hmac" - "crypto/sha1" - "crypto/sha256" - "encoding/asn1" - "encoding/hex" - "errors" - - "hack-browser-data/log" - - "golang.org/x/crypto/pbkdf2" -) - -var ( - chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} -) - func ChromePass(key, encryptPass []byte) ([]byte, error) { + var chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} if len(encryptPass) > 3 { if len(key) == 0 { return nil, errSecurityKeyIsEmpty } - m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) - return m, err + return aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) } else { return nil, errDecryptFailed } @@ -32,146 +15,3 @@ func ChromePass(key, encryptPass []byte) ([]byte, error) { func DPApi(data []byte) ([]byte, error) { return nil, nil } - -/* -SEQUENCE (2 elem) - SEQUENCE (2 elem) - OBJECT IDENTIFIER - SEQUENCE (2 elem) - OCTET STRING (20 byte) - INTEGER 1 - OCTET STRING (16 byte) -*/ -type MetaPBE struct { - SequenceA - Encrypted []byte -} - -type SequenceA struct { - DecryptMethod asn1.ObjectIdentifier - SequenceB -} - -type SequenceB struct { - EntrySalt []byte - Len int -} - -type NssPBE struct { - SequenceNSSA - Encrypted []byte -} - -type SequenceNSSA struct { - PKCS5PBES2 asn1.ObjectIdentifier - SequenceNSSB -} -type SequenceNSSB struct { - SequenceC - SequenceD -} - -type SequenceC struct { - PKCS5PBKDF2 asn1.ObjectIdentifier - SequenceE -} - -type SequenceD struct { - AES256CBC asn1.ObjectIdentifier - IV []byte -} - -type SequenceE struct { - EntrySalt []byte - IterationCount int - KeySize int - SequenceF -} - -type SequenceF struct { - HMACWithSHA256 asn1.ObjectIdentifier -} - -func DecodeMeta(decodeItem []byte) (pbe MetaPBE, err error) { - _, err = asn1.Unmarshal(decodeItem, &pbe) - if err != nil { - log.Error(err) - return - } - return -} - -func DecodeNss(nssA11Bytes []byte) (nss interface{}, err error) { - var pbe NssPBE - _, err = asn1.Unmarshal(nssA11Bytes, &pbe) - - if err != nil { - return DecodeMeta(nssA11Bytes) - } - - return pbe, err -} - -func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { - return decryptMeta(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) -} - -func Nss(globalSalt, masterPwd []byte, pbe interface{}) ([]byte, error) { - switch pbe.(type) { - case NssPBE: - return decryptNss(globalSalt, masterPwd, pbe.(NssPBE).IV, pbe.(NssPBE).EntrySalt, pbe.(NssPBE).Encrypted, pbe.(NssPBE).IterationCount, pbe.(NssPBE).KeySize) - case MetaPBE: - return decryptMeta(globalSalt, masterPwd, pbe.(MetaPBE).EntrySalt, pbe.(MetaPBE).Encrypted) - default: - return nil, errors.New("decrypt nss failed") - } -} - -func decryptMeta(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, error) { - // byte[] GLMP; // GlobalSalt + MasterPassword - // byte[] HP; // SHA1(GLMP) - // byte[] HPES; // HP + EntrySalt - // byte[] CHP; // SHA1(HPES) - // byte[] PES; // EntrySalt completed to 20 bytes by zero - // byte[] PESES; // PES + EntrySalt - // byte[] k1; - // byte[] tk; - // byte[] k2; - // byte[] k; // final value conytaining key and iv - glmp := append(globalSalt, masterPwd...) - hp := sha1.Sum(glmp) - s := append(hp[:], entrySalt...) - chp := sha1.Sum(s) - pes := PaddingZero(entrySalt, 20) - tk := hmac.New(sha1.New, chp[:]) - tk.Write(pes) - pes = append(pes, entrySalt...) - k1 := hmac.New(sha1.New, chp[:]) - k1.Write(pes) - tkPlus := append(tk.Sum(nil), entrySalt...) - k2 := hmac.New(sha1.New, chp[:]) - k2.Write(tkPlus) - k := append(k1.Sum(nil), k2.Sum(nil)...) - iv := k[len(k)-8:] - key := k[:24] - log.Warnf("key=%s iv=%s", hex.EncodeToString(key), hex.EncodeToString(iv)) - return Des3Decrypt(key, iv, encrypted) -} - -func decryptNss(globalSalt, masterPwd, nssIv, entrySalt, encrypted []byte, iter, keySize int) ([]byte, error) { - k := sha1.Sum(globalSalt) - log.Debug(hex.EncodeToString(k[:])) - key := pbkdf2.Key(k[:], entrySalt, iter, keySize, sha256.New) - log.Debug(hex.EncodeToString(key)) - i, err := hex.DecodeString("040e") - if err != nil { - log.Debug(err) - } - // @https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 - iv := append(i, nssIv...) - dst, err := aes128CBCDecrypt(key, iv, encrypted) - if err != nil { - log.Debug(err) - } - return dst, err -} diff --git a/core/decrypt/decrypt_windows.go b/core/decrypt/decrypt_windows.go index 24ad7a2..4f23271 100644 --- a/core/decrypt/decrypt_windows.go +++ b/core/decrypt/decrypt_windows.go @@ -3,16 +3,8 @@ package decrypt import ( "crypto/aes" "crypto/cipher" - "crypto/sha1" - "crypto/sha256" - "encoding/asn1" - "encoding/hex" "syscall" "unsafe" - - "hack-browser-data/log" - - "golang.org/x/crypto/pbkdf2" ) func ChromePass(key, encryptPass []byte) ([]byte, error) { @@ -76,87 +68,3 @@ func DPApi(data []byte) ([]byte, error) { defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData))) return outBlob.ToByteArray(), nil } - -type NssPBE struct { - SequenceA - Encrypted []byte -} - -type MetaPBE struct { - SequenceA - Encrypted []byte -} -type SequenceA struct { - PKCS5PBES2 asn1.ObjectIdentifier - SequenceB -} -type SequenceB struct { - SequenceC - SequenceD -} - -type SequenceC struct { - PKCS5PBKDF2 asn1.ObjectIdentifier - SequenceE -} - -type SequenceD struct { - AES256CBC asn1.ObjectIdentifier - IV []byte -} - -type SequenceE struct { - EntrySalt []byte - IterationCount int - KeySize int - SequenceF -} - -type SequenceF struct { - HMACWithSHA256 asn1.ObjectIdentifier -} - -func DecodeMeta(decodeItem []byte) (pbe MetaPBE, err error) { - _, err = asn1.Unmarshal(decodeItem, &pbe) - if err != nil { - log.Error(err) - return - } - return -} - -func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) { - log.Debug(hex.EncodeToString(nssA11Bytes)) - _, err = asn1.Unmarshal(nssA11Bytes, &pbe) - if err != nil { - log.Error(err) - return - } - return -} - -func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { - return decryptMeta(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize) -} - -func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { - return decryptMeta(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize) -} - -func decryptMeta(globalSalt, masterPwd, nssIv, entrySalt, encrypted []byte, iter, keySize int) ([]byte, error) { - k := sha1.Sum(globalSalt) - log.Debug(hex.EncodeToString(k[:])) - key := pbkdf2.Key(k[:], entrySalt, iter, keySize, sha256.New) - log.Debug(hex.EncodeToString(key)) - i, err := hex.DecodeString("040e") - if err != nil { - log.Debug(err) - } - // @https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 - iv := append(i, nssIv...) - dst, err := aes128CBCDecrypt(key, iv, encrypted) - if err != nil { - log.Debug(err) - } - return dst, err -}