Cómo detectar intentos fallidos de inicio de sesión en Windows (evento 4625) y exportarlos a CSV

En este tutorial aprenderás a detectar accesos fallidos a un equipo con Windows Server (o cualquier Windows que registre eventos de seguridad), específicamente los intentos de inicio de sesión fallidos registrados como evento 4625 en el Visor de eventos.

También aprenderás a exportarlos fácilmente a un archivo CSV o visualizarlos directamente en pantalla, usando un script PowerShell reutilizable que puedes ejecutar con parámetros personalizados.

¿Por qué es importante monitorear el evento 4625?

Cada vez que un usuario (o atacante) intenta ingresar con un usuario o contraseña incorrecta, Windows genera un evento con ID 4625 en el registro de seguridad. Analizar estos eventos permite:

  • Detectar intentos de fuerza bruta por RDP o SMB.
  • Identificar usuarios mal configurados.
  • Localizar dispositivos que están enviando credenciales incorrectas.
  • Prevenir bloqueos masivos de cuentas.

Requisitos

  • PowerShell (presente por defecto en Windows Server y Windows 10+).
  • Permisos de administrador local.
  • El registro de auditoría de seguridad debe estar activado (por defecto lo está en servidores).

Script PowerShell para detectar eventos 4625

Copia el siguiente código en un archivo llamado, por ejemplo: ver_errores_logon.ps1

param(
[int]$Horas = 24,
[switch]$Dias,
[switch]$Exportar
)

# Calcular tiempo de inicio
if ($Dias) {
$fechaInicio = (Get-Date).AddDays(-$Horas)
} else {
$fechaInicio = (Get-Date).AddHours(-$Horas)
}

Write-Host "Buscando eventos 4625 desde $fechaInicio ..." -ForegroundColor Cyan

# Obtener eventos
$resultado = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4625; StartTime=$fechaInicio} | ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Fecha = $_.TimeCreated
Usuario = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "TargetUserName"} | Select-Object -ExpandProperty '#text'
Dominio = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "TargetDomainName"} | Select-Object -ExpandProperty '#text'
IP_Origen = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "IpAddress"} | Select-Object -ExpandProperty '#text'
Host_Origen = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "WorkstationName"} | Select-Object -ExpandProperty '#text'
Motivo = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "FailureReason"} | Select-Object -ExpandProperty '#text'
}
}

# Mostrar o exportar según parámetro
if ($Exportar) {
$ruta = "$env:USERPROFILE\Escritorio\logins_fallidos.csv"
$resultado | Export-Csv -Path $ruta -NoTypeInformation -Encoding UTF8
Write-Host "Exportado a: $ruta" -ForegroundColor Green
} else {
$resultado | Format-Table -AutoSize
}

¿Cómo usar el script?

  1. Copia el archivo .ps1 a tu servidor o equipo.
  2. Abre PowerShell como administrador.
  3. Ejecuta el script con los parámetros deseados:

Ejemplos:

  • Mostrar en pantalla los últimos 24 horas (por defecto):
.\ver_errores_logon.ps1
  • Ver los últimos 2 días:
.\ver_errores_logon.ps1 -Horas 2 -Dias
  • Exportar a CSV los últimos 48 horas:
.\ver_errores_logon.ps1 -Horas 48 -Exportar
  • Exportar a CSV los últimos 3 días:
.\ver_errores_logon.ps1 -Horas 3 -Dias -Exportar

¿Qué información contiene el resultado?

El reporte incluye:

  • Fecha y hora del intento.
  • Usuario y dominio fallido.
  • IP de origen.
  • Host o estación desde donde se hizo el intento.
  • Motivo del fallo (usuario inexistente, clave incorrecta, etc.)