Skip to content

Instantly share code, notes, and snippets.

@xyb
Forked from rwifeng/cookiemonster.go
Last active February 3, 2019 15:41
Show Gist options
  • Select an option

  • Save xyb/81321ecb45da94e44723ca973e4b3a13 to your computer and use it in GitHub Desktop.

Select an option

Save xyb/81321ecb45da94e44723ca973e4b3a13 to your computer and use it in GitHub Desktop.

Revisions

  1. xyb revised this gist Feb 3, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion cookiemonster.go
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    package main

    import (
    "code.google.com/p/go.crypto/pbkdf2"
    "golang.org/x/crypto/pbkdf2"
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha1"
  2. @dacort dacort created this gist Sep 23, 2014.
    141 changes: 141 additions & 0 deletions cookiemonster.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,141 @@
    package main

    import (
    "code.google.com/p/go.crypto/pbkdf2"
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha1"
    "database/sql"
    "fmt"
    "log"
    "os"
    "os/exec"
    "os/user"
    "strings"

    _ "github.com/mattn/go-sqlite3"
    )

    // Inpsiration
    // http://n8henrie.com/2013/11/use-chromes-cookies-for-easier-downloading-with-python-requests/

    // Chromium Mac os_crypt: http://dacort.me/1ynPMgx
    var (
    salt = "saltysalt"
    iv = " "
    length = 16
    password = ""
    iterations = 1003
    )

    // Cookie - Items for a cookie
    type Cookie struct {
    Domain string
    Key string
    Value string
    EncryptedValue []byte
    }

    // DecryptedValue - Get the unencrypted value of a Chrome cookie
    func (c *Cookie) DecryptedValue() string {
    if c.Value > "" {
    return c.Value
    }

    if len(c.EncryptedValue) > 0 {
    encryptedValue := c.EncryptedValue[3:]
    return decryptValue(encryptedValue)
    }

    return ""
    }

    func usage() {
    fmt.Fprintf(os.Stderr, "usage: %s [domain]\n", os.Args[0])
    os.Exit(2)
    }

    func main() {
    if len(os.Args) != 2 {
    usage()
    }

    domain := os.Args[1]
    password = getPassword()

    // TODO: Output in Netscape format
    for _, cookie := range getCookies(domain) {
    fmt.Printf("%s/%s: %s\n", cookie.Domain, cookie.Key, cookie.DecryptedValue())
    }
    }

    func decryptValue(encryptedValue []byte) string {
    key := pbkdf2.Key([]byte(password), []byte(salt), iterations, length, sha1.New)
    block, err := aes.NewCipher(key)
    if err != nil {
    log.Fatal(err)
    }

    decrypted := make([]byte, len(encryptedValue))
    cbc := cipher.NewCBCDecrypter(block, []byte(iv))
    cbc.CryptBlocks(decrypted, encryptedValue)

    plainText, err := aesStripPadding(decrypted)
    if err != nil {
    fmt.Println("Error decrypting:", err)
    return ""
    }
    return string(plainText)
    }

    // In the padding scheme the last <padding length> bytes
    // have a value equal to the padding length, always in (1,16]
    func aesStripPadding(data []byte) ([]byte, error) {
    if len(data)%length != 0 {
    return nil, fmt.Errorf("decrypted data block length is not a multiple of %d", length)
    }
    paddingLen := int(data[len(data)-1])
    if paddingLen > 16 {
    return nil, fmt.Errorf("invalid last block padding length: %d", paddingLen)
    }
    return data[:len(data)-paddingLen], nil
    }

    func getPassword() string {
    parts := strings.Fields("security find-generic-password -wga Chrome")
    cmd := parts[0]
    parts = parts[1:len(parts)]

    out, err := exec.Command(cmd, parts...).Output()
    if err != nil {
    log.Fatal("error finding password ", err)
    }

    return strings.Trim(string(out), "\n")
    }

    func getCookies(domain string) (cookies []Cookie) {
    usr, _ := user.Current()
    cookiesFile := fmt.Sprintf("%s/Library/Application Support/Google/Chrome/Default/Cookies", usr.HomeDir)

    db, err := sql.Open("sqlite3", cookiesFile)
    if err != nil {
    log.Fatal(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT name, value, host_key, encrypted_value FROM cookies WHERE host_key like ?", fmt.Sprintf("%%%s%%", domain))
    if err != nil {
    log.Fatal(err)
    }

    defer rows.Close()
    for rows.Next() {
    var name, value, hostKey string
    var encryptedValue []byte
    rows.Scan(&name, &value, &hostKey, &encryptedValue)
    cookies = append(cookies, Cookie{hostKey, name, value, encryptedValue})
    }

    return
    }