Auditar grupos críticos y alertar por mail en Active Directory
Es muy importante para todo administrador de seguridad conocer en todo momento quienes tienen cuentas de usuario con privilegios elevados y "vigilarlos". A partir de esto se desprende que es todavía más importante saber cuando se otorgan privilegios a usuarios.
En todo dominio existen grupos de usuario utilizados por administradores o helpdesk que cuentan con mayores privilegios que los usuarios comunes, por obvias razones. El problema es cuando se asignan estos privilegios y no todos (o ninguno) los administradores están informados. El caso más riesgoso es cuando un atacante logra escalar privilegios gracias a algún exploit o vulnerabilidad conocida (se acuerdan de pass-the-hash?).
Active Directory posee grupos bien definidos con privilegios elevados, siendo "Domain Admins" el Dios supremo. Cada organización puede definir sus propios grupos, pero "Domain Admins" estará siempre presente como la autoridad máxima, así que debe estar auditado. Otros grupos de importancia son "Enterprise Admins", "Schema Admins" y "Backup Operators".
Este artículo brinda los pasos necesarios para auditar grupos en Active Directory y enviar mensajes de alerta por mail cuando un grupo crítico esté siendo modificado. De esta forma los responsables de Seguridad de la Información estarán notificados al instante cuando ocurra una brecha de seguridad donde un usuario escale privilegios en el dominio. Lo que se auditará serán inserciones y eliminaciones de usuarios en los grupos que corresponda.


Habilitar auditoría de grupos en AD

La auditoría de usuarios/grupos/workstations en AD se realiza de la misma manera que la auditoría de directorios. Para hacerlo hay que utilizar la herramienta "Active Directory Users and Computers" (ADUC). En esta debe dirigirse a View y tildar "Advanced Features" para poder ver las propiedades avanzadas de los objetos.
Con la opción habilitada, resta dirigirse a la unidad organizativa (OU) donde se encuentran los grupos a auditar. Tal vez deseen habilitar la auditoría para todos los usuarios, o para usuarios/grupos particulares. Para esta explicación habilitaremos la auditoría a todos los objetos de la OU Users. Entonces, dirigirse a la OU Users y abrir las propiedades, allí dirigirse a la pestaña Security y en esta dar al botón Advanced. En esta ventana dirigirse a la pestaña Auditing, dar al botón "Add...", escribir Everyone y dar Ok. En la lista de propiedades a auditar, tildar todo menos "Full control", "List contents", "Read all properties" y "Read Permissions" (las lecturas generan demasiados logs y no son de demasiada utilidad)

Lo que se hace con esta configuración es que cada vez que cualquier usuario (por eso se elige Everyone) modifique un objeto de la OU Users se genere una entrada en el log de Windows.

Probablemente se encuentren con que esta auditoría ya se encuentra habilitada y heredada de la configuración del dominio, pero si no es así, con los pasos anteriores podrán configurarla.


Hook al evento y envío de mail

Cuando el evento que deseamos auditar se dispare deberemos realizar alguna acción. Como comenté al principio, nuestra acción será enviar un mail a los encargados de la seguridad, y en el mismo colocaremos el contenido de la entrada de log que generó el evento.
Si bien es muy simple generar una tarea que envíe un mail avisando del evento, la información que puede contener el mismo es muy escueta y no sirve de mucho más que estar informado. Lo mejor es contar con un script que lea la entrada del log y envíe el mail con el contenido de la misma.


Script en PowerShell

En la mayoría de los blogs explican cómo utilizar la herramienta wevtutil para enviar la información de los logs en el mail cuando se ejecuta el evento. Wevtutil permite parsear los logs y obtener información, para lo cual es muy útil, pero para este caso, no lo es tanto. El problema es que estas soluciones se basan en crear un script que lee la última entrada del log que cumple con las condiciones del evento buscado, y de esta forma puede ocurrir que si dos o más eventos con las mismas características se generan en corto intervalo de tiempo, perdamos información.

La mejor solución que encontré es la explicada en el artículo Trigger a PowerShell Script from a Windows Event, a partir de la cual generé el siguiente script (luego de pelear un buen rato con PS), denominado eventreporter.ps1:

  # Script by demasiadovivo
  param($eventRecordID,$eventChannel)

  $event = Get-WinEvent -Logname $eventChannel -FilterXPath "<QueryList><Query Id='0' Path='$eventChannel'><Select Path='$eventChannel'>*[System[(EventRecordID=$eventRecordID)]]</Select></Query></QueryList>"

  $smtp_server = "mailserver.ejemplo.com"
  $msg = New-Object Net.Mail.MailMessage
  $msg.From = "security_alert@ejemplo.com"
  $msg.To.Add("demasiadovivo@ejemplo.com")
  $msg.subject = "ALERTA! se hicieron cambios en grupo crítico"

  $data =$event.Message
  $data = [String]::join([environment]::NewLine, $data)
  $msg.body = $data

  $smtp = new-object Net.Mail.SmtpClient($smtp_server)
  $smtp.Send($msg)

Vayamos por partes para comprender su funcionamiento. El script primero obtiene los parámetros eventRecordID y eventChannel, donde eventRecordID es el valor que identifica la línea del log donde se encuentra el evento que disparó la acción, y eventChannel es el log donde se encuentra (System, Security, Application, etc). No confundir eventRecordID con eventID. El primero es el identificador de la entrada del evento, algo así como el número de línea dentro del log, mientras que eventID es el identificador que utiliza Windows para distinguir el tipo de evento (por ejemplo Logon). Estos valores los envía la tarea programada que se dispara con el evento, como veremos después.
El siguiente paso es obtener la entrada del log correspondiente al evento. Para ello se utiliza el comando Get-WinEvent y los valores de eventRecordID y eventChannel. El evento se obtiene con la consulta "<QueryList><Query Id='0' Path='$eventChannel'><Select Path='$eventChannel'>*[System[(EventRecordID=$eventRecordID)]]</Select></Query></QueryList>" que se encuentra en formato XML y utiliza el lenguaje XPath (http://en.wikipedia.org/wiki/XPath). Se utiliza XPath en las consultas porque internamente los logs se guardan en formato XML. Para entender un poco mejor cómo se arman consultas en este lenguaje, leer MSDN XPath Syntax.
Finalmente el script arma un mail con el contenido obtenido del log ($event.Message) y lo envía a las direcciones designadas. Una línea que puede llamar la atención es "[String]::join([environment]::NewLine, $data)". Me llevó más de 3 horas encontrar cómo hacer que el mensaje del log original conserve los enters, lo cual se logra a través de esa línea... en bash todo esto sería mucho más fácil...

Recuerden que para poder ejecutar scripts de PowerShell, primero deben cambiar la política de ejecución de la máquina. Esto se realiza ejecutando lo siguiente:
  Set-ExecutionPolicy RemoteSigned


Asignar una tarea al evento

Bien, ya activamos la auditoría de los grupos y tenemos el script a ejecutar cuando el evento ocurra. El siguiente paso es asignar la ejecución del script con el evento. Esto se hace a través de la herramienta "Computer Management". Hay que tener en cuenta en este punto que la tarea deberá agregarse en todos los controladores de dominio (DC), dado que las modificaciones de grupos podrían realizarse en cualquiera de ellos.

Como todo en el mundo MS, nada es tan directo y uno termina teniendo que utilizar algunas artimañas para lograr lo que desea. Para poder entregar al script los argumentos eventRecordID y eventChannel hay que hacer algunas modificaciones manuales a una tarea, como veremos a continuación.

Primero hay que crear la tarea que se ejecutará al producirse el evento. Para ello, dirigirse a "Task Scheduler -> Task Scheduler Library" dentro de "Computer Management". Allí dando click derecho se elige "Create Task..." y se obtiene la ventana de configuración de la tarea. A la misma habrá que asignar un nombre y opcionalmente una descripción. Como queremos que se ejecute en modo batch, hay que cambiar el usuario con el que se ejecutará la tarea, así que clickear el botón "Change User or Group..." y escribir "SYSTEM" (o bien crear un usuario con los privilegios suficientes para leer logs).
En la pestaña "Triggers" se agrega la condición que debe darse para que se ejecute el script. En ella debe crearse un nuevo Trigger con "New..." y elegir la opción "On an event" como disparador de la tarea. Luego elegir "Custom" en las settings y dar a "Edit Event Filter...". En esta ventana ir a la pestaña "XML", seleccionar "Edit query manually" y utilizar el siguiente código:
  <QueryList>
    <Query Id="0" Path="Security">
      <Select Path="Security">*[(System/EventID=4732) or (System/EventID=4733)] and *[EventData/Data [@Name='TargetUserName']='Backup Operators'] or *[(System/EventID=4728) or (System/EventID=4729)] and *[(EventData/Data[@Name='TargetUserName']='Domain Admins') or (EventData/Data[@Name='TargetUserName']='Schema Admins') or (EventData/Data[@Name='TargetUserName']='Enterprise Admins')]</Select>
    </Query>
  </QueryList>
Como se puede observar, la consulta es similar a la utilizada en el script, en formato XML y utilizando el lenguaje XPath para seleccionar los siguientes eventos:
- 4732/4733 se agregó/eliminó un usuario a un grupo local. Se utilizan en conjunto con el grupo "Backup Operators" que es local.
- 4728, 4729 se agregó/eliminó un usuario a un grupo global. Se utilizan en conjunto con los grupos "Domain Admins", "Schema Admins" y "Enterprise Admins" que son globales.
  NOTA 1: si en un futuro desean auditar algún grupo universal, los identificadores de eventos son 4756 para cuando se agrega, y 4757 para cuando se elimina un usuario del grupo.
  NOTA 2: para entender mejor la consulta, pueden observar la estructura XML de los eventos en MSDN Event Schema Elements.
Continuando en la pestaña "Actions", elegir "New" para agregar la ejecución del script. Dentro de la ventana emergente elegir la acción "Start a program", en el nombre del programa/script escribir "PowerShell.exe", y en la sección de argumentos escribir ".\eventreporter.ps1 -eventRecordID $(eventRecordID) -eventChannel $(eventChannel)", reemplazando el path del script por el que corresponda. Con esto ya termina la definición de la tarea y pueden dar OK para terminar.
Como se ve, se utilizan los parámetros eventRecordID y eventChannel en la llamada del script, los cuales se utilizan luego para obtener el identificador de la entrada. Acá es donde entra en juego la "magia" del administrador. Para variar, nada es coherente con las herramientas de MS y no es posible indicar, de forma directa, cómo asignar los valores a los parámetros... gracias MS!!! Por ello es necesario aplicar el siguiente artilugio.
Primero deben dar click derecho sobre la tarea recién creada, elegir la opción "Export" y guardar el XML en alguna ubicación. Supongamos que llamamos a este XML como el nombre de la tarea, "Eventos_criticos.xml". Una vez hecho esto, eliminen la tarea. Sí eliminen, ya la volveremos a crear a partir del XML.
Ahora abrir el XML recién generado con algún editor de texto y buscar la sección "<Triggers>". Dentro de la misma encontrarán la sección "<EventTrigger>". En ella colocar el siguiente código:
      <ValueQueries>
        <Value name="eventChannel">Event/System/Channel</Value>
        <Value name="eventRecordID">Event/System/EventRecordID</Value>
        <Value name="eventSeverity">Event/System/Level</Value>
      </ValueQueries>
es decir, la sección completa debe quedar:
  <Triggers>
    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription><QueryList><Query Id="0" Path="Security"><Select Path="Security">*[(System/EventID=4732) or (System/EventID=4733)] and *[EventData/Data [@Name='TargetUserName']='Backup Operators'] or *[(System/EventID=4728) or (System/EventID=4729)] and *[(EventData/Data[@Name='TargetUserName']='Domain Admins') or (EventData/Data[@Name='TargetUserName']='Schema Admins') or (EventData/Data[@Name='TargetUserName']='Enterprise Admins')]</Select></Query></QueryList></Subscription>
      <ValueQueries>
        <Value name="eventChannel">Event/System/Channel</Value>
        <Value name="eventRecordID">Event/System/EventRecordID</Value>
        <Value name="eventSeverity">Event/System/Level</Value>
      </ValueQueries>
    </EventTrigger>
  </Triggers>
Salvar los cambios en el XML.
Finalmente volvemos a crear la tarea a partir del XML modificado. Para ello ir a Task Scheduler, dar click derecho y elegir la opción "Import Task", la cual les permitirá buscar el archivo recién creado. Otra forma de hacer esto último es ejecutando desde consola el siguiente comando:
  C:\>schtasks /create /TN "Event Viewer Tasks\Eventos_Criticos" /XML Eventos_criticos.xml

Lo que se hizo aquí es indicarle a la tarea cuáles son los valores que debe utilizar para los parámetros eventChannel (Event/System/Channel), eventRecordID (Event/System/EventRecordID) y eventSeverity (Event/System/Level), que luego entregará al script creado en la sección anterior. No hay forma de hacer esto modificando la tarea desde el GUI, esta es la única manera... lindo verdad?...

En los otros controladores de dominio solamente deberán importar el XML recién creado y copiar el script a una ubicación donde el DC pueda ejecutar.


Acerca de...

Como comentario final solo me resta renegar una vez más contra MS. Algo tan simple como auditar un log, que en algún *nix sería cosa de dos minutos, se convierte en un dolor de cabeza. Siguiendo los pasos que describo en este artículo podrán hacerlo en dos minutos, pero encontrar la información necesaria para poder armar esto me llevó casi dos tardes. Windows es fácil de usar para el usuario final, pero cuando se quiere hacer algo semi-avanzado de administración te hace la vida imposible.
Dirán, me quejo pero lo uso. Y si, no me queda otra, desgraciadamente la gran mayoría de las organizaciones con una infraestructura IT mediana utilizan Active Directory, y para ser administrador de seguridad, necesitas conocer esta tecnología y hacer cosas como la que describí en este artículo. Lo único que espero es que en algún momento se tome conciencia y se deje de utilizar esta bosta para pasar a algo mejor.


Referencias

- Trigger a PowerShell Script from a Windows Event
- AD DS Auditing Step-by-Step Guide
- Getting event log contents by email on an event log trigger
- Email with event log attachment on an event log trigger
- MSDN XPath Syntax
- Event Schema Elements
- Attaching Tasks to Event Viewer Logs and Events
- PowerShell Cookbook - Chapter 23. Event Logs
- PowerShell Cookbook - Appendix C. XPath Quick Reference