Endurecimiento de la seguridad de PowerShell — registro, AMSI, modo de lenguaje y JEA
· Actualizado el: · Go Komura · PowerShell, Seguridad, Windows, Registro, Auditoría, Sistemas de información, Gestión de permisos, Mejora operativa
Tras una serie de informes de incidentes en los que «PowerShell fue utilizado con fines maliciosos durante una intrusión», algunas organizaciones intentan prohibir por completo el uso de PowerShell en la empresa. Sin embargo, esto no compensa, ni en términos de eficacia real ni en términos de impacto operativo. PowerShell es la propia base de administración de Windows, y detenerlo detiene la automatización operativa. Por su parte, un atacante puede lograr lo mismo por otros medios.
El enfoque realista no es la prohibición, sino la visibilidad y la restricción. Afortunadamente, desde PowerShell 5.0 existen funciones pensadas para el lado defensivo: el registro de bloques de script, que deja constancia incluso del código ofuscado ya expandido; AMSI (Antimalware Scan Interface, el mecanismo de Windows que hace que el contenido de un script sea inspeccionado por el producto antimalware antes de ejecutarse); el modo de lenguaje, que restringe la propia sintaxis que se puede ejecutar; y JEA, que permite «delegar solo las operaciones necesarias». Combinándolos es posible mantener un estado auditable sin detener la operación diaria.
En este artículo se recogen, en orden de mayor a menor impacto, los elementos que conviene configurar para seguir usando PowerShell con seguridad en un entorno Windows corporativo. La directiva de ejecución y la firma de scripts se tratan en «Directiva de ejecución y firma de scripts en PowerShell», así que este artículo va más allá de ese tema.
Entorno de destino y requisitos previos
| Elemento | Contenido |
|---|---|
| SO de destino | Windows 10/11, Windows Server. AMSI presupone Windows 10 o posterior1 |
| Versión de destino | Este artículo cubre tanto Windows PowerShell 5.1 como PowerShell 7. Las claves de directiva y el destino de los registros son distintos entre 5.1 y 7, así que en equipos con ambos instalados configure ambos (capítulos 3 y 4) |
| Permisos necesarios | La configuración de registro de los capítulos 3 a 4, la deshabilitación de PowerShell 2.0 y el registro de un punto de conexión JEA del capítulo 7 requieren, todos ellos, permisos de administrador. En un entorno de producción, en lugar de configurarlo equipo a equipo, se distribuye mediante directiva de grupo |
| Local/remoto | Los capítulos 3 a 6 se completan por completo dentro del equipo o servidor de destino. Solo JEA, en el capítulo 7, presupone que PowerShell Remoting (WinRM) esté habilitado (véase el inicio del capítulo 7) |
| Fuera de alcance | Los detalles de la directiva de ejecución y la firma se tratan en otro artículo (Directiva de ejecución y firma de scripts en PowerShell). Aquí solo se ordena su posicionamiento (capítulo 5) |
1. La conclusión primero
- La prioridad máxima es habilitar el registro de bloques de script. El código ejecutado queda registrado en el registro de eventos, y el código ofuscado también queda ya expandido (ID de evento 4104).2
- Habilite también la transcripción. Puede centralizar el registro de la sesión, incluidas la entrada y la salida, en un recurso compartido de solo escritura.2
- Al habilitar el registro, revise también el tamaño del registro. Con el valor predeterminado se sobrescribe en poco tiempo.2
- Con AMSI, el script se entrega al producto antimalware justo antes de ejecutarse. Está disponible desde PowerShell 5.0, en Windows 10 o posterior.1
- Deshabilite el antiguo motor de PowerShell 2.0. Si sigue presente, queda margen para cambiar a ese motor antiguo, donde ni el registro ni AMSI surten efecto.3
- La directiva de ejecución no es un límite de seguridad. Lo indica explícitamente la documentación oficial. No la convierta en el eje de su defensa.4
- Use el modo de lenguaje como resultado de WDAC/AppLocker. La configuración manual se puede eludir y no funciona como función de seguridad.5
- Para delegar permisos, use JEA. Permite autorizar «solo este comando, con este parámetro» y operar sin repartir permisos de administrador.6
2. Qué se protege — primero la visibilidad, después la restricción
Las cuatro medidas tratadas en este artículo cubren cada una una capa distinta. Primero, el panorama general.
flowchart TB
accTitle: Capas de defensa del endurecimiento de PowerShell
accDescr: Muestra cómo el registro, la delegación, la restricción y la inspección forman capas distintas alrededor de la operación en PowerShell.
U["Administrador, soporte técnico, script de automatización (y también el atacante que ha entrado)"]
DEL["[Delegación] JEA (capítulo 7) — sin repartir permisos de administrador, solo se entregan las operaciones permitidas"]
LIM["[Restricción] Modo de lenguaje + WDAC/AppLocker (capítulo 6) — acota el código y la sintaxis que se pueden usar"]
SCAN["[Inspección] AMSI (capítulo 4) — entrega el contenido al producto antimalware justo antes de ejecutarse; la deshabilitación de PowerShell 2.0 cierra la vía de elusión"]
RUN["Operación en PowerShell"]
REC["[Registro] Registro de bloques de script 4104 y transcripción (capítulo 3) — queda constancia ya sin ofuscar"]
U --> DEL
DEL --> LIM
LIM --> SCAN
SCAN --> RUN
RUN --> REC
El registro crea un estado en el que se puede reconstruir lo ocurrido después de los hechos; la delegación reduce el propio alcance de los permisos; la inspección y la restricción detienen las cosas antes de la ejecución. Las cuatro medidas no son sustitutas entre sí: cada una cubre una capa distinta. Dicho esto, la introducción sí tiene un orden de prioridad.
| Etapa | Qué hacer | Efecto |
|---|---|---|
| 1. Visibilidad | Registro de bloques de script, transcripción | Se sabe qué ha ocurrido. Permite la investigación posterior |
| 2. Neutralización básica | Deshabilitar PowerShell 2.0, mantener la versión actualizada, confirmar AMSI | Cierra las vías que eluden la inspección |
| 3. Limitación de permisos | Delegación con JEA, reducción de permisos de administrador | Limita el alcance del daño |
| 4. Restricción de ejecución | WDAC/AppLocker + modo de lenguaje restringido | Detiene la propia ejecución de código no aprobado |
En muchos entornos, con hacer solo 1 y 3 la situación mejora de forma considerable. El punto 4 tiene un coste de introducción alto y es un ámbito que conviene avanzar verificando el impacto operativo.
3. Habilitar el registro — 4104 es lo más importante
El registro de PowerShell tiene tres niveles.2
| Tipo | Contenido registrado | ID de evento |
|---|---|---|
| Registro de módulos | Detalle de la ejecución del pipeline del módulo indicado | 4103 |
| Registro de bloques de script | Texto del código ejecutado (ya sin ofuscar) | 4104 |
| Registro de destino | En 5.1 es Microsoft-Windows-PowerShell/Operational; en 7 es PowerShellCore/Operational |
— |
| Transcripción | Registra la entrada y salida de la sesión en un archivo de texto | — (salida a archivo) |
Todo esto se puede configurar en directiva de grupo, en «Configuración del equipo > Plantillas administrativas > Componentes de Windows > Windows PowerShell». También se puede configurar directamente en el registro.2
# Habilitar el registro de bloques de script (requiere permisos de administrador; normalmente se distribuye por GPO)
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
# -Type es un parámetro dinámico que añade el proveedor del registro. Permite indicar el tipo de valor (DWord)
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord
# PowerShell 7 (pwsh) usa una clave de directiva distinta. Configure ambas
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord
# Habilitar la transcripción y centralizarla en un recurso compartido de solo escritura
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting' -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory' -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader' -Value 1 -Type DWord
# La transcripción también tiene una clave distinta en el lado de PowerShell 7. Escriba los mismos valores
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting' -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory' -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader' -Value 1 -Type DWord
PowerShell 7 consulta la directiva bajo PowerShellCore, no bajo Windows\PowerShell. Si se conforma con configurar solo el lado de 5.1, el registro de los scripts ejecutados con pwsh queda por completo ausente. Tanto para el registro de bloques de script como para la transcripción, escriba el mismo valor en ambas claves (si se distribuye por GPO, configúrelo también en cada plantilla correspondiente).
El valor del registro de bloques de script está en su resistencia a la ofuscación. Aunque el comando esté codificado en Base64 o el código se haya ensamblado mediante concatenación de cadenas, se registra el contenido ya expandido en el momento de la ejecución.2 Que en una investigación de un incidente se pueda reconstruir «qué se ejecutó» depende de tener o no habilitada esta configuración.
Para verificar el registro se usa Get-WinEvent (véase «Consultar el registro de eventos de forma práctica con Get-WinEvent»).
# Comprobar el registro de bloques de script del último día.
# Windows PowerShell (5.1) y PowerShell 7 escriben en registros distintos,
# así que hay que apuntar a ambos
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational'
ID = 4104
StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
Select-Object TimeCreated, LogName, @{ n='Script'; e={ $_.Message } } -First 20
Para verificar que se ha habilitado correctamente, siga estos tres pasos.
- Tras aplicar la configuración, abra una nueva sesión de PowerShell. El registro de bloques de script solo se registra a partir de las sesiones iniciadas después de habilitarlo.2 No pruebe en una ventana que ya tenía abierta y concluya que «no funciona».
- Ejecute en la nueva sesión un comando que sirva de marcador y compruebe si vuelve como evento 4104. Si la consulta anterior devuelve un evento que contiene el fragmento de código que acaba de ejecutar, está habilitado. Si no devuelve ninguno, compruebe con
Get-ItemPropertyel valor de la clave de directiva (EnableScriptBlockLoggingdebe ser1) y en cuál de las claves, la de 5.1 o la de 7, lo escribió. - Compruebe que no se equivocó de destino de registro. Si ejecutó con
pwsh, el destino esPowerShellCore/Operational. Fijarse solo enMicrosoft-Windows-PowerShell/Operationaly concluir erróneamente «no se está registrando» es el malentendido más frecuente con esta configuración.
Tras habilitarlo, revise siempre el tamaño del registro. Con el tamaño predeterminado, se completa un ciclo en horas o días y, cuando de verdad lo necesite, ya no queda nada.
Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational' |
Select-Object LogName, MaximumSizeInBytes, RecordCount
wevtutil sl Microsoft-Windows-PowerShell/Operational /ms:1073741824 # Ejemplo: ampliar a 1 GB
wevtutil sl PowerShellCore/Operational /ms:1073741824 # No olvide tampoco el lado de PowerShell 7
# Comprobar que se aplicó. Si MaximumSizeInBytes es 1073741824, se aplicó correctamente
Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational' |
Select-Object LogName, MaximumSizeInBytes
wevtutil sl no muestra nada aunque tenga éxito, así que la única forma de confirmarlo es comparar MaximumSizeInBytes antes y después del cambio. Si no se ejecuta con permisos de administrador, el cambio se rechaza, así que si el valor no cambia, sospeche primero de eso.
Convertir el destino de las transcripciones en un recurso compartido «se puede escribir pero no eliminar»
El punto clave es que el destino de salida de las transcripciones sea un recurso compartido en el que el usuario pueda escribir pero no eliminar. Si se guarda localmente, en un equipo comprometido se puede borrar.
Los permisos se configuran tanto en los permisos de acceso del recurso compartido como en los permisos NTFS (el permiso efectivo será el más restrictivo de ambos). El punto clave es dar al usuario únicamente «crear archivos en la carpeta» y «leer y escribir en el archivo que él mismo creó», y no otorgar eliminar (DE) ni eliminar subcarpetas y archivos (DC).
Antes de nada, dejemos claro qué protege y qué no protege esta configuración. Lo que protege es «no tocar en absoluto el registro de otro usuario» y «no poder eliminar tampoco el propio registro». Lo que no protege es «la sobrescritura del propio registro», algo que no se puede cerrar solo con la ACL (el motivo se explica más adelante). Aun así, el hecho de que, desde una cuenta comprometida, ya no se pueda borrar el rastro de otros cambia mucho la posibilidad de delimitar el alcance del daño. Aunque caiga un equipo, el registro de los demás equipos permanece.
# Se ejecuta en el servidor de centralización de registros (requiere permisos de administrador)
$path = 'D:\PSTranscripts'
$null = New-Item -Path $path -ItemType Directory -Force
# Crear el recurso compartido. El usuario solo llega a Cambiar (escritura); la administración es solo del responsable de los registros
New-SmbShare -Name 'pstranscripts$' -Path $path `
-ChangeAccess 'EXAMPLE\Domain Users' -FullAccess 'EXAMPLE\LogAdmins'
# Cortar la herencia desde la carpeta principal (segundo argumento $false = no heredar las ACE que venían heredadas).
# Si permanece el Control total heredado de CREATOR OWNER,
# se llega al estado "cada uno puede eliminar el archivo que creó", lo que anula el sentido de la centralización.
#
# Sin embargo, lo que quita SetAccessRuleProtection son solo las "ACE heredadas";
# las ACE asignadas directamente a esa carpeta permanecen. El New-Item -Force anterior
# tiene éxito incluso en una carpeta existente, así que si antes se había asignado
# directamente el permiso de cambio a Domain Users o a Everyone, ese permiso
# sobrevive aunque se ejecuten todas las líneas siguientes. El usuario seguiría pudiendo
# sobrescribir o eliminar las transcripciones de otros. Por eso, primero se eliminan
# de golpe todas las asignaciones directas y luego se vuelven a añadir solo las necesarias
$acl = Get-Acl -Path $path
$acl.SetAccessRuleProtection($true, $false)
foreach ($ace in @($acl.Access)) { [void]$acl.RemoveAccessRuleSpecific($ace) }
# Una vez eliminado todo, se añaden en el mismo objeto $acl el lado de administración y SYSTEM,
# y se aplica de una sola vez.
# Si se hace "vaciar" y "volver a añadir" con dos Set-Acl separados, existe un instante en que nadie puede acceder
foreach ($id in @('EXAMPLE\LogAdmins', 'SYSTEM')) {
$acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
$id, 'FullControl', 'ContainerInherit, ObjectInherit', 'None', 'Allow'))
}
Set-Acl -Path $path -AclObject $acl
# Al usuario solo se le permite "crear" en la carpeta. El punto clave es no añadir (OI),
# porque este permiso no se hereda a los archivos que haya dentro
# WD=crear archivo AD=crear carpeta X=recorrer la carpeta RA=leer atributos
# (Si se añade (OI), se acaba concediendo también "escribir datos en un archivo existente")
icacls $path /grant 'EXAMPLE\Domain Users:(CI)(WD,AD,X,RA)'
# Permitir que quien escribe pueda leer y escribir "solo el archivo que él mismo creó".
# CREATOR OWNER se sustituye, en el momento de crear el archivo, por "ese creador",
# así que no surte efecto sobre los archivos de otros. No se otorga DE (eliminar)
# RD,WD,AD = lectura, escritura y adición de datos RA,WA = lectura y escritura de atributos
# REA,WEA = lectura y escritura de atributos extendidos RC = lectura de permisos
# S = sincronización. Está incluido en GENERIC_READ/GENERIC_WRITE, así que no se puede quitar
# Da la tentación de limitarlo solo a AD (adición), pero con eso no se puede escribir ni una línea de la transcripción (se explica más adelante)
icacls $path /grant 'CREATOR OWNER:(OI)(IO)(RD,WD,AD,REA,WEA,RA,WA,RC,S)'
# Este es el punto clave. Con la línea anterior no basta.
# El usuario que crea un archivo se convierte en el "propietario" de ese archivo.
# Windows concede implícitamente al propietario READ_CONTROL y WRITE_DAC,
# así que, aunque no se hayan otorgado ni WD ni DE, el propietario puede
# reescribir su propia DACL y volver a asignarse a sí mismo permisos de sobrescritura y eliminación.
# Es decir, una cuenta comprometida puede borrar su propio rastro. Cuando existe
# la ACE de OWNER RIGHTS, el sistema ignora los permisos implícitos
# READ_CONTROL / WRITE_DAC otorgados al propietario
icacls $path /grant 'OWNER RIGHTS:(OI)(IO)(RA,REA)'
icacls $path # Comprobar el resultado de la configuración
Al reutilizar una carpeta ya existente, revise sin falta las ACE asignadas directamente. Lo que quita SetAccessRuleProtection($true, $false) son solo las ACE heredadas; las ACE asignadas directamente a esa carpeta permanecen tal cual.7 New-Item -Force tiene éxito incluso en una carpeta existente, así que si se reutiliza una carpeta con un historial de, por ejemplo, «se había otorgado temporalmente el permiso de cambio a Domain Users», aunque después se restrinja CREATOR OWNER, esa asignación directa por sí sola sobrevive, y el usuario puede sobrescribir o eliminar las transcripciones de otros. El código anterior elimina primero todo y luego lo vuelve a añadir precisamente por esto. Si crea una carpeta nueva no hace falta, pero no está de más incluirlo de todos modos. Después de aplicarlo, revise visualmente la salida de icacls $path y compruebe que no aparece ningún sujeto no deseado.
Aquí, la forma en que se añade (OI) determina el éxito o el fracaso de la medida. Si al permiso del usuario se le añade (OI) y se otorga WD, esa ACE se hereda a todos los archivos por debajo, y basta con conocer el nombre para poder sobrescribir o truncar las transcripciones de otros.7 Con eso no se cumple el objetivo de «que el registro sobreviva incluso después de que el equipo o la cuenta hayan sido comprometidos». Como se ha mostrado, hay que separar el permiso de creación en la carpeta (sin heredarlo) de la lectura y escritura, a través de CREATOR OWNER, solo del archivo que uno mismo creó.
Y no elimine la línea de OWNER RIGHTS. El usuario que crea un archivo se convierte en el propietario de ese archivo. Windows otorga implícitamente al propietario READ_CONTROL y WRITE_DAC,8 así que, aunque en la ACE no se haya otorgado DE, el propietario puede reescribir su propia DACL y volver a asignarse a sí mismo el permiso de eliminación. Si se queda en el estado de «cada uno puede eliminar su propio registro», no tiene sentido haber restringido CREATOR OWNER. Al colocar la ACE de OWNER RIGHTS (S-1-3-4), el sistema ignora los permisos implícitos READ_CONTROL/WRITE_DAC otorgados al propietario, y solo entonces se cumple de verdad el «se puede escribir pero no eliminar».8
Por qué no limitarse solo a AD (adición)
Da la tentación de pensar que, «como no se quiere que se sobrescriba, basta con que CREATOR OWNER tenga solo AD». Si se hace así, no queda registrada ni una línea de la transcripción.
En Windows, «solo se puede añadir» se cumple únicamente cuando quien escribe abre el archivo especificando FILE_APPEND_DATA en solitario. La referencia de Microsoft define FILE_APPEND_DATA como «(en archivos locales, si se especifica este indicador sin FILE_WRITE_DATA, las operaciones de escritura no sobrescriben los datos existentes)».9 Es decir, la escritura exclusiva de adición es una propiedad de cómo se abre el archivo, no algo que se logre simplemente colocando AD en la ACE.
Y la vía de escritura de Start-Transcript no abre en modo exclusivo de adición. La implementación de PowerShell abre primero con FileMode.OpenOrCreate + FileAccess.ReadWrite y, solo si eso falla, vuelve a abrir con FileMode.Append + FileAccess.Write.10 En el lado de .NET, FileAccess.Read/Write se traducen respectivamente a GENERIC_READ/GENERIC_WRITE, y FileMode.Append internamente se sustituye por FileMode.OpenOrCreate y solo se desplaza al final del archivo.11 No existe ninguna vía que solicite FILE_APPEND_DATA en solitario.
FILE_GENERIC_WRITE incluye FILE_WRITE_DATA, FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, READ_CONTROL y SYNCHRONIZE; FILE_GENERIC_READ incluye FILE_READ_DATA, FILE_READ_ATTRIBUTES, FILE_READ_EA, READ_CONTROL y SYNCHRONIZE.9 La comprobación de acceso evalúa todos los derechos solicitados, así que ese CreateFile se rechaza para un usuario que solo tenga AD,RA,REA. Aunque el recurso compartido permita Cambiar, se cae en el lado de NTFS. Con la intención de proteger el registro, se acaba deteniendo el propio registro.
Por eso la ACL de esta sección no se limita a AD en solitario, sino que se ha planteado como «permitir leer y escribir, pero no otorgar DE (eliminar), WDAC (cambiar permisos) ni WO (cambiar propietario)». El propietario puede sobrescribir y truncar su propia transcripción. Asuma que esto no se puede cerrar con la ACL.
Si se quiere cerrar también la sobrescritura, no hay más remedio que sacar el registro a un lugar que quien escribe no pueda tocar. Hay tres opciones.
- Con JEA, use
TranscriptDirectoryen el archivo de configuración de sesión (capítulo 7). Quien escribe esta salida no es el usuario que se conecta, sino Local System, y la documentación de Microsoft exige que «los usuarios estándar no tengan acceso a esta carpeta» y que se limite «a los administradores de seguridad que auditan».12 Como el usuario no necesita permisos en absoluto, el problema de esta sección desaparece - Incorporarlo al reenvío de eventos de Windows o a un SIEM. El registro de bloques de script (4104) aparece en el registro de eventos, así que puede ser objeto de reenvío
- Levantar un proceso de recolección propio que abra en modo exclusivo de adición. Si usted mismo escribe el código para abrir solicitando
FILE_APPEND_DATAen solitario, entonces elADdel lado de la ACE cobra sentido por primera vez
Tenga en cuenta que lo que la ACL protege es, en cualquier caso, frente a «los usuarios que escriben en ese recurso compartido». El administrador del servidor de archivos puede tomar posesión, y si el equipo cliente está completamente controlado, ya se puede manipular antes incluso de escribir en el recurso compartido. Entienda el diseño de permisos de esta sección como algo que garantiza que «los usuarios que comparten el mismo recurso no puedan borrarse entre sí», nada más.
El significado de los símbolos de permiso de icacls (WD, AD, DE, DC, etc.) y de los indicadores de herencia ((OI), (CI), (IO)) está recogido en la referencia de comandos de Windows.7 Además, pruebe siempre esta configuración una vez en una máquina de verificación antes de desplegarla. Como la transcripción va añadiéndose al mismo archivo mientras la sesión sigue en ejecución, si se restringen demasiado los permisos, el propio registro deja de generarse. Lo más seguro es comprobar realmente estos tres puntos: «se puede escribir desde el equipo del usuario», «el usuario no puede leer ni modificar un archivo existente de otro» y «el usuario no puede eliminar su propio archivo».
4. Cerrar las vías de elusión — PowerShell 2.0 y AMSI
Gracias a AMSI (Antimalware Scan Interface), desde PowerShell 5.0, el contenido del script se entrega al producto antimalware justo antes de ejecutarse y se inspecciona ya sin ofuscar.1 Si se usa un producto compatible, incluido Microsoft Defender, funciona sin configuración adicional.
El problema es que el motor antiguo carece de este mecanismo. Si el motor de Windows PowerShell 2.0 sigue habilitado, queda margen para cambiar, mediante powershell.exe -Version 2, a un entorno donde ni el registro ni AMSI surten efecto. Esta función está en desuso y se recomienda deshabilitarla.3
# Comprobar el estado del motor de PowerShell 2.0 y deshabilitarlo (requiere permisos de administrador; puede necesitar reinicio)
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2* |
Select-Object FeatureName, State
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart
Tenga en cuenta que, si tiene instalado PowerShell 7 (pwsh), tanto la clave de configuración como el destino del registro son distintos de los de 5.1 (la directiva está bajo PowerShellCore, y el registro es PowerShellCore/Operational). En un entorno donde se usan ambos, aplique la configuración, el tamaño del registro y las consultas de investigación a los dos. Sobre la convivencia de versiones, consulte «Diferencias entre Windows PowerShell 5.1 y PowerShell 7».
5. No equivocar el papel de la directiva de ejecución
Conviene dejarlo claro de nuevo. La directiva de ejecución no es un límite de seguridad. La documentación oficial señala explícitamente que es una función de seguridad pensada para evitar que un usuario ejecute un script sin querer, no para impedir operaciones malintencionadas.4
Dicho esto, no carece de valor. Trabajar con firmas aporta otro valor distinto: «poder confirmar que el módulo distribuido no ha sido manipulado» (véase «Distribución y actualización interna de módulos de PowerShell»). Lo importante es entender correctamente su papel y usarla en consecuencia; el planteamiento más peligroso es creer que «con configurar AllSigned ya estamos seguros».
6. Modo de lenguaje — combinarlo con el control de aplicaciones
Las sesiones de PowerShell tienen un modo de lenguaje que restringe los elementos del lenguaje disponibles.5
| Modo | Qué se puede usar | Posicionamiento práctico |
|---|---|---|
| FullLanguage | Todos los elementos del lenguaje (predeterminado) | Sesión normal |
| ConstrainedLanguage | Todos los cmdlets funcionan; también se pueden usar bucles, condicionales, expansión de cadenas y acceso a propiedades. Sin embargo, los tipos .NET utilizables se limitan a una lista permitida, y Add-Type solo puede cargar ensamblados firmados |
Modo al que PowerShell cambia automáticamente bajo WDAC/AppLocker. La operación interactiva y las tareas de administración habituales se pueden realizar en gran medida sin cambios |
| RestrictedLanguage | Se pueden ejecutar comandos, pero no se pueden usar bloques de script. Las variables se limitan a $PSCulture, $PSUICulture, $true, $false y $null, y los operadores de comparación se limitan a -eq, -gt y -lt; no se permiten asignaciones, acceso a propiedades ni llamadas a métodos |
Modo usado para cargar manifiestos de módulo (.psd1). No está pensado para uso interactivo por personas |
| NoLanguage | El propio lenguaje de script queda deshabilitado. No se pueden usar ni scripts ni variables; solo se pueden invocar cmdlets y comandos nativos | Predeterminado en la configuración de sesión JEA (RestrictedRemoteServer). Modo para crear una interfaz de «solo ejecutar los comandos determinados» |
Los tres modos restrictivos no son escalones, sino que sirven a propósitos distintos. ConstrainedLanguage es «un entorno en el que la persona escribe y usa scripts, pero se impide solo el uso de tipos peligrosos», mientras que NoLanguage es «no dejar escribir scripts en absoluto», y este último solo tiene sentido en una interfaz acotada como JEA.5
El modo actual se puede comprobar así:
$ExecutionContext.SessionState.LanguageMode
Lo importante es la forma de configurarlo. El modo de lenguaje restringido funciona correctamente cuando PowerShell cambia a él de forma automática al configurar un control de aplicaciones por lista de permitidos con WDAC (Windows Defender Application Control) o AppLocker.5 Configurarlo manualmente mediante variables de entorno u otros medios es fácil de eludir y no funciona como función de seguridad. Entienda que restringir solo el modo de lenguaje sin implantar control de aplicaciones tiene poco efecto en relación con el esfuerzo invertido.
7. JEA — delegar «solo las operaciones necesarias»
Lo que en la práctica determina el alcance del daño es, en muchos casos, la amplitud de los permisos. En un estado donde «se ha dado permisos de administrador al soporte técnico» o «todo el personal de operaciones está en el grupo Domain Admins», la vulneración de un solo equipo se convierte en la vulneración de toda la empresa.
JEA (Just Enough Administration) es un mecanismo que permite delegar solo operaciones concretas sin entregar permisos de administrador.6 Encaja a la perfección con una necesidad como «encargar al soporte técnico solo el reinicio de un servicio de aplicación».
Solo este capítulo tiene un requisito distinto — JEA se apoya en el procesamiento remoto
A diferencia de los capítulos 3 a 6, que se completan con configuración local, JEA está construido sobre el propio mecanismo de PowerShell Remoting (WinRM).13 «Delegar con JEA» significa registrar en el servidor de destino un punto de conexión dedicado (una configuración de sesión) y hacer que se conecten a él. Que su organización pueda reproducir esto o no depende, en primer lugar, de este punto.
Se necesitan estos tres elementos.
| Requisito | Contenido |
|---|---|
| Versión de PowerShell | JEA está disponible desde PowerShell 5.013 |
| Habilitación del procesamiento remoto | Que PowerShell Remoting esté habilitado en el servidor de destino. Desde Windows Server 2012 está habilitado de forma predeterminada; si no lo está, ejecute Enable-PSRemoting en una PowerShell con permisos de administrador13 |
| Ubicación de los archivos | El archivo de funciones de rol (.psrc) debe estar en la carpeta RoleCapabilities de un módulo en el servidor de destino, y la configuración de sesión (.pssc) se registra en el servidor de destino. No se colocan en el equipo del usuario6 |
Es decir, los pasos 1 a 3 siguientes son tareas que se realizan en el servidor al que se delega, con permisos de administrador. En el equipo del usuario solo hace falta poder conectarse a ese servidor. La configuración del propio procesamiento remoto y su configuración segura se recogen en «Introducción a PowerShell Remoting (WinRM)».
Paso 1: definir las operaciones permitidas en el archivo de funciones de rol (.psrc)
El archivo de funciones de rol debe estar en la carpeta RoleCapabilities de un módulo de PowerShell. Con solo crear la carpeta no se reconoce como módulo y no se puede resolver por nombre desde RoleDefinitions, como se explica más abajo. Por eso, primero hay que crear el contenedor: un módulo (una carpeta con manifiesto).6
# Crear el módulo donde se colocará la función de rol (necesita manifiesto)
$moduleRoot = 'C:\Program Files\WindowsPowerShell\Modules\KsJea'
$null = New-Item -Path "$moduleRoot\RoleCapabilities" -ItemType Directory -Force
# La carpeta del módulo necesita al menos un archivo con el mismo nombre que la carpeta
$null = New-Item -Path "$moduleRoot\KsJea.psm1" -ItemType File -Force
New-ModuleManifest -Path "$moduleRoot\KsJea.psd1" -RootModule 'KsJea.psm1'
# Crear la plantilla del archivo de funciones de rol (el nombre del archivo se convierte en el nombre del rol).
# El nombre del rol se resuelve "solo por nombre" entre todos los módulos de PSModulePath,
# así que un nombre genérico como 'HelpDesk' puede chocar con el .psrc de otro módulo.
# Si hay conflicto, no hay garantía de cuál se elige, y se pueden otorgar permisos no deseados.
# Use un nombre único con un prefijo de la organización
New-PSRoleCapabilityFile -Path "$moduleRoot\RoleCapabilities\KsHelpDesk.psrc"
# Comprobar que se ve como módulo
Get-Module -Name KsJea -ListAvailable
# KsHelpDesk.psrc (extracto) — declara "qué" se permite y "hasta dónde"
@{
GUID = '....'
# Los comandos de solo lectura se pueden mostrar tal cual
VisibleCmdlets = @(
'Get-Service',
'Get-EventLog'
)
# Restart-Service, que cambia el estado, se deja fuera de VisibleCmdlets a propósito (se explica más adelante)
# VisibleFunctions solo acota "qué funciones se cargan en la sesión";
# no define las funciones. Las funciones propias se escriben en FunctionDefinitions,
# y además hay que incluir el nombre en VisibleFunctions (hacen falta las dos cosas)
VisibleFunctions = @('Get-KsAppStatus', 'Restart-KsAppService')
FunctionDefinitions = @(
@{
Name = 'Get-KsAppStatus'
ScriptBlock = {
# El cuerpo de la función se ejecuta con el modo de lenguaje predeterminado y no está sujeto a las restricciones de JEA.
# No pase la entrada del usuario directamente a un comando peligroso
Get-Service -Name 'KsAppService' |
Microsoft.PowerShell.Utility\Select-Object Name, Status, StartType
}
},
@{
# El reinicio se expone como una función que "solo recibe el destino como argumento"
Name = 'Restart-KsAppService'
ScriptBlock = {
param(
[Parameter(Mandatory)]
[ValidateSet('KsAppService', 'Spooler')]
[string] $Name
)
Microsoft.PowerShell.Management\Restart-Service -Name $Name
}
}
)
VisibleExternalCommands = @()
}
El ValidateSet de VisibleCmdlets por sí solo no basta para restringir con seguridad los comandos que cambian el estado. La restricción mediante Parameters y ValidateSet solo se evalúa cuando ese parámetro se enlaza realmente. Como Restart-Service puede recibir un ServiceController por el pipeline,
Get-Service WinRM | Restart-Service
si se escribe así, -Name no llega a enlazarse y el ValidateSet se salta por completo. En un punto de conexión pensado para «solo se pueden reiniciar KsAppService y Spooler», se acaba pudiendo reiniciar cualquier servicio.
Lo mismo puede ocurrir con otro conjunto de parámetros. Aunque se añada un ValidateSet de LogName a Get-WinEvent,
Get-WinEvent -ProviderName Microsoft-Windows-Security-Auditing -MaxEvents 1
si se escribe así, LogName no se enlaza y la restricción no surte efecto. Como la sesión JEA se ejecuta como administrador virtual, en este caso incluso se podría llegar a leer el registro de Security.
Por eso, en el ejemplo anterior no se incluye Restart-Service en VisibleCmdlets, sino que solo se publica una función contenedora que únicamente recibe el destino como argumento (Restart-KsAppService). Si la única vía de entrada es el argumento, no hay forma de eludirla.
La restricción mediante Parameters y ValidateSet solo funciona cuando «ese parámetro llega a enlazarse». En comandos donde se puede evitar el enlace mediante entrada por pipeline u otro conjunto de parámetros, la propia restricción queda inutilizada. Como norma, el comando cuyos argumentos se quiera acotar debe envolverse en una función contenedora.
Aquí hay dos aspectos con los que es fácil equivocarse. El primero es que VisibleFunctions no crea la función. Una función mencionada solo por nombre no existe en la sesión, y el usuario la ve como «ese comando no existe». Las funciones propias hay que definirlas en FunctionDefinitions y, además, incluirlas en VisibleFunctions.14 Si el número aumenta, resulta más manejable extraerlas a un módulo de script y publicar las funciones de ese módulo mediante VisibleFunctions.
El segundo es que el cuerpo de la función no está sujeto a las restricciones de JEA.14 Si quiere usar en su comportamiento original un comando restringido que JEA sustituye, como Select-Object, llámelo con el nombre completamente calificado, como en el ejemplo anterior: Microsoft.PowerShell.Utility\Select-Object. Dicho de otro modo, dentro de la función se puede hacer cualquier cosa, así que evite por completo escribir código que pase la entrada del usuario a algo como Invoke-Expression.
Paso 2: definir, en el archivo de configuración de sesión (.pssc), a quién se le asigna qué rol
# SessionType: servidor remoto restringido (NoLanguage de forma predeterminada)
# RunAsVirtualAccount: se ejecuta como cuenta de administrador virtual
# TranscriptDirectory: registra lo realizado
$pssc = @{
Path = '.\KsHelpDesk.pssc'
SessionType = 'RestrictedRemoteServer'
RunAsVirtualAccount = $true
TranscriptDirectory = 'C:\JeaTranscripts'
RoleDefinitions = @{ 'EXAMPLE\HelpDesk' = @{ RoleCapabilities = 'KsHelpDesk' } }
}
New-PSSessionConfigurationFile @pssc
Paso 3: registrar
Register-PSSessionConfiguration -Name 'KsHelpDesk' -Path .\KsHelpDesk.pssc -Force
El usuario se conecta de la siguiente manera.
Enter-PSSession -ComputerName 'appsrv01' -ConfigurationName 'KsHelpDesk'
# Solo puede usar los comandos permitidos. Restart-Service también, solo para el servicio indicado
JEA tiene tres puntos clave.6
- El usuario no tiene permisos de administrador. La ejecución se realiza en el lado de la cuenta virtual
- La sesión se configura como servidor remoto restringido. De forma predeterminada, el modo de lenguaje queda restringido y no se puede ejecutar código arbitrario
- Lo realizado queda registrado mediante transcripción. Se puede auditar quién hizo qué
Tenga en cuenta que, como se ha indicado antes, el archivo de funciones de rol debe estar bajo la carpeta RoleCapabilities de un módulo, y se presupone que ese módulo se puede encontrar desde $env:PSModulePath. Si el nombre de rol indicado en RoleDefinitions no se puede resolver, compruebe primero con Get-Module -ListAvailable si el módulo es visible.6
Los puntos de conexión registrados se pueden listar con Get-PSSessionConfiguration. Como se indicó al principio, JEA presupone la configuración de ejecución remota, así que si no puede conectarse, delimite el problema empezando por el lado de WinRM, no por la definición de JEA (véase «Introducción a PowerShell Remoting (WinRM)»).
8. Lista de comprobación práctica
| Elemento | Prioridad | Estado |
|---|---|---|
| Se habilitó el registro de bloques de script (4104) | Alta | Distribuido por GPO a toda la empresa215 |
| Se amplió el tamaño de los registros relacionados con PowerShell | Alta | Con el valor predeterminado desaparece en pocos días |
| Se habilitó la transcripción y se centralizó en un recurso compartido de solo escritura | Alta | No se guarda de forma local en el equipo2 |
| Se deshabilitó el motor de PowerShell 2.0 | Alta | Vía de elusión del registro y de AMSI3 |
| El producto antimalware admite AMSI | Alta | Habilitado de forma predeterminada1 |
| No se confunde la directiva de ejecución con un límite de seguridad | Media | La firma tiene otro valor distinto4 |
| Se hizo inventario del alcance de los permisos de administrador otorgados | Alta | La amplitud de los permisos determina el alcance del daño |
| Se delegaron tareas rutinarias con JEA | Media | Se vincula directamente a la reducción de permisos de administrador6 |
| Se consideró WDAC/AppLocker | Media | Va de la mano con la restricción del modo de lenguaje5 |
| Se distribuyó también la configuración de registro en el lado de PowerShell 7 | Media | La configuración es distinta entre 5.1 y 7 |
9. Resumen
- Prohibir PowerShell tiene poca eficacia y detiene la operación diaria. El enfoque es «visibilidad y restricción».
- La prioridad máxima es habilitar el registro de bloques de script (4104). Incluso el código ofuscado se registra ya expandido. Aplíquelo junto con la ampliación del tamaño del registro.
- El punto clave de la transcripción es centralizarla en un recurso compartido que el usuario no pueda eliminar.
- Deshabilite el motor de PowerShell 2.0. Es para no dejar una vía en la que ni el registro ni AMSI surten efecto.
- La directiva de ejecución no es un límite de seguridad. La firma tiene valor como detección de manipulación, pero no la convierta en el eje de la defensa.
- La amplitud de los permisos determina la amplitud del daño. Con JEA, delegando «solo las operaciones necesarias», se puede operar sin repartir permisos de administrador.
Descarga del código de ejemplo
El código tratado en este artículo se distribuye recopilado en un formato listo para ejecutar. Incluye la habilitación de la configuración de registro y la creación de un punto de conexión JEA.
Descargar el código de ejemplo (zip)
Como los ejemplos de este artículo dependen de Windows y del inquilino, no se ha realizado una verificación de ejecución. Se ha llevado a cabo análisis de sintaxis y análisis estático con PSScriptAnalyzer sobre todos los archivos, pero confirme siempre el funcionamiento en su propia máquina de verificación.
# Análisis de sintaxis + análisis estático (se puede ejecutar también fuera de Windows)
./Invoke-SampleTests.ps1
Los valores de configuración (rutas, nombres de servidor, ID de inquilino, etc.) son ejemplos. No los ejecute tal cual en un entorno de producción; adáptelos al entorno de su propia organización.
Artículos relacionados
- Directiva de ejecución y firma de scripts en PowerShell — dejar atrás la operación de «tapar con Bypass»
- Consultar el registro de eventos de forma práctica con Get-WinEvent — la velocidad de filtrado determina el tiempo de investigación
- Introducción a PowerShell Remoting (WinRM) — administrar varios equipos Windows de una sola vez
- Manejo seguro de credenciales en PowerShell — erradicar las contraseñas en texto plano de los scripts
- Lista mínima de comprobación de seguridad para aplicaciones Windows
- Lectura de las «10 principales amenazas de seguridad de la información 2026» de la IPA desde la perspectiva de los sistemas empresariales
Áreas de consultoría relacionadas
KomuraSoft LLC atiende consultas sobre revisión de la configuración de seguridad de entornos operativos Windows, diseño operativo que incluye la delegación de permisos (JEA), y la implantación y aprovechamiento de registros de auditoría.
- Consultoría técnica y revisión de diseño
- Investigación de fallos y análisis de causas
- Modificación y mantenimiento de software Windows existente
- Contacto
Referencias
-
Microsoft Learn, Antimalware Scan Interface (AMSI). Sobre que AMSI es el mecanismo por el cual las aplicaciones y servicios entregan contenido a cualquier producto antimalware para que lo inspeccione, que los motores de scripting de Windows, incluido PowerShell, están integrados con él, y que incluso un script ofuscado se puede inspeccionar con el contenido tal como está en el momento de la ejecución. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, about_Logging_Windows. Sobre los tres tipos de funciones de registro (registro de módulos, registro de bloques de script y transcripción), su habilitación mediante directiva de grupo y mediante el registro (bajo HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell), el hecho de que el registro de bloques de script se registre como ID de evento 4104 con el código ya sin ofuscar, que el registro de módulos se registre como ID de evento 4103, y sobre la configuración de OutputDirectory y EnableInvocationHeader de la transcripción. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Microsoft Learn, Desuso de Windows PowerShell 2.0. Sobre que el motor de Windows PowerShell 2.0 está en desuso y se recomienda deshabilitarlo, y que se ofrece como función opcional de Windows que se puede habilitar o deshabilitar. ↩ ↩2 ↩3
-
Microsoft Learn, about_Execution_Policies. Sobre que la directiva de ejecución no es un límite de seguridad, sino una función de seguridad para evitar que un usuario ejecute un script sin querer, y sobre la existencia de varios medios para eludirla. ↩ ↩2 ↩3
-
Microsoft Learn, about_Language_Modes. Sobre los elementos del lenguaje disponibles en cada modo (FullLanguage, ConstrainedLanguage, RestrictedLanguage y NoLanguage), la comprobación del modo actual mediante $ExecutionContext.SessionState.LanguageMode, el hecho de que PowerShell se ejecute en modo de lenguaje restringido en entornos donde el control de aplicaciones mediante WDAC o AppLocker está habilitado, y que la configuración manual del modo de lenguaje no está pensada como función de seguridad. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Introducción a Just Enough Administration (JEA). Sobre que JEA es un mecanismo que delega únicamente tareas administrativas concretas sin otorgar permisos de administrador, la restricción mediante VisibleCmdlets y mediante parámetros y ValidateSet en el archivo de funciones de rol (.psrc), los elementos RestrictedRemoteServer, RunAsVirtualAccount, TranscriptDirectory y RoleDefinitions del archivo de configuración de sesión (.pssc), el registro mediante Register-PSSessionConfiguration, y sobre que en una sesión JEA el modo de lenguaje queda restringido. Sobre la necesidad de colocar el archivo de funciones de rol en la carpeta RoleCapabilities de un módulo de PowerShell (dejarlo en un estado detectable como módulo, creación con New-PSRoleCapabilityFile), véase JEA Role Capabilities. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, icacls. Sobre el comando para mostrar y modificar las listas de control de acceso de archivos y carpetas, la concesión de permisos con /grant y la eliminación de permisos ya concedidos con /remove:g, los símbolos detallados de permisos que se pueden indicar en la máscara de permisos (DE=eliminar, DC=eliminar subcarpetas y archivos, WD=escribir datos/crear archivo, AD=añadir datos/crear subcarpeta, WA=escribir atributos, WEA=escribir atributos extendidos, RA=leer atributos, X=ejecutar/recorrer, F=acceso total, etc.), y sobre los indicadores de herencia ((OI)=herencia de objeto, (CI)=herencia de contenedor). Sobre la creación de un recurso compartido y la asignación de permisos de acceso, véase New-SmbShare (-FullAccess / -ChangeAccess / -ReadAccess); sobre la deshabilitación de la herencia, véase ObjectSecurity.SetAccessRuleProtection (el primer argumento protege la herencia y el segundo indica si se conservan las ACE ya heredadas). Este método solo puede actuar sobre el tratamiento de las ACE heredadas; las ACE asignadas directamente a ese objeto quedan fuera de su alcance. Para eliminar las asignaciones directas hay que quitarlas de forma individual, por ejemplo con ObjectSecurity.RemoveAccessRuleSpecific. ↩ ↩2 ↩3
-
Microsoft Learn, Grupos de identidades especiales (Special Identity Groups). Sobre que
OWNER RIGHTS(S-1-3-4) es un grupo que representa al propietario actual del objeto, y que cuando se aplica al objeto una ACE con este SID, el sistema ignora los permisos implícitosREAD_CONTROLyWRITE_DACotorgados al propietario. Dicho a la inversa, sin esta ACE, el propietario puede reescribir implícitamente la DACL. ↩ ↩2 -
Microsoft Learn, File Access Rights Constants. Sobre que
FILE_APPEND_DATAse define como «For a file object, the right to append data to the file. (For local files, write operations will not overwrite existing data if this flag is specified without FILE_WRITE_DATA.)», que la escritura exclusiva de adición se cumple cuando se abre especificandoFILE_APPEND_DATAsinFILE_WRITE_DATA, y sobre el significado deFILE_WRITE_DATA/FILE_ADD_FILEyFILE_DELETE_CHILD. Sobre la correspondencia de los derechos de acceso genéricos (FILE_GENERIC_WRITE=FILE_APPEND_DATA+FILE_WRITE_ATTRIBUTES+FILE_WRITE_DATA+FILE_WRITE_EA+STANDARD_RIGHTS_WRITE+SYNCHRONIZE,FILE_GENERIC_READ=FILE_READ_ATTRIBUTES+FILE_READ_DATA+FILE_READ_EA+STANDARD_RIGHTS_READ+SYNCHRONIZE), véase File Security and Access Rights. ↩ ↩2 -
PowerShell,
TranscriptionOption.FlushContentToDisk(src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs). Sobre que la vía de escritura de la transcripción se abre connew FileStream(this.Path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read), y que solo si eso falla se vuelve a abrir connew FileStream(this.Path, FileMode.Append, FileAccess.Write, FileShare.Read), y sobre que no existe ninguna vía que soliciteFILE_APPEND_DATAen solitario. ↩ -
.NET,
SafeFileHandle.Open(src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs). Sobre queFileAccess.Read/FileAccess.Writese traducen respectivamente aGENERIC_READ/GENERIC_WRITE, y queFileMode.Appendsimplemente se sustituye porFileMode.OpenOrCreateantes de llamar aCreateFile, sin que la máscara de acceso llegue nunca a usarFILE_APPEND_DATAen solitario. ↩ -
Microsoft Learn, Configuraciones de sesión de JEA. Sobre que, al indicar una carpeta en
TranscriptDirectorydel archivo de configuración de sesión, la transcripción se registra automáticamente, y sobre «Transcripts are written to the folder by the Local System account, which requires read and write access to the directory. Standard users should have no access to the folder. Limit the number of security administrators that have access to audit the transcripts.» (los usuarios estándar no deben tener acceso a esta carpeta, y hay que limitarlo a los administradores de seguridad que auditan). ↩ -
Microsoft Learn, Requisitos previos de JEA. Sobre que JEA está disponible desde PowerShell 5.0, que PowerShell Remoting es la base de JEA y que debe estar habilitado y correctamente protegido antes de usar JEA, y que desde Windows Server 2012 PowerShell Remoting está habilitado de forma predeterminada; si no lo está, se puede habilitar ejecutando Enable-PSRemoting en una ventana de PowerShell con permisos de administrador. ↩ ↩2 ↩3
-
Microsoft Learn, JEA Role Capabilities. Sobre la necesidad de definir las funciones propias en FunctionDefinitions y además incluir su nombre en VisibleFunctions («Don’t forget to add the name of your custom functions to the VisibleFunctions field so they can be run by the JEA users.»), que el cuerpo de la función (el bloque de script) se ejecuta con el modo de lenguaje predeterminado del sistema y no está sujeto a las restricciones de JEA, que para usar en su implementación original un comando restringido que JEA sustituye se necesita el nombre completamente calificado (Microsoft.PowerShell.Utility\Select-Object), el método de extraer las funciones a un módulo de script y publicarlas mediante VisibleFunctions cuando son numerosas, y la necesidad de que la carpeta del módulo contenga un archivo con el mismo nombre que la carpeta. ↩ ↩2
-
Microsoft Learn, Set-ItemProperty. Sobre que, al usar el proveedor del registro, -Type se añade como parámetro dinámico y permite indicar el tipo de dato del valor del registro (String / ExpandString / Binary / DWord / MultiString / QWord, etc.), incluido el hecho de que se crea si el valor no existe. ↩
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Investigar el registro de eventos con Get-WinEvent de forma práctica — la velocidad del filtrado determina el tiempo de investigación
Cómo agilizar la investigación del registro de eventos de Windows con PowerShell. Por qué filtrar con Where-Object es lento, cuándo usar ...
Auditoría de seguridad en Windows e investigación práctica del registro de eventos — cómo convertirse en un responsable de sistemas capaz de leer el 4625
Guía práctica para investigar fallos de inicio de sesión: directiva de auditoría básica y detallada, subcategorías mínimas, cómo leer 462...
Guía práctica de Windows LAPS — abandone la contraseña de administrador local común a todos los PC
La contraseña de administrador local común permite que un PC comprometido exponga a todos a Pass-the-Hash. Cubrimos la rotación automátic...
Guía práctica del almacén de certificados de Windows — ¿en el de usuario o en el de equipo?
¿En qué almacén debe colocarse un certificado de cliente, en el de usuario o en el del equipo? Esta guía repasa certmgr.msc y certlm.msc,...
El firewall de Windows y las aplicaciones empresariales — Registre las reglas de entrada con el instalador
El firewall de Windows es la causa típica de que "funcione en el equipo de desarrollo pero no se comunique en el cliente". Explicamos el ...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Desarrollo de aplicaciones para Windows
Aplicaciones empresariales, integración de dispositivos y herramientas de comunicación, de los requisitos al desarrollo.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Deberíamos prohibir el uso de PowerShell en la empresa como medida de seguridad?
- No lo recomendamos: la eficacia es baja y el efecto secundario es grande. PowerShell es la propia base de administración de Windows y, en el fondo, es una funcionalidad sobre .NET. Bloquear el ejecutable no impide llamar a las mismas API por otros medios, así que apenas supone un obstáculo para un atacante, mientras que sí detiene con certeza la administración legítima y la automatización. El enfoque realista no es la prohibición, sino la "visibilidad y la restricción": registrar lo que se ejecuta con el registro de bloques de script, deshabilitar las versiones antiguas y, cuando haga falta, acotar el alcance de ejecución con el modo de lenguaje o JEA.
- Si habilito el registro de bloques de script, ¿no generará una cantidad enorme de registros?
- En efecto aumenta, así que actívelo junto con el ajuste del tamaño del registro. Si deja el tamaño máximo del registro Microsoft-Windows-PowerShell/Operational en su valor predeterminado, se sobrescribirá en poco tiempo y no quedará nada justo cuando lo necesite. En la operación diaria, reserve un tamaño suficiente y, si hace falta, centralice con un SIEM o con reenvío de eventos. Existe además una configuración más detallada que registra el "inicio y fin de cada llamada", pero genera un volumen de salida muy alto, así que normalmente basta con habilitar solo el registro predeterminado de bloques de script.
- ¿Configurar la directiva de ejecución en AllSigned equivale a una medida de seguridad?
- La directiva de ejecución no es un límite de seguridad. La propia documentación oficial señala que es un mecanismo para evitar que un usuario ejecute un script peligroso sin querer, no para detener operaciones malintencionadas, porque existen varias formas de eludirla. Trabajar con firmas tiene otro valor, el de poder verificar la integridad de lo distribuido, pero el eje principal de la defensa debe apoyarse en la visibilidad mediante el registro, la inspección con AMSI y la restricción de permisos con el modo de lenguaje o JEA.
- ¿Está bien configurar manualmente el modo de lenguaje restringido (ConstrainedLanguage)?
- No se recomienda configurarlo manualmente mediante variables de entorno, porque es fácil de eludir y no funciona como una función de seguridad real. El modo de lenguaje restringido está pensado, por diseño, para activarse automáticamente en PowerShell como resultado de configurar el control de aplicaciones con WDAC (Windows Defender Application Control) o AppLocker. Restringir solo el modo de lenguaje sin implantar control de aplicaciones no constituye una defensa efectiva.
- Quiero que el personal de soporte técnico solo pueda reiniciar un servicio concreto de un servidor.
- JEA (Just Enough Administration) es el mecanismo pensado exactamente para eso. Se define, en un archivo de funciones de rol, "permitir solo este comando, con este parámetro, con este valor" y se registra como configuración de sesión. El usuario puede ejecutar únicamente las operaciones permitidas a través de una cuenta virtual, sin llegar a tener permisos de administrador. La sesión se configura como servidor remoto restringido y, de forma predeterminada, también se restringe el modo de lenguaje, de modo que no queda margen para ejecutar código arbitrario. Lo realizado puede registrarse mediante una transcripción.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.