feat: add csv and json output

pull/83/head
ᴍᴏᴏɴD4ʀᴋ 4 years ago
parent 5bfa781466
commit 55bfd89fa9
  1. 183
      .gitignore
  2. 46
      cmd/cmd.go
  3. 136
      core/common/common.go
  4. 1
      go.mod
  5. 2
      go.sum
  6. 16
      main.go
  7. 48
      utils/utils.go
  8. 12
      utils/utils_darwin.go

183
.gitignore vendored

@ -0,0 +1,183 @@
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# idea
.idea/
.idea
# windows
*.exe
# macOS
# binary
cmd/agent
cmd/server
# bin
# file
*.csv
*.xlsx
*.txt
# config file
config.toml
*.json
Bookmarks
Login Data
Cookies
hack-browser-data
History

@ -1,9 +1,11 @@
package cmd package cmd
import ( import (
"hack-browser-data/core/common"
"hack-browser-data/log" "hack-browser-data/log"
"hack-browser-data/utils" "hack-browser-data/utils"
"os" "os"
"path/filepath"
"runtime" "runtime"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -20,31 +22,57 @@ var (
func Execute() { func Execute() {
app := &cli.App{ app := &cli.App{
Name: "hack-browser-data", Name: "hack-browser-data",
Usage: "export password/cookie/history/bookmark from browser", Usage: "export passwords/cookies/history/bookmarks from browser",
Version: "0.0.1", Version: "0.1.0",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"}, &cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"},
&cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browser, Value: "all", Usage: "browser name, all|chrome|safari"}, &cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browser, Value: "all", Usage: "browser name, all|chrome|safari"},
&cli.StringFlag{Name: "dir", Aliases: []string{"d"}, Destination: &exportDir, Value: "data", Usage: "export dir"}, &cli.StringFlag{Name: "results-dir", Aliases: []string{"d"}, Destination: &exportDir, Value: "results", Usage: "export dir"},
&cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "result format, csv|json"}, &cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "result format, csv|json"},
&cli.StringFlag{Name: "export-data", Aliases: []string{"e"}, Destination: &exportData, Value: "all", Usage: "all|password|cookie|history|bookmark"}, &cli.StringFlag{Name: "export-data", Aliases: []string{"e"}, Destination: &exportData, Value: "all", Usage: "all|password|cookie|history|bookmark"},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
log.InitLog() log.InitLog()
utils.MakeDir(exportDir)
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":
err := utils.InitChromeKey() err := utils.InitChromeKey()
if err != nil { if err != nil {
} }
case "windows": case "windows":
}
var fileList []string
switch exportData {
case "all":
fileList = utils.GetDBPath(utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies)
case "password", "cookie", "history", "bookmark":
fileList = utils.GetDBPath(exportData)
default:
log.Fatal("choose one all|password|cookie|history|bookmark")
}
for _, v := range fileList {
dst := filepath.Base(v)
err := utils.CopyDB(v, dst)
if err != nil {
log.Println(err)
continue
} }
switch { common.ParseDB(dst)
case browser == "all":
case exportDir == "data":
case exportData == "all":
case outputFormat == "json":
} }
if outputFormat == "json" {
err := common.FullData.OutPutJson(exportDir, outputFormat)
if err != nil {
log.Error(err)
}
} else {
err := common.FullData.OutPutCsv(exportDir, outputFormat)
if err != nil {
log.Error(err)
}
}
return nil return nil
}, },
} }

@ -1,11 +1,16 @@
package common package common
import ( import (
"bytes"
"database/sql" "database/sql"
"encoding/json"
"hack-browser-data/log" "hack-browser-data/log"
"hack-browser-data/utils" "hack-browser-data/utils"
"os"
"time" "time"
"github.com/gocarina/gocsv"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@ -35,6 +40,7 @@ const (
type ( type (
BrowserData struct { BrowserData struct {
BrowserName string BrowserName string
OutPutType string
LoginData []*LoginData LoginData []*LoginData
Bookmarks []*Bookmarks Bookmarks []*Bookmarks
Cookies []*Cookies Cookies []*Cookies
@ -42,7 +48,7 @@ type (
} }
LoginData struct { LoginData struct {
UserName string `json:"user_name"` UserName string `json:"user_name"`
encryptPass []byte `json:"-"` encryptPass []byte
Password string `json:"password"` Password string `json:"password"`
LoginUrl string `json:"login_url"` LoginUrl string `json:"login_url"`
CreateDate time.Time `json:"create_date"` CreateDate time.Time `json:"create_date"`
@ -75,6 +81,129 @@ type (
} }
) )
func (b BrowserData) OutPutCsv(dir, format string) error {
switch {
case len(b.Bookmarks) != 0:
filename := utils.FormatFileName(dir, utils.Bookmarks, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
err = gocsv.MarshalFile(b.Bookmarks, file)
if err != nil {
log.Error(err)
}
fallthrough
case len(b.LoginData) != 0:
filename := utils.FormatFileName(dir, utils.LoginData, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
err = gocsv.MarshalFile(b.LoginData, file)
if err != nil {
log.Error(err)
}
fallthrough
case len(b.Cookies) != 0:
filename := utils.FormatFileName(dir, utils.Cookies, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
err = gocsv.MarshalFile(b.Cookies, file)
if err != nil {
log.Error(err)
}
fallthrough
case len(b.History) != 0:
filename := utils.FormatFileName(dir, utils.History, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
err = gocsv.MarshalFile(b.History, file)
if err != nil {
log.Error(err)
}
}
return nil
}
func (b BrowserData) OutPutJson(dir, format string) error {
switch {
case len(b.Bookmarks) != 0:
filename := utils.FormatFileName(dir, utils.Bookmarks, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
enc.Encode(b.BrowserName)
file.Write(w.Bytes())
fallthrough
case len(b.Cookies) != 0:
filename := utils.FormatFileName(dir, utils.Cookies, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(b.Cookies)
if err != nil {
log.Println(err)
}
file.Write(w.Bytes())
fallthrough
case len(b.History) != 0:
filename := utils.FormatFileName(dir, utils.History, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(b.History)
if err != nil {
log.Println(err)
}
file.Write(w.Bytes())
fallthrough
case len(b.LoginData) != 0:
filename := utils.FormatFileName(dir, utils.LoginData, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(b.LoginData)
if err != nil {
log.Println(err)
}
file.Write(w.Bytes())
}
return nil
}
func ParseDB(dbname string) { func ParseDB(dbname string) {
switch dbname { switch dbname {
case utils.LoginData: case utils.LoginData:
@ -107,7 +236,6 @@ func parseBookmarks() {
var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins` var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins`
func parseLogin() { func parseLogin() {
//datetime(visit_time / 1000000 + (strftime('%s', '1601-01-01')), 'unixepoch')
login := &LoginData{} login := &LoginData{}
loginDB, err := sql.Open("sqlite3", utils.LoginData) loginDB, err := sql.Open("sqlite3", utils.LoginData)
defer func() { defer func() {
@ -201,7 +329,7 @@ func parseCookie() {
FullData.Cookies = cookieList FullData.Cookies = cookieList
} }
var queryUrl = `SELECT url, title, visit_count, last_visit_time FROM urls` var queryHistory = `SELECT url, title, visit_count, last_visit_time FROM urls`
func parseHistory() { func parseHistory() {
history := &History{} history := &History{}
@ -215,7 +343,7 @@ func parseHistory() {
log.Println(err) log.Println(err)
} }
err = historyDB.Ping() err = historyDB.Ping()
rows, err := historyDB.Query(queryUrl) rows, err := historyDB.Query(queryHistory)
defer func() { defer func() {
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {
log.Println(err) log.Println(err)

@ -3,6 +3,7 @@ module hack-browser-data
go 1.14 go 1.14
require ( require (
github.com/gocarina/gocsv v0.0.0-20200330101823-46266ca37bd3
github.com/mattn/go-sqlite3 v1.14.0 github.com/mattn/go-sqlite3 v1.14.0
github.com/tidwall/gjson v1.6.0 github.com/tidwall/gjson v1.6.0
github.com/urfave/cli/v2 v2.2.0 github.com/urfave/cli/v2 v2.2.0

@ -7,6 +7,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gocarina/gocsv v0.0.0-20200330101823-46266ca37bd3 h1:B7k6N+JlLM/u1xrIkpifUfE7GRJsZIYHoHbiAa5cSP4=
github.com/gocarina/gocsv v0.0.0-20200330101823-46266ca37bd3/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=

@ -1,7 +1,9 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"hack-browser-data/cmd"
"hack-browser-data/core/common" "hack-browser-data/core/common"
"hack-browser-data/log" "hack-browser-data/log"
"hack-browser-data/utils" "hack-browser-data/utils"
@ -10,8 +12,7 @@ import (
) )
func main() { func main() {
log.InitLog() cmd.Execute()
parse()
} }
func parse() { func parse() {
@ -26,11 +27,7 @@ func parse() {
case "windows": case "windows":
fmt.Println("Windows") fmt.Println("Windows")
} }
chromePath, err := utils.GetDBPath(utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies) chromePath := utils.GetDBPath(utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies)
//chromePath, err := utils.GetDBPath(utils.Cookies)
if err != nil {
log.Error("can't find chrome.app in OS")
}
for _, v := range chromePath { for _, v := range chromePath {
dst := filepath.Base(v) dst := filepath.Base(v)
err := utils.CopyDB(v, dst) err := utils.CopyDB(v, dst)
@ -44,4 +41,9 @@ func parse() {
fmt.Println("cookies", len(common.FullData.Cookies)) fmt.Println("cookies", len(common.FullData.Cookies))
fmt.Println("login data", len(common.FullData.LoginData)) fmt.Println("login data", len(common.FullData.LoginData))
fmt.Println("history", len(common.FullData.History)) fmt.Println("history", len(common.FullData.History))
d, err := json.MarshalIndent(common.FullData.Bookmarks, "", "\t")
err = utils.WriteFile("data.json", d)
if err != nil {
log.Println(err)
}
} }

@ -1,10 +1,13 @@
package utils package utils
import ( import (
"fmt"
"hack-browser-data/log" "hack-browser-data/log"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings"
"time" "time"
) )
@ -29,7 +32,6 @@ func CopyDB(src, dst string) error {
if err != nil { if err != nil {
log.Println(err.Error()) log.Println(err.Error())
} }
err = ioutil.WriteFile(dst, sourceFile, 0777) err = ioutil.WriteFile(dst, sourceFile, 0777)
if err != nil { if err != nil {
log.Println(err.Error()) log.Println(err.Error())
@ -37,14 +39,6 @@ func CopyDB(src, dst string) error {
return err return err
} }
func ParseBookMarks() {
}
func RemoveFile() {
}
func IntToBool(a int) bool { func IntToBool(a int) bool {
switch a { switch a {
case 0, -1: case 0, -1:
@ -54,6 +48,10 @@ func IntToBool(a int) bool {
} }
func TimeEpochFormat(epoch int64) time.Time { func TimeEpochFormat(epoch int64) time.Time {
maxTime := int64(99633311740000000)
if epoch > maxTime{
epoch = maxTime
}
t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC) t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
d := time.Duration(epoch) d := time.Duration(epoch)
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
@ -62,11 +60,37 @@ func TimeEpochFormat(epoch int64) time.Time {
return t return t
} }
// check time our range[1.9999]
func checkTimeRange(check time.Time) time.Time {
end, _ := time.Parse(time.RFC3339, "9000-01-02T15:04:05Z07:00")
if check.Before(end) {
return check
} else {
return end
}
}
func ReadFile(filename string) (string, error) { func ReadFile(filename string) (string, error) {
s, err := ioutil.ReadFile(filename) s, err := ioutil.ReadFile(filename)
return string(s), err return string(s), err
} }
//func MakeDir(dirName string) error { func WriteFile(filename string, data []byte) error {
// err := ioutil.WriteFile(filename, data, 0644)
//} if err != nil {
return nil
}
return err
}
func FormatFileName(dir, filename, format string) string {
r := strings.TrimSpace(strings.ToLower(filename))
p := path.Join(dir, fmt.Sprintf("%s.%s", r, format))
return p
}
func MakeDir(dirName string) {
if _, err := os.Stat(dirName); os.IsNotExist(err) {
err = os.Mkdir(dirName, 0700)
}
}

@ -25,17 +25,19 @@ var (
chromePass []byte chromePass []byte
) )
func GetDBPath(dbName ...string) (dbFile []string, err error) { func GetDBPath(dbName ...string) (dbFile []string) {
for _, v := range dbName { for _, v := range dbName {
s, err := filepath.Glob(macChromeDir + v) s, err := filepath.Glob(macChromeDir + v)
if err != nil && len(s) == 0 { if err != nil && len(s) == 0 {
continue continue
} }
if len(s) > 0{ if len(s) > 0 {
log.Debugf("Find %s File Success", v)
log.Debugf("%s file location is %s", v, s[0])
dbFile = append(dbFile, s[0]) dbFile = append(dbFile, s[0])
} }
} }
return dbFile, nil return dbFile
} }
func InitChromeKey() error { func InitChromeKey() error {
@ -52,8 +54,8 @@ func InitChromeKey() error {
return err return err
} }
if stderr.Len() > 0 { if stderr.Len() > 0 {
log.Println(stderr.String())
err = errors.New(stderr.String()) err = errors.New(stderr.String())
log.Println(err)
} }
temp := stdout.Bytes() temp := stdout.Bytes()
chromePass = temp[:len(temp)-1] chromePass = temp[:len(temp)-1]
@ -66,7 +68,7 @@ func decryptPass(chromePass []byte) {
} }
func Aes128CBCDecrypt(encryptPass []byte) (string, error) { func Aes128CBCDecrypt(encryptPass []byte) (string, error) {
if chromeKey == nil { if len(chromeKey) == 0 {
return "", nil return "", nil
} }
block, err := aes.NewCipher(chromeKey) block, err := aes.NewCipher(chromeKey)

Loading…
Cancel
Save