YARA-L에서 다단계 쿼리 만들기

다음에서 지원:

이 문서에서는 YARA-L의 다단계 쿼리를 사용하여 한 쿼리 단계의 출력을 후속 단계의 입력에 직접 제공하는 방법을 설명합니다. 이 프로세스를 사용하면 단일 모놀리식 쿼리보다 데이터 변환을 더 효과적으로 제어할 수 있습니다.

다단계 쿼리를 기존 기능과 통합

다단계 쿼리는 Google Security Operations의 다음 기존 기능과 함께 작동합니다.

  • 복합 감지 규칙: 다단계 쿼리는 복합 감지 규칙을 보완합니다. 복합 규칙과 달리 검색을 사용하는 다단계 쿼리는 결과를 실시간으로 반환할 수 있습니다.

  • 기간 및 멀티 이벤트 규칙: 다단계 쿼리를 사용하여 데이터 내의 여러 기간을 비교하여 이상치를 감지할 수 있습니다. 예를 들어 초기 쿼리 단계를 사용하여 장기간에 걸쳐 기준을 설정한 다음 후반 단계를 사용하여 해당 기준에 대한 최근 활동을 평가할 수 있습니다. 다중 이벤트 규칙을 사용하여 유사한 유형의 비교를 만들 수도 있습니다.

YARA-L의 다단계 쿼리는 대시보드검색 모두에서 지원됩니다.

조인을 사용하면 여러 소스의 데이터를 상호 연관시켜 조사에 더 많은 컨텍스트를 제공할 수 있습니다. 관련 이벤트, 엔티티, 기타 데이터를 연결하여 복잡한 공격 시나리오를 조사할 수 있습니다. 자세한 내용은 검색에서 조인 사용을 참고하세요.

다단계 YARA-L 구문 정의

다단계 쿼리를 구성할 때는 다음 사항에 유의하세요.

  • 제한 단계: 다단계 쿼리에는 루트 단계 외에 명명된 단계가 1~4개 포함되어야 합니다.
  • 순서 구문: 항상 루트 단계 구문을 정의하기 전에 이름이 지정된 단계 구문을 정의합니다.

다단계 YARA-L 쿼리 만들기

다단계 YARA-L 쿼리를 만들려면 다음 단계를 완료하세요.

단계 구조 및 문법

조사 > 검색으로 이동합니다. 쿼리 단계를 정의할 때는 다음 구조적 요구사항을 따르세요.

구문: 각 단계를 명명하고 다른 단계와 구분하려면 다음 구문을 사용합니다.

stage <stage name> { }

  • 중괄호: 모든 단계 구문을 중괄호 {} 안에 배치합니다.

  • 순서: 루트 단계를 정의하기 전에 이름이 지정된 모든 단계의 구문을 정의합니다.

  • 참조: 각 단계는 쿼리에서 이전에 정의된 단계를 참조할 수 있습니다.

  • 루트 단계: 쿼리에는 모든 명명된 단계 후에 처리되는 루트 단계가 있어야 합니다.

다음 예시 단계 daily_stats는 일일 네트워크 통계를 수집합니다.

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)
}

액세스 단계 출력

명명된 단계의 출력은 단계 필드를 사용하여 후속 단계에서 액세스할 수 있습니다. 스테이지 필드는 스테이지의 matchoutcome 변수에 해당하며 통합 데이터 모델 (UDM) 필드와 유사하게 사용할 수 있습니다.

다음 구문을 사용하여 단계 필드에 액세스합니다.

$<stage name>.<variable name>

액세스 기간 타임스탬프 (선택사항)

명명된 스테이지에서 홉, 슬라이딩 또는 텀블링 윈도우를 사용하는 경우 다음 예약된 필드를 사용하여 각 출력 행의 윈도우 시작 및 윈도우 끝에 액세스합니다.

  • $<stage name>.window_start

  • $<stage name>.window_end

window_startwindow_end은 Unix 에포크 이후 초 단위로 표현된 정수 필드입니다. 단계별 창의 크기는 다를 수 있습니다.

제한사항

다단계 쿼리에는 다음과 같은 기능 및 구조적 제약이 있습니다.

구조 및 단계 제한

  • 루트 단계: 쿼리당 하나의 루트 단계만 허용됩니다.

  • 이름이 지정된 단계: 이름이 지정된 단계는 최대 4개까지 지원됩니다.

  • 참조 단계: 단계는 동일한 쿼리에서 논리적으로 앞에 정의된 단계만 참조할 수 있습니다.

  • 조인: 모든 단계에서 데이터 테이블이 아닌 조인은 최대 4개까지 허용됩니다.

  • 결과 요구사항: 이름이 지정된 각 단계 (루트 단계 제외)에는 match 섹션 또는 outcome 섹션이 포함되어야 합니다. outcome 섹션에는 집계가 필요하지 않습니다.

창 및 호환성 한도

  • 기능 지원: 다단계 쿼리는 검색대시보드에서 지원되지만 규칙에서는 지원되지 않습니다.

  • 기간 유형: 단일 쿼리 내에서 다양한 기간 유형을 혼합하지 마세요.

  • 창 종속 항목: 홉 또는 슬라이딩 창을 사용하는 스테이지는 홉 또는 슬라이딩 창을 사용하는 다른 스테이지에 종속될 수 없습니다.

  • 텀블링 창 크기: 다양한 단계에서 텀블링 창의 크기는 다를 수 있지만 크기 차이는 720x 미만이어야 합니다.

예: 단계 집계 차이

다음 예시 창 구성은 허용되지 않습니다.

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

monthly_stats 단계에서 월별로 데이터를 집계하고 루트 단계에서 monthly_stats의 출력을 분별로 집계하는 경우 monthly_stats의 각 행은 루트 단계의 43,200개 행에 매핑됩니다 (한 달에 43,200분이 있기 때문).

단계 및 쿼리 제한사항

다단계 쿼리 내의 각 개별 단계에는 다음과 같은 제약 조건이 있습니다.

  • 단일 스테이지 쿼리에 적용되는 대부분의 제한사항은 각 개별 스테이지에도 적용됩니다.

    • 출력 요구사항: 모든 단계는 하나 이상의 경기 또는 결과 변수 (단계 필드)를 출력해야 합니다.

    • 조인의 기간: 조인에 사용되는 최대 기간 크기 (홉, 텀블링 또는 슬라이딩)는 2일입니다.

    • 결과 변수의 최대 수:

      • 결과 변수 한도를 늘리도록 선택하지 않은 고객의 경우 20

      • 더 큰 결과 변수 한도를 허용하도록 선택한 고객의 경우 50

    • 홉 창의 최소 및 최대 크기

    • 배열 값 결과 변수의 최대 요소 수

  • 다단계 쿼리에는 통계 쿼리와 동일한 제한사항이 적용됩니다.

    • 통계 쿼리: 120QPH (API 및 UI)

    • Google SecOps에서 뷰 검색: 분당 100개 뷰

    • 다단계 조인은 사용자 인터페이스와 EventService.UDMSearch API에서 지원되지만 SearchService.UDMSearch API에서는 지원되지 않습니다. 조인이 없는 다단계 쿼리도 사용자 인터페이스에서 지원됩니다.

이벤트 및 전역 제한사항

최대 이벤트 수:

다단계 쿼리는 동시에 처리할 수 있는 이벤트 수가 엄격하게 제한됩니다.

  • UDM 이벤트: 최대 2개의 UDM 이벤트가 허용됩니다.

  • 엔티티 컨텍스트 그래프 (ECG) 이벤트: ECG 이벤트는 최대 1개까지 허용됩니다.

전역 쿼리 제한사항:

이러한 제한은 다단계 쿼리가 얼마나 이전의 데이터를 얼마나 많이 반환할 수 있는지를 제어하는 플랫폼 전체 제약 조건입니다.

  • 쿼리 기간의 경우 표준 쿼리의 최대 기간은 30일입니다.

  • 최대 총 결과 집합 크기는 10,000개입니다.

다단계 쿼리 예시

이 섹션의 예는 완전한 다단계 YARA-L 쿼리를 만드는 방법을 보여줍니다.

예: 비정상적으로 활성 상태인 네트워크 연결 검색 (시간)

이 다단계 YARA-L 예시에서는 3시간 이상 높은 활동을 유지하는 쌍을 타겟팅하여 정상보다 높은 네트워크 활동이 있는 IP 주소 쌍을 식별합니다. 쿼리에는 이름이 지정된 단계(hourly_stats)와 root 단계라는 두 가지 필수 구성요소가 포함됩니다.

hourly_stats 단계에서는 네트워크 활동 수준이 높은 principal.iptarget.ip 쌍을 검색합니다.

이 단계에서는 다음 필드에 대해 시간별 단일 값을 반환합니다.

  • 소스 IP 통계 (문자열): $hourly_stats.src_ip

  • 대상 IP 통계 (문자열): $hourly_stats.dst_ip

  • 이벤트 수 (정수) 통계: $hourly_stats.count

  • 표준 편차 수신 바이트 (부동 소수점): $hourly_stats.std_recd_bytes

  • 평균 수신 바이트 (부동 소수점): $hourly_stats.avg_recd_bytes

  • 시간 버킷 시작 시간 (초 단위, 유닉스 시간 기준, 정수): $hourly_stats.window_start

  • 시간 버킷 종료 시간 (초 단위, 유닉스 시간 기준, 정수): $hourly_stats.window_end

루트 단계는 hourly_stats 단계의 출력을 처리합니다. $hourly_stats에 지정된 기준점을 초과하는 활동이 있는 principal.iptarget.ip 쌍의 통계를 계산합니다. 그런 다음 활동이 많은 시간이 3시간을 초과하는 쌍을 필터링합니다.


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

다음과 같이 루트 단계에서 일치 조건을 변경하면 다단계 쿼리에 대해 일별로 창 집계를 도입할 수 있습니다.

match:
 $src_ip, $dst_ip by day

예: 비정상적으로 활성 상태인 네트워크 연결 검색 (Z 점수 사용)

이 다단계 쿼리는 Z 점수 계산 (평균에서 벗어난 표준 편차 수 측정)을 사용하여 일일 평균 네트워크 활동을 오늘의 활동과 비교합니다. 이 쿼리는 내부 애셋과 외부 시스템 간의 비정상적으로 높은 네트워크 활동을 효과적으로 검색합니다.

필수사항: 계산된 Z 점수가 유효하려면 쿼리 기간이 2일 이상이어야 하며 당일이 포함되어야 합니다.

이 다단계 쿼리에는 daily_stats 단계와 root 단계가 포함되어 있으며, 이 두 단계는 함께 작동하여 네트워크 활동의 Z 점수를 계산합니다.

  • daily_stats 단계는 초기 일일 집계를 실행합니다. 각 IP 쌍 (sourcetarget)에 대해 매일 교환된 총 바이트를 계산하고 다음 단계 필드 (출력 행의 열에 해당)를 반환합니다.

    • $daily_stats.source: 단수, 문자열
    • $daily_stats.target: 단수, 문자열
    • $daily_stats.exchanged_bytes: 단수, 정수
    • $daily_stats.window_start: 단수, 정수
    • $daily_stats.window_end: 단수, 정수
  • 루트 단계에서는 각 IP 쌍의 daily_stats 단계 출력을 집계합니다. 전체 검색 범위에서 교환된 일일 바이트의 평균 및 표준 편차와 오늘 교환된 바이트를 계산합니다. 이러한 세 가지 계산된 값을 사용하여 Z-점수를 결정합니다.

  • 출력에는 오늘 모든 IP 쌍의 Z 점수가 내림차순으로 정렬되어 나열됩니다.

// 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

단계에서 집계되지 않은 변수 내보내기

이름이 지정된 단계에는 집계되지 않은 outcome 섹션이 포함될 수 있습니다. 즉, 해당 outcome 섹션 내에 정의된 변수는 단계에서 직접 출력되므로 후속 단계에서 그룹화된 집계 없이 단계 필드로 액세스할 수 있습니다.

예: 집계되지 않은 변수 내보내기

이 예에서는 집계되지 않은 변수를 내보내는 방법을 보여줍니다. 다음 로직을 참고하세요.

  • top_5_bytes_sent 단계에서는 네트워크 활동이 가장 많은 5개의 이벤트를 검색합니다.

  • top_5_bytes_sent 단계는 출력 행의 열에 해당하는 다음 단계 필드를 출력합니다.

    • $top_5_bytes_sent.bytes_sent: 단수, 정수
    • $top_5_bytes_sent.timestamp_seconds: 단수, 정수
  • root 단계에서는 네트워크 활동이 가장 많은 5개 이벤트의 최신 타임스탬프와 가장 이른 타임스탬프를 계산합니다.

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))

다단계 쿼리에서 윈도우화 구현

다단계 쿼리는 명명된 단계에서 모든 유형의 윈도우 (홉, 슬라이딩, 텀블링)를 지원합니다. 명명된 스테이지에 윈도우가 포함된 경우 각 출력 행의 윈도우 시작 및 윈도우 종료는 다음 예약된 필드를 사용하여 액세스할 수 있습니다.

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

예: 홉 윈도우

다음 예는 다단계 쿼리에서 홉 윈도우를 사용하는 방법을 보여줍니다.

  • hourly_stats 단계에서는 동일한 시간 내에 네트워크 활동이 많은 IP 쌍을 검색합니다.

  • hourly_stats는 출력 행의 열에 해당하는 다음 단계 필드를 출력합니다.

    • $hourly_stats.src_ip: 단수, 문자열
    • $hourly_stats.dst_ip: 단수, 문자열
    • $hourly_stats.count: 단수, 정수
    • $hourly_stats.std_recd_bytes: 단수, 부동 소수점
    • $hourly_stats.avg_recd_bytes: 단수, 부동 소수점
    • $hourly_stats.window_start: 단수, 정수
    • $hourly_stats.window_end: 단수, 정수
  • 루트 단계에서는 활동이 많은 시간이 3시간을 초과하는 IP 쌍을 필터링합니다. 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

다단계 쿼리의 조인

내부 조인은 다단계 쿼리의 단계 내 및 단계 간에 지원됩니다. 내부 조인 기능은 다음 유형을 지원합니다.

  • UDM 및 UDM
  • UDM 및 심전도
  • UDM 및 DataTable

조인 컨텍스트에서 윈도우 처리된 단계는 윈도우가 포함된 일치 섹션이 있는 단계를 의미합니다. 반면 테이블 스테이지는 창을 출력하지 않습니다.

다음 예에서는 다단계 쿼리에서 UDM 이벤트와 테이블 단계 간에 일치하지 않는 조인을 구성하는 방법을 보여줍니다.

  • median 단계에서는 각 소스 호스트와 타겟 IP 쌍에 대해 전송된 바이트의 중앙값을 계산합니다.
  • median 단계에서는 출력 행의 열에 해당하는 다음 단계 필드를 출력합니다.
    • $median.host: 단수, 문자열
    • $median.target: 단수, 문자열
    • $median.median: 단수, 부동 소수점
  • absolute_deviations 단계는 각 UDM 이벤트를 동일한 소스 호스트 및 타겟 IP 쌍의 median 행과 조인합니다. 각 UDM 이벤트에 대해 전송된 바이트의 절댓값을 계산합니다.
  • absolute_deviations는 출력 행의 열에 해당하는 다음 단계 필드를 출력합니다.
    • $absolute_deviations.host: 단수, 문자열
    • $absolute_deviations.target: 단수, 문자열
    • $absolute_deviations.absolute_deviation: 단수, 부동 소수점
  • 루트 단계에서는 모든 UDM 이벤트에서 전송된 바이트의 절대 편차 평균을 계산합니다.
stage median {
  metadata.event_type = "NETWORK_CONNECTION"
  $host = principal.hostname
  $target = target.ip

  match:
    $host, $target

  outcome:
    $median = window.median(network.sent_bytes, true)
}

stage absolute_deviations {
  metadata.event_type = "NETWORK_CONNECTION"
  $join_host = principal.hostname
  $join_host = $median.host
  $join_target = target.ip[0]
  $join_target = $median.target

  outcome:
    $host = $join_host
    $target = $join_target
    $absolute_deviation = math.abs(network.sent_bytes - $median.median)
}

$host = $absolute_deviations.host
$target = $absolute_deviations.target

match:
  $host, $target

outcome:
  $mean_absolute_deviation = avg($absolute_deviations.absolute_deviation)

예: 윈도우 처리된 스테이지와 테이블 스테이지 간의 일치하지 않는 조인

다음 예시에서는 다단계 쿼리에서 윈도우 단계와 테이블 단계 간에 일치하지 않는 조인을 구성하는 방법을 보여줍니다.

  • hourly_stats 단계에서는 각 소스 및 타겟 호스트 쌍과 시간 버킷에 대해 전송된 총 바이트를 계산합니다.
  • hourly_stats 단계에서는 출력 행의 열에 해당하는 다음 단계 필드를 출력합니다.
    • $hourly_stats.source_host: 단수, 문자열
    • $hourly_stats.dst_host: 단수, 문자열
    • $hourly_stats.total_bytes_sent: 단수, 부동 소수점
    • $hourly_stats.window_start: 단수, 정수
    • $hourly_stats.window_end: 단수, 정수
  • agg_stats 단계에서는 각 소스 및 타겟 호스트 쌍의 시간당 바이트의 평균과 표준 편차를 계산합니다.
  • agg_stats는 출력 행의 열에 해당하는 다음 단계 필드를 출력합니다.

    • $agg_stats.source_host: 단수, 문자열
    • $agg_stats.dst_host: 단수, 문자열
    • $agg_stats.avg_bytes_sent: 단수, 부동 소수점
    • $agg_stats.stddev_bytes_sent: 단수, 부동 소수점
  • 루트 단계에서는 hourly_stats의 각 행을 동일한 소스 및 타겟 호스트 쌍의 agg_stats 행과 조인합니다. 각 소스 및 타겟 호스트 쌍에 대해 해당 호스트 쌍 버킷의 총 전송 바이트와 집계 통계를 사용하여 z-점수를 계산합니다.

stage hourly_stats {
 $source_host = principal.hostname
 $dst_host = target.hostname
 principal.hostname != ""
 target.hostname != ""
 match:
   $source_host, $dst_host by hour
 outcome:
   $total_bytes_sent = sum(network.sent_bytes)
}

stage agg_stats {
  $source_host = $hourly_stats.source_host
  $dst_host = $hourly_stats.dst_host
  match:
    $source_host, $dst_host
  outcome:
   $avg_bytes_sent = avg($hourly_stats.total_bytes_sent)
   $stddev_bytes_sent = stddev($hourly_stats.total_bytes_sent)
}

$source_host = $agg_stats.source_host
$source_host = $hourly_stats.source_host

$dst_host = $agg_stats.dst_host
$dst_host = $hourly_stats.dst_host

outcome:
  $hour_bucket = timestamp.get_timestamp($hourly_stats.window_start)
  $z_score = ($hourly_stats.total_bytes_sent - $agg_stats.avg_bytes_sent)/$agg_stats.stddev_bytes_sent

알려진 문제

다단계 쿼리를 구현할 때는 다음 제한사항과 권장되는 해결 방법을 검토하는 것이 좋습니다.

  • 모든 다단계 쿼리는 통계 검색 쿼리와 같이 작동합니다 (출력은 집계되지 않은 이벤트 또는 데이터 테이블 행이 아닌 집계된 통계로 구성됨).

  • 한쪽의 UDM 및 엔티티 이벤트와의 조인 성능은 해당 데이터 세트의 크기로 인해 성능이 저하될 수 있습니다. 조인의 UDM 및 항목 이벤트를 최대한 많이 필터링하는 것이 좋습니다 (예: 이벤트 유형으로 필터링).

권장사항에 관한 일반적인 안내는 Yara-L 권장사항을 참고하고, 조인 관련 정보는 권장사항을 참고하세요.

도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가에게 문의하여 답변을 받으세요.