param( [string]$PagerHost = "172.16.52.1", [string]$User = "root", [string]$Password = "", [string]$SshKey = "", [string]$BuildDir = "", [switch]$NoPortalRefresh ) $ErrorActionPreference = "Stop" $Root = Split-Path -Parent $PSScriptRoot $PayloadKey = "pager-webui" $PayloadCategory = "remote_access" $PayloadDir = Join-Path $Root "payload\user\$PayloadCategory\$PayloadKey" if (-not (Test-Path $PayloadDir)) { throw "Payload dir not found: $PayloadDir" } if (-not $BuildDir) { $BuildDir = Join-Path $Root "build" } $OutDir = Join-Path $BuildDir $PayloadKey New-Item -ItemType Directory -Force -Path $OutDir | Out-Null # --- 1. Stage payload tree ------------------------------------------------- $Stage = Join-Path $OutDir "stage" if (Test-Path $Stage) { Remove-Item -Recurse -Force $Stage } New-Item -ItemType Directory -Force -Path (Join-Path $Stage "user\$PayloadCategory") | Out-Null Copy-Item -Recurse $PayloadDir (Join-Path $Stage "user\$PayloadCategory\$PayloadKey") # --- 2. Build zip (portal format: payload-.zip) ----------------------- $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($PayloadKey)).TrimEnd('=').Replace('+','-').Replace('/','_') $ZipName = "payload-$b64.zip" $ZipPath = Join-Path $OutDir $ZipName if (Test-Path $ZipPath) { Remove-Item -Force $ZipPath } Add-Type -AssemblyName System.IO.Compression $zip = New-Object System.IO.Compression.ZipArchive([IO.File]::Open($ZipPath, 'Create'), [IO.Compression.ZipArchiveMode]::Create) try { Get-ChildItem -Recurse -File $Stage | Where-Object { $_.FullName -notmatch '[\\/]__pycache__[\\/]' -and $_.Extension -ne '.pyc' } | ForEach-Object { $rel = $_.FullName.Substring($Stage.Length + 1).Replace('\', '/') $entry = $zip.CreateEntry($rel, [IO.Compression.CompressionLevel]::Optimal) $es = $entry.Open() $bytes = [IO.File]::ReadAllBytes($_.FullName) if ($rel -match '(^|/)(payload\.sh|pagerwebui\.init)$') { $bytes = [byte[]]($bytes | Where-Object { $_ -ne 13 }) } $es.Write($bytes, 0, $bytes.Length) $es.Close() } } finally { $zip.Dispose() } # --- 3. Manifest with generated fields ------------------------------------ $hash = (Get-FileHash -Algorithm SHA256 $ZipPath).Hash.ToLower() $manifest = Get-Content -Raw (Join-Path $PayloadDir "_hak5_manifest.json") | ConvertFrom-Json $manifest.time = [int64]([DateTimeOffset]::UtcNow.ToUnixTimeSeconds()) $manifest.last_hash = $hash $manifest.zip = $ZipName $manifest = $manifest | ConvertTo-Json Set-Content -Path (Join-Path $OutDir "_hak5_manifest.json") -Value $manifest -Encoding ascii Write-Host "Built: $ZipPath" # --- 4. Credentials / transport ------------------------------------------- if ($SshKey) { $sshBase = "$User@$PagerHost" $scp = "scp -i `"$SshKey`"" $ssh = "ssh -i `"$SshKey`"" } elseif (Get-Command sshpass -ErrorAction SilentlyContinue) { if (-not $Password) { $Password = Read-Host -AsSecureString "Pager root password"; $Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) } $sshBase = "$User@$PagerHost" $scp = "sshpass -p `"$Password`" scp" $ssh = "sshpass -p `"$Password`" ssh" } else { Write-Host "`nNo sshpass or -SshKey found. Run these manually (password prompts appear):" Write-Host " scp `"$ZipPath`" $User@${PagerHost}:/tmp/" Write-Host " ssh $User@$PagerHost `"cd /root/payloads && unzip -q -o /tmp/$ZipName && chmod +x user/general/$PayloadKey/payload.sh && rm -f /tmp/$ZipName`"" Write-Host "Then re-run this script with -SshKey, or install sshpass." exit 1 } # --- 5. Upload + extract on device ---------------------------------------- & cmd /c "$scp `"$ZipPath`" `"$(Join-Path $OutDir '_hak5_manifest.json')`" ${sshBase}:/tmp/" | Out-Null if ($LASTEXITCODE -ne 0) { throw "SCP failed" } $remotePayloadDir = "user/$PayloadCategory/$PayloadKey" $legacyPayloadDir = "user/general/$PayloadKey" $remoteCmd = "cd /root/payloads && rm -rf $remotePayloadDir $legacyPayloadDir && unzip -q -o /tmp/$ZipName && cp /tmp/_hak5_manifest.json $remotePayloadDir/_hak5_manifest.json && chmod +x $remotePayloadDir/payload.sh && chmod -R 755 $remotePayloadDir/www && rm -f /tmp/$ZipName /tmp/_hak5_manifest.json && if [ -x /etc/init.d/pagerwebui ] && /etc/init.d/pagerwebui running >/dev/null 2>&1; then /etc/init.d/pagerwebui restart; fi && echo EXTRACT_OK" & cmd /c "$ssh $sshBase `"$remoteCmd`"" if ($LASTEXITCODE -ne 0) { throw "Remote extraction failed" } Write-Host "Installed to /root/payloads/$remotePayloadDir/" # --- 6. Portal refresh (best-effort) -------------------------------------- if (-not $NoPortalRefresh) { if (-not $Password) { Write-Host "Skipping portal refresh (no password supplied). Run the payload from the on-device menu to verify." } else { # Base64 the remote command so cmd/ssh quoting cannot mangle the JSON body. $loginCmd = "curl -s -X POST http://127.0.0.1:1471/api/login -d '{""username"":""root"",""password"":""$Password""}'" $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($loginCmd)) $tokenJson = & cmd /c "$ssh $sshBase `"echo $b64 | base64 -d | sh`"" $tokenJson = [string]$tokenJson -replace '\x1b\[[0-9;?]*[A-Za-z]', '' $token = ($tokenJson | ConvertFrom-Json).token if ($token) { $refreshCmd = "curl -fsS -X POST http://127.0.0.1:1471/api/payloads/portal/refresh -H 'Authorization: Bearer $token'" $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($refreshCmd)) & cmd /c "$ssh $sshBase `"echo $b64 | base64 -d | sh`"" | Out-Null if ($LASTEXITCODE -eq 0) { Write-Host "Portal refreshed. The payload should appear in the on-device Payloads menu / Virtual Pager portal." } else { Write-Warning "Portal refresh failed; the local payload installation is still complete." } } else { Write-Host "Login to portal refresh failed; the payload is installed as a directory - run it from the menu." } } } Write-Host "Deploy complete. Run payload.sh from the Pager menu, then browse http://${PagerHost}:8080/"