#Requires -Version 5.1 <# .SYNOPSIS Devuelve el activo de laboratorio a su estado previo al ejercicio, para repetir la prueba. .DESCRIPTION Borra todo lo que el ejercicio deja en la maquina: directorios de trabajo, stager descargado, canarios plantados y claves de registro. Con tres salvaguardas que hemos aprendido a golpes en este laboratorio: 1. Si hay una ejecucion SIN REVERTIR, se DETIENE y no borra nada. Los manifiestos son lo unico que sabe devolver los ficheros a su nombre: borrarlos dejaria los ficheros atrapados con la extension simulada y sin vuelta atras. 2. Si el fondo de pantalla de ataque sigue puesto, lo RESTAURA antes de borrar la copia del fondo original. Si no, el activo se queda con el fondo del ataque y sin forma de recuperar el suyo. 3. NUNCA toca el Escritorio, Documentos, el perfil del usuario ni el ZIP de evidencia. Solo rutas del arnes y la carpeta de canarios, que la crea el propio ejercicio. Sin -Execute solo muestra lo que borraria (simulacion), igual que el resto del arnes. .PARAMETER Execute Segunda llave, como en todo el arnes. Sin ella no se borra nada. .PARAMETER BorrarEvidencia Borra tambien los ZIP de evidencia del perfil. Por defecto NO se borran: son el material del informe. .EXAMPLE # Ver que se borraria, sin tocar nada .\Reset-LabTarget.ps1 .EXAMPLE # Borrar de verdad .\Reset-LabTarget.ps1 -Execute #> [CmdletBinding()] param( [switch]$Execute, [switch]$BorrarEvidencia, [string[]]$WorkRoots = @('C:\ProgramData\RT', 'C:\ProgramData\RT26', 'C:\RT'), [string]$StagerPath = 'C:\ProgramData\rt-bootstrap.ps1', [string]$PlantedPath ) $ErrorActionPreference = 'Stop' function Rt-Titulo([string]$t) { Write-Host ''; Write-Host "== $t ==" -ForegroundColor Cyan } function Rt-Ok([string]$t) { Write-Host " [OK] $t" -ForegroundColor Green } function Rt-Aviso([string]$t) { Write-Host " [AVISO] $t" -ForegroundColor Yellow } function Rt-Mal([string]$t) { Write-Host " [FALLO] $t" -ForegroundColor Red } # Rutas que el ejercicio crea. Ni una mas. $raices = $WorkRoots $stager = $StagerPath $canarios = if ($PlantedPath) { $PlantedPath } else { Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'RT-Canarias' } $claves = @('HKCU:\Software\RT26', 'HKCU:\FuisteAtacadoPorUnRansomware') Write-Host '' Write-Host '=== RESET DEL ACTIVO DE LABORATORIO ===' -ForegroundColor White Write-Host "Maquina: $env:COMPUTERNAME" Write-Host "Modo : $(if ($Execute) { 'BORRADO REAL' } else { 'SIMULACION (falta -Execute)' })" # -------------------------------------------------------------------------- # Salvaguarda 3: que ninguna ruta prohibida entre en la lista # -------------------------------------------------------------------------- function Test-RutaBorrable([string]$Ruta) { $prohibidas = @( [System.IO.Path]::GetPathRoot($Ruta), $env:USERPROFILE, $env:LOCALAPPDATA, $env:APPDATA, [Environment]::GetFolderPath('Desktop'), [Environment]::GetFolderPath('MyDocuments'), 'C:\Windows', 'C:\Program Files', 'C:\Program Files (x86)', 'C:\ProgramData', 'C:\Users' ) $full = [System.IO.Path]::GetFullPath($Ruta).TrimEnd('\') foreach ($p in $prohibidas) { if ([string]::IsNullOrWhiteSpace($p)) { continue } if ($full -eq ([System.IO.Path]::GetFullPath($p).TrimEnd('\'))) { return $false } } return $true } $aBorrar = @() foreach ($r in ($raices + $stager + $canarios)) { if (-not (Test-RutaBorrable -Ruta $r)) { Rt-Mal "RUTA PROHIBIDA, se omite: $r"; continue } if (Test-Path -LiteralPath $r) { $aBorrar += $r } } # -------------------------------------------------------------------------- # Salvaguarda 1: ejecucion sin revertir # -------------------------------------------------------------------------- Rt-Titulo '1. Comprobando si hay ejecuciones sin revertir' $marcadores = @() foreach ($r in $raices) { if (-not (Test-Path -LiteralPath $r)) { continue } $marcadores += @(Get-ChildItem -LiteralPath $r -Recurse -File -Filter 'active-*.json' -ErrorAction SilentlyContinue) } # Una marca NO prueba que haya trabajo pendiente: apunta a un manifiesto, y si ese # manifiesto ya no existe (porque la reversion lo archivo), la marca quedo huerfana. # Eso pasa cuando la reversion se hizo con las rutas ya cambiadas de sitio. Mirar el # hecho y no la marca es lo que evita falsos positivos... y falsos negativos. $vivos = @() $huerfanos = @() foreach ($m in $marcadores) { $referencias = @() try { $j = Get-Content -LiteralPath $m.FullName -Raw | ConvertFrom-Json $referencias = @($j.PSObject.Properties | Where-Object { $_.Value -is [string] -and $_.Value -like '*.jsonl' } | ForEach-Object { [string]$_.Value }) } catch { } $vive = $true if ($referencias.Count -gt 0) { $vive = (@($referencias | Where-Object { Test-Path -LiteralPath $_ }).Count -gt 0) } if ($vive) { $vivos += $m } else { $huerfanos += $m } } if ($huerfanos.Count -gt 0) { Rt-Aviso "$($huerfanos.Count) marca(s) huerfana(s): apuntan a un manifiesto que ya no existe (su reversion se hizo bien)" $huerfanos | ForEach-Object { Write-Host " $($_.FullName)" -ForegroundColor DarkGray } if ($Execute) { foreach ($h in $huerfanos) { Remove-Item -LiteralPath $h.FullName -Force -ErrorAction SilentlyContinue } Rt-Ok 'marcas huerfanas eliminadas' } else { Rt-Aviso 'se eliminarian al ejecutar con -Execute' } } if ($vivos.Count -gt 0) { Rt-Mal "Hay $($vivos.Count) ejecucion(es) SIN REVERTIR (su manifiesto sigue vivo):" $vivos | ForEach-Object { Write-Host " $($_.FullName)" } Rt-Mal 'NO se borra nada. Revierte primero con la accion Vector2Revert y vuelve a ejecutar esto.' Write-Host '' Write-Host ' powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$env:ProgramData\rt-bootstrap.ps1" \`' -ForegroundColor Gray Write-Host ' -BaseUrl /rt -Action Vector2Revert -Execute' -ForegroundColor Gray Write-Host '' exit 1 } Rt-Ok 'No hay ninguna ejecucion pendiente' # -------------------------------------------------------------------------- # Salvaguarda 2: el fondo de ataque sigue puesto # -------------------------------------------------------------------------- Rt-Titulo '2. Comprobando el fondo de pantalla' $wp = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' -Name WallPaper -ErrorAction SilentlyContinue).WallPaper $esAtaque = $false foreach ($r in $raices) { if ($wp -and $wp.StartsWith($r, [StringComparison]::OrdinalIgnoreCase)) { $esAtaque = $true } } if ($esAtaque) { Rt-Aviso "el fondo apunta al payload del ataque: $wp" $restore = Get-ChildItem -Path $raices -Recurse -File -Filter 'wallpaper_restore.jpg' -ErrorAction SilentlyContinue | Select-Object -First 1 if ($restore) { if ($Execute) { # Se reutiliza la funcion del propio arnes (ya probada) si todavia esta en disco: # en este punto el stage sigue existiendo, porque se borra despues. $common = Get-ChildItem -Path $raices -Recurse -File -Filter 'Common.ps1' -ErrorAction SilentlyContinue | Where-Object { $_.FullName -like '*\lib\Common.ps1' } | Select-Object -First 1 $hecho = $false if ($common) { try { . $common.FullName if (Get-Command Set-RTWallpaper -ErrorAction SilentlyContinue) { [void](Set-RTWallpaper -Path $restore.FullName) $hecho = $true } } catch { } } if ($hecho) { Rt-Ok "fondo restaurado desde: $($restore.FullName)" } else { Rt-Aviso 'NO se pudo restaurar el fondo automaticamente.' Rt-Aviso 'Ponlo a mano (Configuracion > Personalizacion) con este fichero:' Write-Host " $($restore.FullName)" -ForegroundColor Yellow } } else { Rt-Aviso "se restauraria desde: $($restore.FullName)" } } else { Rt-Mal 'no encuentro la copia del fondo original: el activo se quedara con el fondo del ataque' } } else { Rt-Ok 'el fondo de pantalla no apunta al payload (nada que restaurar)' } # -------------------------------------------------------------------------- # Inventario # -------------------------------------------------------------------------- Rt-Titulo '3. Que se va a borrar' $totalBytes = 0 $totalFich = 0 foreach ($r in $aBorrar) { if (Test-Path -LiteralPath $r -PathType Leaf) { $it = Get-Item -LiteralPath $r $totalBytes += $it.Length; $totalFich++ Write-Host (" {0,-42} {1,8:N0} B (fichero)" -f $r, $it.Length) } else { $its = @(Get-ChildItem -LiteralPath $r -Recurse -File -ErrorAction SilentlyContinue) $b = ($its | Measure-Object -Property Length -Sum).Sum if (-not $b) { $b = 0 } $totalBytes += $b; $totalFich += $its.Count Write-Host (" {0,-42} {1,7} ficheros {2,10:N0} B" -f $r, $its.Count, $b) } } foreach ($k in $claves) { if (Test-Path -LiteralPath $k) { Write-Host (" {0,-42} (clave de registro)" -f $k) } } Write-Host '' Write-Host (" TOTAL: {0} ficheros, {1:N2} MB" -f $totalFich, ($totalBytes / 1MB)) -ForegroundColor White $evidencia = @(Get-ChildItem -LiteralPath $env:USERPROFILE -File -Filter 'evidencia-RT*.zip' -ErrorAction SilentlyContinue) if ($evidencia.Count -gt 0) { Rt-Titulo '4. Evidencia' foreach ($e in $evidencia) { if ($BorrarEvidencia) { Write-Host " se BORRARA: $($e.Name)" -ForegroundColor Yellow } else { Write-Host " se CONSERVA: $($e.Name) ($([int]($e.Length/1KB)) KB) <- material del informe" -ForegroundColor Green } } } # -------------------------------------------------------------------------- # Borrado # -------------------------------------------------------------------------- if (-not $Execute) { Rt-Titulo 'Simulacion' Write-Host ' No se ha borrado nada. Anade -Execute para hacerlo.' -ForegroundColor Yellow Write-Host '' exit 0 } Rt-Titulo '5. Borrando' $errores = 0 foreach ($r in $aBorrar) { try { Remove-Item -LiteralPath $r -Recurse -Force -ErrorAction Stop Rt-Ok "borrado: $r" } catch { $errores++ Rt-Mal "no se pudo borrar '$r': $($_.Exception.Message)" } } foreach ($k in $claves) { if (Test-Path -LiteralPath $k) { try { Remove-Item -LiteralPath $k -Recurse -Force -ErrorAction Stop; Rt-Ok "clave eliminada: $k" } catch { $errores++; Rt-Mal "no se pudo eliminar '$k': $($_.Exception.Message)" } } } if ($BorrarEvidencia) { foreach ($e in $evidencia) { try { Remove-Item -LiteralPath $e.FullName -Force -ErrorAction Stop; Rt-Ok "evidencia borrada: $($e.Name)" } catch { $errores++; Rt-Mal "no se pudo borrar '$($e.Name)'" } } } Rt-Titulo 'Comprobacion final' foreach ($r in ($raices + $stager + $canarios)) { if (Test-Path -LiteralPath $r) { Rt-Aviso "sigue existiendo: $r" } else { Rt-Ok "limpio: $r" } } foreach ($k in $claves) { if (Test-Path -LiteralPath $k) { Rt-Aviso "sigue existiendo: $k" } else { Rt-Ok "limpio: $k" } } Write-Host '' if ($errores -eq 0) { Write-Host 'Activo listo para repetir la prueba desde cero.' -ForegroundColor Green } else { Write-Host "Terminado con $errores error(es): revisa los avisos de arriba." -ForegroundColor Yellow } Write-Host ''