Skip to content

Commit

Permalink
Add token decryption for Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
cedws committed Oct 15, 2022
1 parent 31f5d25 commit 8918f43
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 12 deletions.
5 changes: 5 additions & 0 deletions client/token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (
"github.com/syndtr/goleveldb/leveldb/opt"
)

const (
v10Prefix = "v10"
dpapiPrefix = "DPAPI"
)

var (
ErrorTokenRetrieve = errors.New("token: error retrieving token from database")
ErrorTokenPlatform = errors.New("token: retrieval not supported on this platform yet")
Expand Down
23 changes: 12 additions & 11 deletions client/token/token_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ func GetToken() (string, error) {
safeTokens, err := getSafeStorageTokens(path)
if err != nil {
// try another database
log.Debug(err)
log.Error(err)
continue
}

key, err := getDecryptionKey()
if err != nil {
log.Error(err)
continue
}

for _, safeToken := range safeTokens {
// strip rickroll
safeToken := strings.TrimPrefix(safeToken, "dQw4w9WgXcQ:")
token, err := decryptToken(safeToken)
token, err := decryptToken(key, safeToken)
if err != nil {
// try next token
log.Error(err)
Expand Down Expand Up @@ -74,24 +80,19 @@ func getDecryptionKey() ([]byte, error) {
return key, nil
}

func decryptToken(safeToken string) (string, error) {
func decryptToken(key []byte, safeToken string) (string, error) {
safeTokenBytes, err := base64.StdEncoding.DecodeString(safeToken)
if err != nil {
return "", fmt.Errorf("token: error decoding safeStorage token")
}

decryptionKey, err := getDecryptionKey()
if err != nil {
return "", err
return "", fmt.Errorf("token: error decoding safeStorage token: %w", err)
}

block, err := aes.NewCipher(decryptionKey)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}

iv := bytes.Repeat([]byte{' '}, 16)
ciphertext := safeTokenBytes[3:]
ciphertext := safeTokenBytes[len(v10Prefix):]

cbc := cipher.NewCBCDecrypter(block, iv)
cbc.CryptBlocks(ciphertext, ciphertext)
Expand Down
2 changes: 1 addition & 1 deletion client/token/token_unknown.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !darwin
//go:build !darwin && !windows

package token

Expand Down
119 changes: 119 additions & 0 deletions client/token/token_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package token

import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/billgraziano/dpapi"
log "github.com/sirupsen/logrus"
)

type LocalState struct {
OsCrypt struct {
EncryptedKey string `json:"encrypted_key"`
} `json:"os_crypt"`
}

var versions = []string{"Discord", "discordcanary", "discordptb"}

func GetToken() (string, error) {
log.Warnf("discord must not be running to retrieve your token under %v", runtime.GOOS)

appdata, def := os.LookupEnv("APPDATA")
if !def {
return "", ErrorNoAppdataPath
}

for _, ver := range versions {
path := filepath.Join(appdata, ver, "Local Storage/leveldb")
log.Debugf("searching for leveldb database in %v", path)

safeTokens, err := getSafeStorageTokens(path)
if err != nil {
// try another database
log.Error(err)
continue
}

path = filepath.Join(appdata, ver, "Local State")
key, err := getDecryptionKey(path)
if err != nil {
log.Error(err)
continue
}

for _, safeToken := range safeTokens {
// strip rickroll
safeToken := strings.TrimPrefix(safeToken, "dQw4w9WgXcQ:")
token, err := decryptToken(key, safeToken)
if err != nil {
// try next token
log.Error(err)
continue
}

return strings.Trim(token, "\n"), nil
}
}

return "", ErrorTokenRetrieve
}

func getDecryptionKey(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("token: error reading local state: %w", err)
}
defer file.Close()

var localState LocalState
if err := json.NewDecoder(file).Decode(&localState); err != nil {
return nil, fmt.Errorf("token: error unmarshaling local state: %w", err)
}

decoded, err := base64.StdEncoding.DecodeString(localState.OsCrypt.EncryptedKey)
if err != nil {
return nil, fmt.Errorf("token: error decoding: %w", err)
}

decrypted, err := dpapi.DecryptBytes(decoded[len(dpapiPrefix):])
if err != nil {
return nil, fmt.Errorf("token: error decrypting with dpapi: %w", err)
}

return []byte(decrypted), nil
}

func decryptToken(key []byte, safeToken string) (string, error) {
safeTokenBytes, err := base64.StdEncoding.DecodeString(safeToken)
if err != nil {
return "", fmt.Errorf("token: error decoding safeStorage token: %w", err)
}

block, err := aes.NewCipher(key)
if err != nil {
return "", err
}

aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}

nonce := safeTokenBytes[len(v10Prefix) : len(v10Prefix)+12]
ciphertext := safeTokenBytes[len(v10Prefix)+12:]

plaintext, err := aesgcm.Open(nil, []byte(nonce), []byte(ciphertext), nil)
if err != nil {
return "", err
}

return string(plaintext), nil
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/cedws/discord-delete
go 1.18

require (
github.com/billgraziano/dpapi v0.4.0
github.com/keybase/go-keychain v0.0.0-20220610143837-c2ce06069005
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
Expand All @@ -15,6 +16,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20220325203850-36772127a21f // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
github.com/billgraziano/dpapi v0.4.0 h1:t39THI1Ld1hkkLVrhkOX6u5TUxwzRddOffq4jcwh2AE=
github.com/billgraziano/dpapi v0.4.0/go.mod h1:gi1Lin0jvovT53j0EXITkY6UPb3hTfI92POaZgj9JBA=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
Expand Down Expand Up @@ -32,6 +35,8 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand Down Expand Up @@ -66,6 +71,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200828161417-c663848e9a16/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=
golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down

0 comments on commit 8918f43

Please sign in to comment.