Crear consultas de varias fases en YARA-L

Disponible en:

En este documento se describe cómo las consultas de varias fases de YARA-L te permiten introducir la salida de una fase de una consulta directamente en la entrada de una fase posterior. Este proceso te ofrece un mayor control sobre la transformación de datos que una sola consulta monolítica.

Integrar consultas de varias fases con funciones actuales

Las consultas de varias fases funcionan junto con las siguientes funciones de Google Security Operations:

  • Reglas de detección compuestas: las consultas de varias fases complementan las reglas de detección compuestas. A diferencia de las reglas compuestas, las consultas de varias fases que usan la búsqueda pueden devolver resultados en tiempo real.

  • Intervalos de tiempo y reglas de varios eventos: puede usar consultas de varias fases para detectar anomalías comparando diferentes ventanas de tiempo en sus datos. Por ejemplo, puedes usar las fases de consulta iniciales para establecer una base de referencia durante un periodo prolongado y, después, usar una fase posterior para evaluar la actividad reciente en comparación con esa base de referencia. También puedes usar reglas de varios eventos para crear un tipo de comparación similar.

Las consultas de varias fases en YARA-L se admiten tanto en los paneles de control como en la búsqueda.

Las combinaciones ayudan a correlacionar datos de varias fuentes para proporcionar más contexto a una investigación. Al vincular eventos, entidades y otros datos relacionados, puede investigar escenarios de ataque complejos. Para obtener más información, consulta Usar combinaciones en la Búsqueda.

Definir la sintaxis de YARA-L de varias fases

Cuando configure una consulta multietapa, tenga en cuenta lo siguiente:

  • Fase de límite: las consultas de varias fases deben contener entre 1 y 4 fases con nombre, además de la fase raíz.
  • Sintaxis de orden: define siempre la sintaxis de la fase con nombre antes de definir la sintaxis de la fase raíz.

Crear una consulta YARA-L de varias fases

Para crear una consulta YARA-L de varias fases, sigue estos pasos.

Estructura y sintaxis de las fases

Ve a Investigación > Búsqueda. Sigue estos requisitos estructurales cuando definas las fases de tu consulta:

Sintaxis: usa la siguiente sintaxis para asignar un nombre a cada fase y separarla de las demás:

stage <stage name> { }

  • Llaves: coloca toda la sintaxis de la fase entre llaves {}.

  • Orden: define la sintaxis de todas las fases con nombre antes de definir la fase raíz.

  • Referencias: cada fase puede hacer referencia a fases definidas anteriormente en la consulta.

  • Fase raíz: una consulta debe tener una fase raíz, que se procesa después de todas las fases con nombre.

La siguiente fase de ejemplo, daily_stats, recoge estadísticas de red diarias:

stage daily_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $source = principal.hostname
  $target = target.ip
  $source != ""
  $target != ""
  $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)
  match:
    $source, $target by day
  outcome:
    $exchanged_bytes = sum($total_bytes)
}

Salida de la fase de acceso

Las fases posteriores pueden acceder a la salida de una fase con nombre mediante campos de fase. Los campos de fase se corresponden con las variables match y outcome de la fase y se pueden usar de forma similar a los campos del modelo de datos unificado (UDM).

Usa la siguiente sintaxis para acceder a un campo de fase:

$<stage name>.<variable name>

Marcas de tiempo de la ventana de acceso (opcional)

Si una fase con nombre usa una ventana de salto, deslizamiento o volcado, accede al inicio y al final de la ventana de cada fila de salida mediante estos campos reservados:

  • $<stage name>.window_start

  • $<stage name>.window_end

window_start y window_end son campos de números enteros expresados en segundos desde la época de Unix. Las ventanas en diferentes fases pueden variar de tamaño.

Limitaciones

Las consultas de varias fases tienen las siguientes restricciones funcionales y estructurales:

Límites de estructura y de fases

  • Fase raíz: solo se permite una fase raíz por consulta.

  • Etapas con nombre: se admiten un máximo de cuatro etapas con nombre.

  • Referencia de fases: una fase solo puede hacer referencia a fases definidas lógicamente antes que ella en la misma consulta.

  • Combinaciones: se permiten un máximo de cuatro combinaciones que no sean de tablas de datos en todas las fases.

  • Requisito de resultado: cada fase con nombre (excepto la fase raíz) debe incluir una sección match o una sección outcome. La sección outcome no requiere agregación.

Límites de ventanas y compatibilidad

  • Compatibilidad con funciones: las consultas de varias fases se admiten en Búsqueda y Paneles de control, pero no en Reglas.

  • Tipos de ventana: no mezcles diferentes tipos de ventana en una misma consulta.

  • Dependencia de ventanas: una fase que use una ventana de salto o una ventana deslizante no puede depender de otra fase que también use una ventana de salto o una ventana deslizante.

  • Tamaño de la ventana de tiempo: aunque las ventanas de tiempo de las diferentes fases pueden variar de tamaño, la diferencia entre ellas debe ser inferior a 720x.

Ejemplo: Diferencia de agregación de fases

No se permite la siguiente configuración de ventana de ejemplo:

stage monthly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
    $source = principal.hostname
    $target = target.ip
    $source != ""
    $target != ""
    $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)

  match:
    $source, $target by month

  outcome:
    $exchanged_bytes = sum($total_bytes)
}

$source = $monthly_stats.source
$target = $monthly_stats.target

match:
    $source, $target by minute

Si la fase monthly_stats agrega datos por mes y la fase raíz agrega el resultado de monthly_stats por minuto, cada fila de monthly_stats se asigna a 43.200 filas en la fase raíz (porque hay 43.200 minutos en un mes).

Limitaciones de las fases y las consultas

Cada fase de una consulta de varias fases tiene las siguientes restricciones:

  • La mayoría de las limitaciones que se aplican a una consulta de una sola fase también se aplican a cada fase individual:

  • Las consultas multietapa están sujetas a las mismas limitaciones que las consultas de estadísticas:

    • Consultas de estadísticas: 120 consultas por hora (API e interfaz de usuario)

    • Vistas de búsqueda de Google SecOps: 100 vistas por minuto

    • Las combinaciones de varias fases se admiten en la interfaz de usuario y en la API de EventService.UDMSearch, pero no en la API de SearchService.UDMSearch. Las consultas de varias fases sin combinaciones también se admiten en la interfaz de usuario.

Limitaciones de eventos y globales

Número máximo de eventos:

El número de eventos que pueden procesar simultáneamente las consultas multietapa está estrictamente limitado:

  • Eventos de UDM: se permiten un máximo de dos eventos de UDM.

  • Eventos de gráfico de contexto de entidad (ECG): se permite un máximo de un evento de ECG.

Limitaciones de las consultas globales:

Estos límites son restricciones de toda la plataforma que controlan la antigüedad y la cantidad de datos que puede devolver una consulta multietapa.

  • En el caso de un intervalo de tiempo de una consulta, el intervalo máximo de una consulta estándar es de 30 días.

  • El tamaño máximo del conjunto de resultados es de 10.000 resultados.

Ejemplos de consultas de varias fases

Los ejemplos de esta sección te ayudarán a entender cómo puedes crear una consulta completa de YARA-L de varias fases.

Ejemplo: Buscar conexiones de red inusualmente activas (horas)

En este ejemplo de YARA-L de varias fases se identifican pares de direcciones IP con una actividad de red superior a la normal. El objetivo son los pares que mantienen una actividad alta durante más de tres horas. La consulta incluye dos componentes obligatorios: la fase con nombre, hourly_stats, y la fase root.

En la fase hourly_stats, se buscan pares de principal.ip y target.ip con niveles altos de actividad de red.

Esta fase devuelve un único valor por hora para los siguientes campos:

  • Estadísticas de la IP de origen (cadena): $hourly_stats.src_ip

  • Estadísticas de la IP de destino (cadena): $hourly_stats.dst_ip

  • Estadísticas del recuento de eventos (número entero): $hourly_stats.count

  • Desviación estándar de los bytes recibidos (float): $hourly_stats.std_recd_bytes

  • Media de bytes recibidos (float): $hourly_stats.avg_recd_bytes

  • Hora de inicio del intervalo en segundos desde el inicio del registro de tiempo de Unix (entero): $hourly_stats.window_start

  • Hora de finalización del intervalo en segundos desde el inicio del registro de tiempo de Unix (entero): $hourly_stats.window_end

La fase raíz procesa el resultado de la fase hourly_stats. Calcula estadísticas de los pares principal.ip y target.ip cuya actividad supera el umbral especificado por $hourly_stats. A continuación, filtra las parejas con más de tres horas de actividad alta.


stage hourly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $src_ip = principal.ip
  $dst_ip = target.ip
  $src_ip != ""
  $dst_ip != ""

  match:
    $src_ip, $dst_ip by hour

  outcome:
    $count = count(metadata.id)
    $avg_recd_bytes = avg(network.received_bytes)
    $std_recd_bytes = stddev(network.received_bytes)

  condition:
    $avg_recd_bytes > 100 and $std_recd_bytes > 50
}

$src_ip = $hourly_stats.src_ip
$dst_ip = $hourly_stats.dst_ip
$time_bucket_count = strings.concat(timestamp.get_timestamp($hourly_stats.window_start), "|", $hourly_stats.count)

match:
 $src_ip, $dst_ip

outcome:
 $list = array_distinct($time_bucket_count)
 $count = count_distinct($hourly_stats.window_start)

condition:
 $count > 3

Si modifica la condición de coincidencia en la fase raíz de la siguiente manera, puede introducir una agregación por día en la consulta de varias fases.

match:
 $src_ip, $dst_ip by day

Ejemplo: Buscar conexiones de red inusualmente activas (con la puntuación Z)

Esta consulta de varias fases compara la actividad de red media diaria con la actividad de hoy mediante un cálculo de puntuación Z (que mide el número de desviaciones estándar con respecto a la media). Esta consulta busca de forma eficaz una actividad de red inusualmente alta entre recursos internos y sistemas externos.

Requisito previo: la ventana temporal de la consulta debe ser igual o superior a 2 días e incluir el día actual para que la puntuación Z calculada sea efectiva.

Esta consulta de varias fases incluye las fases daily_stats y root, que funcionan conjuntamente para calcular la puntuación Z de la actividad de la red:

  • La fase daily_stats realiza la agregación diaria inicial. Calcula el total de bytes intercambiados cada día por cada par de IPs (source y target) y devuelve los siguientes campos de la fase (que corresponden a las columnas de las filas de salida):

    • $daily_stats.source: singular, cadena
    • $daily_stats.target: singular, cadena
    • $daily_stats.exchanged_bytes: singular, entero
    • $daily_stats.window_start: singular, entero
    • $daily_stats.window_end: singular, entero
  • La fase raíz agrega la salida de la fase daily_stats de cada par de IPs. Calcula la media y la desviación estándar de los bytes diarios intercambiados en todo el intervalo de búsqueda, así como los bytes intercambiados hoy. Usa esos tres valores calculados para determinar la puntuación Z.

  • En la salida se muestran las puntuaciones Z de todos los pares de IPs de hoy, ordenadas de forma descendente.

// Calculate the total bytes exchanged per day by source and target

stage daily_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $source = principal.hostname
  $target = target.ip
  $source != ""
  $target != ""
  $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)
  match:
    $source, $target by day
  outcome:
    $exchanged_bytes = sum($total_bytes)
}

// Calculate the average per day over the time window and compare with the bytes
   exchanged today

$source = $daily_stats.source
$target = $daily_stats.target
$date = timestamp.get_date($daily_stats.window_start)

match:
  $source, $target

outcome:
  $today_bytes = sum(if($date = timestamp.get_date(timestamp.current_seconds()), $daily_stats.exchanged_bytes, 0))
  $average_bytes = window.avg($daily_stats.exchanged_bytes)
  $stddev_bytes = window.stddev($daily_stats.exchanged_bytes)
  $zscore = ($today_bytes - $average_bytes) / $stddev_bytes

order:
  $zscore desc

Exportar variables sin agregar de fases

Las fases con nombre pueden incluir una sección outcome sin agregar. Esto significa que las variables definidas en esa sección outcome se generan directamente desde la fase, lo que permite que las fases posteriores accedan a ellas como campos de fase sin necesidad de una agregación agrupada.

Ejemplo: Exportar una variable sin agregar

En este ejemplo se muestra cómo exportar variables sin agregar. Ten en cuenta la siguiente lógica:

  • top_5_bytes_sent busca los cinco eventos con la mayor actividad de red.

  • La fase top_5_bytes_sent genera los siguientes campos de fase correspondientes a las columnas de las filas de salida:

    • $top_5_bytes_sent.bytes_sent: singular, entero
    • $top_5_bytes_sent.timestamp_seconds: singular, entero
  • La fase root calcula las marcas de tiempo más recientes y más antiguas de los cinco eventos con la mayor actividad de red.

stage top_5_bytes_sent {
  metadata.event_type = "NETWORK_CONNECTION"
  network.sent_bytes > 0

  outcome:
    $bytes_sent = cast.as_int(network.sent_bytes)
    $timestamp_seconds = metadata.event_timestamp.seconds

  order:
    $bytes_sent desc 
  
  limit:
    5
}

outcome:
  $latest_timestamp = timestamp.get_timestamp(max($top_5_bytes_sent.timestamp_seconds))
  $earliest_timestamp = timestamp.get_timestamp(min($top_5_bytes_sent.timestamp_seconds))

Implementar ventanas en consultas de varias fases

Las consultas de varias fases admiten todos los tipos de ventanas (saltos, deslizantes y de acoplamiento) en fases con nombre. Si una fase con nombre incluye una ventana, se puede acceder al inicio y al final de la ventana de cada fila de salida mediante los siguientes campos reservados:

  • $<stage name>.window_start
  • $<stage name>.window_end

Ejemplo: ventana de salto

En el siguiente ejemplo se muestra cómo puedes usar ventanas de salto en una consulta de varias fases:

  • La fase hourly_stats busca pares de IPs que tengan una actividad de red alta en la misma hora.

  • hourly_stats genera los siguientes campos de fase correspondientes a las columnas de las filas de salida:

    • $hourly_stats.src_ip: singular, cadena
    • $hourly_stats.dst_ip: singular, cadena
    • $hourly_stats.count: singular, entero
    • $hourly_stats.std_recd_bytes: singular, float
    • $hourly_stats.avg_recd_bytes: singular, float
    • $hourly_stats.window_start: singular, entero
    • $hourly_stats.window_end: singular, entero
  • La fase raíz filtra los pares de IPs con más de 3 horas de actividad alta. Las horas podrían solaparse debido al uso de una ventana de salto en la fase hourly_stats.

stage hourly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $src_ip = principal.ip
  $dst_ip = target.ip
  $src_ip != ""
  $dst_ip != ""

  match:
    $src_ip, $dst_ip over 1h

  outcome:
    $count = count(metadata.id)
    $avg_recd_bytes = avg(network.received_bytes)
    $std_recd_bytes = stddev(network.received_bytes)

  condition:
    $avg_recd_bytes > 100 and $std_recd_bytes > 50
}

$src_ip = $hourly_stats.src_ip
$dst_ip = $hourly_stats.dst_ip
$time_bucket_count = strings.concat(timestamp.get_timestamp($hourly_stats.window_start), "|", $hourly_stats.count)

match:
 $src_ip, $dst_ip

outcome:
 $list = array_distinct($time_bucket_count)
 $count = count_distinct($hourly_stats.window_start)

condition:
 $count > 3

Problemas conocidos

Te recomendamos que revises las siguientes limitaciones y soluciones alternativas cuando implementes consultas multietapa:

  • Todas las consultas de varias fases se comportan como las consultas de búsqueda de estadísticas (la salida consta de estadísticas agregadas en lugar de eventos sin agregar o filas de tablas de datos).

  • El rendimiento de las uniones con eventos de UDM y de entidad en un lado puede ser bajo debido al tamaño de ese conjunto de datos. Recomendamos encarecidamente filtrar los eventos de UDM y de entidad del lado de la unión tanto como sea posible (por ejemplo, filtrar por tipo de evento).

Para obtener directrices generales sobre las prácticas recomendadas, consulta las prácticas recomendadas de Yara-L. Para obtener información específica sobre las combinaciones, consulta las prácticas recomendadas.

¿Necesitas más ayuda? Recibe respuestas de los miembros de la comunidad y de los profesionales de Google SecOps.