Estimados amigos de Inseguros !!!
Hoy vamos con KQL en Microsoft Sentinel. Y voy a empezar con una confesión: la primera vez que abrí Log Analytics me quedé mirando la pantalla como el que mira el cuadro de mandos de un avión…
Sabes que ahí está todo. No sabes por dónde se empieza.
Este post es el que me habría gustado leer aquel día. No es una referencia del lenguaje —para eso está la documentación oficial de Kusto, que es buena— sino el camino corto hasta escribir consultas que sirvan para algo.
KQL no es SQL, y esa es la buena noticia
Kusto Query Language se lee de arriba abajo, como una tubería. Empiezas por una tabla, y vas encadenando operaciones con el pipe.
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType != 0
| summarize Intentos = count() by UserPrincipalName
| order by Intentos desc
Tabla, filtro, agrupación, orden. Si entiendes esas cuatro líneas, ya puedes trabajar.
Y a diferencia de SQL, aquí no hay que pensar en el orden raro del SELECT. Escribes en el orden en que piensas: de dónde saco los datos, qué me quedo, cómo lo agrupo.
Las tablas donde vive lo importante
En un tenant Microsoft te vas a mover casi siempre entre estas cinco:
| Tabla | Qué contiene | Cuándo la usas |
|---|---|---|
SigninLogs | Inicios de sesión de Entra ID | Accesos sospechosos, geografía, MFA, acceso condicional |
AuditLogs | Cambios de configuración de identidad | Quién creó una app, quién asignó un rol |
OfficeActivity | Actividad de Microsoft 365 | Buzones, SharePoint, OneDrive, reglas de correo |
AzureActivity | Operaciones sobre recursos | Creación/borrado de recursos, cambios en suscripción |
SecurityEvent | Eventos de Windows | Si tienes agente desplegado: procesos, logons, servicios |
¿Cómo sé qué columnas tiene cada una? SigninLogs | take 10 y a mirar. Sin vergüenza. Es lo que hago yo, cada vez que vuelvo a una tabla que llevo meses sin tocar.
Otro truco: search "texto" busca por todas las tablas. Es lento y no lo uses en producción con rangos amplios, pero para orientarte cuando no sabes ni dónde mirar, va muy bien.
Los operadores de KQL que cubren el 90%
where filtra filas. Es el que más vas a escribir. Ponlo lo antes posible en la consulta: cuanto antes reduzcas el volumen, más rápido va todo.
project selecciona columnas. Sin él, cada resultado tiene cuarenta campos y no ves nada. project-away hace lo contrario: quita las que estorban.
summarize agrupa y agrega. count(), dcount() para valores distintos, min(), max(), make_set() para juntar valores en una lista.
join cruza tablas. Es el más delicado: presta atención al tipo (inner, leftouter) porque cambia el resultado por completo.
let guarda algo para reutilizarlo. Sirve para un valor suelto y también para una subconsulta entera, lo que te permite escribir consultas legibles en vez de churros de veinte líneas.
let VentanaTiempo = 7d;
let UsuariosCriticos = dynamic(["admin@dominio.com","backup@dominio.com"]);
SigninLogs
| where TimeGenerated > ago(VentanaTiempo)
| where UserPrincipalName in (UsuariosCriticos)
| project TimeGenerated, UserPrincipalName, IPAddress, Location, ResultType
Eso ya es una consulta que puedes guardar, parametrizar y reutilizar.
Cinco consultas KQL para el primer día
1. Fallos de autenticación agrupados por usuario. La del principio. Te dice quién está recibiendo intentos y cuántos.
2. Geografía de un usuario concreto:
SigninLogs
| where UserPrincipalName == "usuario@dominio.com"
| summarize Accesos = count() by Location, IPAddress
| order by Accesos desc
Si sale España, España, España… y un país donde no tienes a nadie, ya tienes por dónde tirar.
3. Picos de actividad fuera de horario:
SigninLogs
| where ResultType == 0
| summarize Logins = count() by bin(TimeGenerated, 1h)
| render timechart
bin() agrupa por intervalos y render te lo pinta. Es la forma más rápida de ver un pico que no debería estar ahí.
4. Cambios de rol recientes:
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName has "role"
| project TimeGenerated, OperationName, InitiatedBy, TargetResources
5. Reglas de reenvío de correo creadas:
OfficeActivity
| where Operation has "InboxRule"
| project TimeGenerated, UserId, Operation, Parameters
Esta última es de las que más veces me ha dado un incidente. Una regla que reenvía el buzón fuera es señal clásica de compromiso de cuenta.
Los cuatro errores que vas a cometer con KQL
Consultar sin filtro de tiempo. Te vas a comer el volumen entero de la tabla y la consulta va a tardar o a fallar. Empieza siempre por where TimeGenerated > ago(...).
Concluir que no hay ataque cuando no hay datos. Este es el gordo. Escribes una consulta, no devuelve nada, y piensas que estás limpio. Muchas veces lo que no hay es dato: el conector no está activado, o esa tabla no recibe ese tipo de evento. Antes de dar nada por bueno, un | count sin filtros te dice si ahí hay algo o estás consultando el vacío. Me ha pasado. Más de una vez 🙂
Copiar consultas sin entenderlas. Hay repositorios estupendos de consultas —el propio Sentinel trae plantillas— pero si no entiendes qué hace cada línea, no sabrás por qué no funciona en tu entorno. Y no va a funcionar, porque tus tablas y tus campos no son los del ejemplo.
Filtrar por ResultType == 0 sin saber qué significa. Cero es éxito. Todo lo demás son códigos de error distintos, y algunos son muy informativos: hay códigos específicos para MFA requerido, para bloqueo por acceso condicional, para credencial incorrecta. Aprenderte cuatro o cinco de esos códigos te ahorra muchísimo tiempo.
Cómo seguir practicando
Hay dos recursos gratuitos que merecen la pena de verdad.
La documentación de Microsoft Sentinel es sorprendentemente buena, sobre todo la parte de reglas analíticas.
Y Must Learn KQL, la serie de Rod Trent, que va del cero al uso real en capítulos cortos. Si solo vas a leer una cosa además de este post, que sea esa.
El problema no es la sintaxis
KQL se aprende escribiendo consultas contra datos reales. Y ahí está el cuello de botella de siempre: montar el laboratorio, decidir qué conectar primero, y no perderte entre workspaces, conectores y tablas antes de haber escrito la primera línea útil.
Y sé más o menos cómo acaba esto, porque lo veo cada semana. Guardas el post, te dices que el finde montas el workspace, y llega el finde. Tienes tres cursos empezados y ninguno terminado.
No es falta de ganas. Es que nadie avanza solo y a ratos sueltos.
Eso es lo que intentamos resolver en la membresía de SeguridadSI, y no a base de meterte más vídeos —contenido sobra en internet y ahora en la IA—, sino con una ruta que te dice por dónde empiezas, un directo cada 15 días para desatascarte y gente que va a lo mismo que tú. A tu ritmo, sin culpa: si la vida aprieta, pausas.
Dentro tienes el Curso de Introducción a Microsoft Sentinel, para dejarlo desplegado, con datos entrando y con tus primeras detecciones funcionando.
Y cuando eso ya rule, el siguiente escalón es Threat Hunting y DFIR con Microsoft Sentinel, donde KQL deja de ser sintaxis y pasa a ser la herramienta con la que investigas incidentes reales.
Y si todavía estás decidiendo por dónde entrar al mundo defensivo, lo conté en Curso SOC para empezar en Blue Team.
Más información: La Membresía de SeguridadSI
Espero que te guste y lo pruebes !!!
Gracias por leerme.