<# .SYNOPSIS NCO Toolbox Remote Launcher .DESCRIPTION Downloads and runs the NCO Admin Toolbox from a private GitHub repo. Usage: irm https://toolbox.nxcore.hu | iex The user enters a short memorable password that decrypts the stored GitHub PAT, which is then used to download the repo as a zip file. No git installation required. Files are stored in %LOCALAPPDATA%\NCO-Toolbox and overwritten on each run. #> # ============================================================ # CONFIG — Paste your encrypted PAT here (from encrypt-pat.ps1) # ============================================================ $EncryptedPAT = "y8PkMAysuFZJIAx0FuFOLcbWNecr69+Ya7b4kXCChSEii4LVj0IVWNf8MA+2FBCwdFmMc/XZ/8jnkbf2LKenYlnCNOHnuiFEd928XrDTZktlZyVsokYXNlgSDxQ5sJn6fTwuaOmplmFVdDHbKQZtbg==" $RepoOwner = "subs-dash" $RepoName = "nco-admin-toolbox-pwsh" $Branch = "main" # ============================================================ # --- Box rendering (inner width = 32, computed padding) --- $boxW = 32 $d = [char]0x2500 $titleText = "$d$d NCO Admin Toolbox " $midText = " Remote Launcher" $hrTop = " $([char]0x250C)$titleText$([string]::new($d, $boxW - $titleText.Length))$([char]0x2510)" $hrMid = " $([char]0x2502)$midText$(' ' * ($boxW - $midText.Length))$([char]0x2502)" $hrBottom = " $([char]0x2514)$([string]::new($d, $boxW))$([char]0x2518)" Write-Host "" Write-Host $hrTop -ForegroundColor Cyan Write-Host $hrMid -ForegroundColor Cyan Write-Host $hrBottom -ForegroundColor Cyan Write-Host "" # --- 1. Password prompt --- $securePass = Read-Host " Unlock password" -AsSecureString $password = [Runtime.InteropServices.Marshal]::PtrToStringAuto( [Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePass) ) if ($password.Length -lt 8) { Write-Host " [!] Invalid password." -ForegroundColor Red return } # --- 2. Decrypt PAT --- Write-Host " Authenticating..." -ForegroundColor DarkGray try { $combined = [Convert]::FromBase64String($EncryptedPAT) $iv = $combined[0..15] $cipherText = $combined[16..($combined.Length - 1)] $sha = [System.Security.Cryptography.SHA256]::Create() $keyBytes = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($password)) $sha.Dispose() $password = $null $aes = [System.Security.Cryptography.Aes]::Create() $aes.Key = $keyBytes $aes.IV = $iv $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC $aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 $decryptor = $aes.CreateDecryptor() $decrypted = $decryptor.TransformFinalBlock($cipherText, 0, $cipherText.Length) $pat = [System.Text.Encoding]::UTF8.GetString($decrypted) $decryptor.Dispose() $aes.Dispose() if ($pat.Length -lt 10) { throw "Decrypted value too short" } } catch { Write-Host " [!] Wrong password or corrupted data." -ForegroundColor Red return } Write-Host " $([char]0x2714) Authenticated" -ForegroundColor Green # --- 2b. Launch mode + temp base --- function Resolve-NcoTempBase { # Primary C:\ProgramData\.nco-temp (creatable by a normal user, ASCII-safe), # fallback %USERPROFILE%\Downloads\.nco-temp, last resort %TEMP%. $candidates = @( (Join-Path $env:ProgramData ".nco-temp"), (Join-Path $env:USERPROFILE "Downloads\.nco-temp") ) foreach ($base in $candidates) { try { if (-not (Test-Path $base)) { New-Item -Path $base -ItemType Directory -Force -ErrorAction Stop | Out-Null } $probe = Join-Path $base ".write-probe" [System.IO.File]::WriteAllText($probe, "x") Remove-Item $probe -Force -ErrorAction SilentlyContinue return $base } catch { continue } } return $env:TEMP } Write-Host "" Write-Host " [R] Run " -NoNewline -ForegroundColor Cyan; Write-Host "download to temp, run, auto-clean on exit" -ForegroundColor Gray Write-Host " [D] Download " -NoNewline -ForegroundColor Cyan; Write-Host "pick a folder, run from there, auto-clean on exit" -ForegroundColor Gray Write-Host "" $modeChoice = Read-Host " Select mode (R/D) [default R]" if ($modeChoice -match '^[Dd]') { $target = (Read-Host " Target folder (full path)").Trim().Trim('"') if ([string]::IsNullOrWhiteSpace($target)) { Write-Host " [!] No folder given - using Run mode." -ForegroundColor Yellow $baseTemp = Resolve-NcoTempBase } else { try { if (-not (Test-Path $target)) { New-Item -Path $target -ItemType Directory -Force -ErrorAction Stop | Out-Null } $baseTemp = $target } catch { Write-Host " [!] Cannot use that folder ($_). Using Run mode." -ForegroundColor Yellow $baseTemp = Resolve-NcoTempBase } } } else { $baseTemp = Resolve-NcoTempBase } Write-Host " Temp base: $baseTemp" -ForegroundColor DarkGray # --- 3. Download repo as zip --- # All ephemeral files live under $baseTemp; the install goes in an 'NCO-Toolbox' # subfolder that is the ONLY thing we auto-delete (never the user's chosen parent). $installDir = Join-Path $baseTemp "NCO-Toolbox" $zipPath = Join-Path $baseTemp "nco-toolbox-dl.zip" # Export for the toolbox process (in-process launch inherits these). $env:NCO_TEMP = $baseTemp $env:NCO_EPHEMERAL_DIR = $installDir Write-Host " Downloading toolbox..." -ForegroundColor DarkGray try { $headers = @{ Authorization = "Bearer $pat" Accept = "application/vnd.github+json" "X-GitHub-Api-Version" = "2022-11-28" } $zipUrl = "https://api.github.com/repos/$RepoOwner/$RepoName/zipball/$Branch`?t=$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())" $pat = $null Invoke-WebRequest -Uri $zipUrl -Headers $headers -OutFile $zipPath -UseBasicParsing } catch { Write-Host " [!] Download failed: $_" -ForegroundColor Red if (Test-Path $zipPath) { Remove-Item $zipPath -Force -ErrorAction SilentlyContinue } return } Write-Host " $([char]0x2714) Downloaded" -ForegroundColor Green # --- 4. Extract to stable short path --- Write-Host " Extracting..." -ForegroundColor DarkGray try { # Extract to temp first $tmpExtract = Join-Path $baseTemp "nco-extract-tmp" if (Test-Path $tmpExtract) { Remove-Item $tmpExtract -Recurse -Force } Expand-Archive -Path $zipPath -DestinationPath $tmpExtract -Force Remove-Item $zipPath -Force -ErrorAction SilentlyContinue # GitHub zip creates a subfolder like "owner-repo-hash/" — find it $innerDir = Get-ChildItem -Path $tmpExtract -Directory | Select-Object -First 1 if (-not $innerDir) { throw "No directory found in archive" } # Copy contents to stable short path (overwrite previous version) if (Test-Path $installDir) { Remove-Item $installDir -Recurse -Force } Copy-Item -Path $innerDir.FullName -Destination $installDir -Recurse -Force # Clean up temp extract Remove-Item $tmpExtract -Recurse -Force -ErrorAction SilentlyContinue $scriptPath = Join-Path $installDir "nco-toolbox.ps1" if (-not (Test-Path $scriptPath)) { throw "nco-toolbox.ps1 not found at: $installDir" } # Drop the ephemeral marker (holds the temp base) so the toolbox knows it is # loader-managed and self-cleans — this survives elevation relaunches. [System.IO.File]::WriteAllText((Join-Path $installDir ".nco-ephemeral"), $baseTemp, [System.Text.Encoding]::UTF8) } catch { Write-Host " [!] Extraction failed: $_" -ForegroundColor Red return } Write-Host " $([char]0x2714) Ready" -ForegroundColor Green Write-Host "" # --- 5. Run the toolbox --- try { & $scriptPath } catch { Write-Host " [!] Toolbox error: $_" -ForegroundColor Red } # --- 6. Cleanup & Done --- # The toolbox has exited (either directly or via admin relaunch). $relaunchFile = Join-Path $baseTemp "nco-relaunch.ps1" $wtLaunchFile = Join-Path $baseTemp "nco-wt-admin.ps1" if (Test-Path $relaunchFile) { # Admin relaunch happened — the elevated session is still running from $installDir. # DON'T delete the folder now. The elevated session's own cleanup will handle temp files. # Schedule a delayed cleanup that waits for the elevated pwsh to finish. try { $shell = (Get-Process -Id $PID).Path $escapedDir = $installDir -replace "'", "''" $escapedRelaunch = $relaunchFile -replace "'", "''" $escapedWt = $wtLaunchFile -replace "'", "''" # Wait until the relaunch file is gone (elevated session cleans it) or timeout after 2 hours $cleanupScript = @" `$timeout = (Get-Date).AddHours(2) while ((Test-Path '$escapedRelaunch') -and (Get-Date) -lt `$timeout) { Start-Sleep -Seconds 5 } Start-Sleep -Seconds 3 Remove-Item -Path '$escapedDir' -Recurse -Force -ErrorAction SilentlyContinue Remove-Item -Path '$escapedRelaunch' -Force -ErrorAction SilentlyContinue Remove-Item -Path '$escapedWt' -Force -ErrorAction SilentlyContinue "@ $cleanupScriptPath = Join-Path $baseTemp "nco-deferred-cleanup.ps1" [System.IO.File]::WriteAllText($cleanupScriptPath, $cleanupScript, [System.Text.Encoding]::UTF8) Start-Process $shell -ArgumentList "-NoProfile -WindowStyle Hidden -File `"$cleanupScriptPath`"" -ErrorAction SilentlyContinue } catch { } } else { # Normal exit (no relaunch) — schedule immediate folder removal. try { $shell = (Get-Process -Id $PID).Path $cleanupCmd = "Start-Sleep -Seconds 2; Remove-Item -Path '$installDir' -Recurse -Force -ErrorAction SilentlyContinue" Start-Process $shell -ArgumentList "-NoProfile -WindowStyle Hidden -Command `"$cleanupCmd`"" -ErrorAction SilentlyContinue } catch { } } # Clean download zip if still around $dlZip = Join-Path $baseTemp "nco-toolbox-dl.zip" if (Test-Path $dlZip) { Remove-Item $dlZip -Force -ErrorAction SilentlyContinue } Write-Host "" Write-Host " $([char]0x2714) Session ended." -ForegroundColor Green