#Requires -Version 5.1 <# .SYNOPSIS REVERSION COMPLETA en un solo comando: deshace todo lo que hizo el ejercicio. .DESCRIPTION Es el espejo de Invoke-EjercicioCompleto.ps1. Encadena, en el orden correcto: 1. Vector 2: restaurar los ficheros renombrados (sha256 verificado), el fondo, la nota y la marca del registro -> Invoke-Revert.ps1 2. Vector 1: retirar los canarios plantados en el perfil del usuario -> Remove-Canaries.ps1 EL ORDEN IMPORTA: primero se restaura lo que el impacto toco, y despues se retiran los senuelos. Al reves se correria el riesgo de retirar un senuelo que el manifiesto de reversion todavia necesita para verificar. DOS DIFERENCIAS DELIBERADAS CON EL DE EJECUCION: 1. Aqui SIEMPRE se ejecuta de verdad. No hay modo simulacion que valga: una reversion a medias es peor que no revertir, porque deja al equipo con parte de los cambios y sin saber cuales. Es la misma decision que ya toma el stager (Vector2Revert y Retire llevan ForceExecute = $true). 2. Repetir la reversion NO es un error. Si ya no hay nada que deshacer, los pasos lo dicen y el comando termina con exito (exit 0). Lo que no puede pasar es que una segunda pasada rompa algo, ni que devuelva un fallo que haga pensar que el equipo quedo sucio. IDEMPOTENCIA: Invoke-Revert.ps1 trabaja contra el manifiesto de la ejecucion (C:\RT\manifests\manifest--.jsonl). Si no queda manifiesto, no hay nada que restaurar y lo informa. Remove-Canaries.ps1 hace lo mismo con el suyo. Los dos son seguros de repetir. .PARAMETER ScopePath Alcance principal. Se acepta por compatibilidad con el stager: cada fase usa su propio alcance (scope.json para el Vector 2 y scope.vector1.json para el Vector 1) y solo se recurre a este si alguno no esta en el stage. .PARAMETER Execute Se acepta por compatibilidad de interfaz con el resto del arnes. En este script no cambia nada: la reversion siempre se ejecuta. .EXAMPLE .\Invoke-RevertirTodo.ps1 -ScopePath ..\scope.json .EXAMPLE # Desde el stager, que es como lo usa la GPO: .\rt-bootstrap.ps1 -BaseUrl 'https://c2/rt' -Revertir #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$ScopePath, [switch]$Execute ) $ErrorActionPreference = 'Stop' # OJO: este script vive en \harness\bin, asi que el stage esta DOS niveles mas # arriba. El mismo detalle que en Invoke-EjercicioCompleto.ps1: con un solo Split-Path # apuntaria a \harness, no encontraria los alcances del payload y acabaria # usando el que le pasen, que puede ser el equivocado. Medido alli. $hbin = $PSScriptRoot $stage = Split-Path -Parent (Split-Path -Parent $hbin) $scopeV1 = Join-Path $stage 'scope.vector1.json' $scopeV2 = Join-Path $stage 'scope.json' if (-not (Test-Path -LiteralPath $scopeV1)) { $scopeV1 = $ScopePath } if (-not (Test-Path -LiteralPath $scopeV2)) { $scopeV2 = $ScopePath } Write-Host '' Write-Host '=== REVERSION COMPLETA: deshacer el ejercicio ===' -ForegroundColor Cyan Write-Host "Alcance Vector 1: $scopeV1" Write-Host "Alcance Vector 2: $scopeV2" Write-Host 'Modo : EJECUCION REAL (la reversion no se simula)' Write-Host '' $pasos = @( @{ Nombre = 'Vector 2: restaurar ficheros, fondo, nota y registro'; Script = 'Invoke-Revert.ps1'; Alcance = $scopeV2; Args = @() } @{ Nombre = 'Vector 1: retirar los senuelos del perfil'; Script = 'Remove-Canaries.ps1'; Alcance = $scopeV1; Args = @() } ) $fallos = 0 foreach ($p in $pasos) { $scriptPath = Join-Path $hbin ([string]$p.Script) if (-not (Test-Path -LiteralPath $scriptPath)) { Write-Host (" FALTA {0} en {1}" -f $p.Script, $hbin) -ForegroundColor Red $fallos++ continue } Write-Host ("--- {0} ({1})" -f $p.Nombre, $p.Script) -ForegroundColor Cyan # -Execute siempre: no hay reversion simulada. $childArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $scriptPath, '-ScopePath', $p.Alcance, '-Execute') + @($p.Args) & powershell.exe @childArgs if ($LASTEXITCODE -ne 0) { $fallos++ Write-Host (" FALLO en '{0}' (exit {1})" -f $p.Nombre, $LASTEXITCODE) -ForegroundColor Red } Write-Host '' } Write-Host '===========================================================' -ForegroundColor Cyan if ($fallos -eq 0) { Write-Host 'Reversion completa terminada sin errores.' -ForegroundColor Green Write-Host 'El equipo queda en el estado previo al ejercicio.' -ForegroundColor Green exit 0 } else { Write-Host ("Reversion terminada con {0} paso(s) fallido(s). Revisa los registros." -f $fallos) -ForegroundColor Red exit 1 }