YARA-L 2.0 언어 개요

YARA-L 2.0은 기업 로그 데이터가 Google Security Operations 인스턴스에 수집될 때 이 데이터를 검색할 수 있는 규칙을 만들기 위해 사용되는 컴퓨터 언어입니다. YARA-L 구문은 VirusTotal에서 개발된 YARA 언어로부터 파생됩니다. 이 언어는 Google Security Operations Detection Engine과 함께 작동하며 대량의 데이터에 대해 위협 요소 및 기타 이벤트를 찾을 수 있게 해줍니다.

자세한 내용은 다음을 참조하세요.

YARA-L 2.0 예시 규칙

다음 예시는 YARA-L 2.0으로 작성된 규칙을 보여줍니다. 각 항목은 규칙 언어 내에서 이벤트의 상관관계를 지정하는 방법을 보여줍니다.

규칙 및 조정

다음 규칙은 이벤트 데이터의 특정 패턴을 확인하고 패턴이 발견되면 감지를 만듭니다. 이 규칙에는 이벤트 유형과 metadata.event_type UDM 필드를 추적할 수 있도록 $e1 변수가 포함됩니다. 이 규칙은 e1과 일치하는 정규 표현식 특정 일치 항목을 확인합니다. $e1 이벤트가 발생하면 감지가 생성됩니다. not 조건은 악성이 아닌 특정 경로를 제외하는 규칙에 포함되어 있습니다. not 조건을 추가하여 거짓양성을 방지할 수 있습니다.

rule suspicious_unusual_location_svchost_execution
{

 meta:
   author = "Google Cloud Security"
   description = "Windows 'svchost' executed from an unusual location"
   yara_version = "YL2.0"
   rule_version = "1.0"

 events:

   $e1.metadata.event_type = "PROCESS_LAUNCH"
   re.regex($e1.principal.process.command_line, `\bsvchost(\.exe)?\b`) nocase
   not re.regex($e1.principal.process.command_line, `\\Windows\\System32\\`) nocase

condition:

   $e1
}

다른 도시의 로그인

다음 규칙은 5분 이내에 두 개 이상의 도시에서 회사에 로그인한 사용자를 검색합니다.

rule DifferentCityLogin {
  meta:

  events:
    $udm.metadata.event_type = "USER_LOGIN"
    $udm.principal.user.userid = $user
    $udm.principal.location.city = $city

  match:
    $user over 5m

  condition:
    $udm and #city > 1
}

일치 변수: $user

이벤트 변수:$udm

자리표시자 변수: $city$user

다음은 이 규칙의 작동 방법을 설명합니다.

  • 이벤트를 사용자 이름($user)과 그룹으로 묶고 일치가 발견되면 이를 반환($user)합니다.
  • 기간은 5분입니다. 즉, 서로 5분 이내에 발생하는 이벤트만 상관관계가 지정됩니다.
  • 해당 이벤트 유형이 USER_LOGIN인 이벤트 그룹($udm)을 검색합니다.
  • 이 이벤트 그룹에 대해 이 규칙은 사용자 ID를 $user로 호출하고 로그인 도시를 $city.로 호출합니다.
  • city 값(#city로 표시됨)의 고유 숫자가 이벤트 그룹($udm)에서 5분 기간 내에 1보다 크면 일치를 반환합니다.

빠른 사용자 만들기 및 삭제

다음 규칙은 생성되고 4시간 내에 삭제된 사용자를 검색합니다.

rule UserCreationThenDeletion {
  meta:

  events:
    $create.target.user.userid = $user
    $create.metadata.event_type = "USER_CREATION"

    $delete.target.user.userid = $user
    $delete.metadata.event_type = "USER_DELETION"

    $create.metadata.event_timestamp.seconds <=
       $delete.metadata.event_timestamp.seconds

  match:
    $user over 4h

  condition:
    $create and $delete
}

이벤트 변수: $create$delete

일치 변수: $user

자리표시자 변수: 해당 없음

다음은 이 규칙의 작동 방법을 설명합니다.

  • 이벤트를 사용자 이름($user)과 그룹으로 묶고 일치가 발견되면 이를 반환($user)합니다.
  • 기간은 4시간이며, 간격이 4시간 미만인 이벤트만 상관관계가 지정됩니다.
  • $create#create >= 1과 동일한 2개의 이벤트 그룹($create$delete)을 검색합니다.
  • $createUSER_CREATION 이벤트에 해당하며 사용자 ID를 $user로 호출합니다.
  • $user는 2개의 이벤트 그룹을 하나로 조인하기 위해 사용됩니다.
  • $deleteUSER_DELETION 이벤트에 해당하며 사용자 ID를 $user로 호출합니다. 이 규칙은 두 이벤트 그룹의 사용자 식별자가 동일한 일치를 찾습니다.
  • 이 규칙은 $delete의 이벤트가 $create의 이벤트보다 늦게 발생하는 경우를 찾고, 발견되면 일치를 반환합니다.

단일 이벤트 규칙

단일 이벤트 규칙은 단일 이벤트에 대한 상관관계가 있는 규칙입니다. 단일 이벤트 규칙은 다음과 같을 수 있습니다.

  • 일치 섹션이 없는 모든 규칙
  • 1개 이벤트 존재만 확인하는 match 섹션 및 condition 섹션의 규칙(예: "$e", "#e > 0", "#e >= 1", "1 <= #e", "0 < #e")

예를 들어 다음 규칙은 사용자 로그인 이벤트를 검색하고 Google Security Operations 계정 내에 저장된 기업 데이터 내에서 발생하는 첫 번째 항목을 반환합니다.

rule SingleEventRule {
  meta:
    author = "noone@altostrat.com"

  events:
    $e.metadata.event_type = "USER_LOGIN"

  condition:
    $e
}

다음은 일치 섹션이 있는 단일 이벤트 규칙의 예시입니다. 이 규칙은 5분 이내에 한 번 이상 로그인한 사용자를 검색합니다. 사용자 로그인 이벤트의 단순한 존재를 확인합니다.

rule SingleEventRule {
  meta:
    author = "alice@example.com"
    description = "windowed single event example rule"

  events:
    $e.metadata.event_type = "USER_LOGIN"
    $e.principal.user.userid = $user

  match:
    $user over 5m

  condition:
    #e > 0
}
rule MultiEventRule{
  meta:
    author = "alice@example.com"
    description = "Rule with outcome condition and simple existence condition on one event variable"

  events:
    $e.metadata.event_type = "USER_LOGIN"
    $e.principal.user.userid = $user

  match:
    $user over 10m

  outcome:
    $num_events_in_match_window = count($e.metadata.id)

  condition:
    #e > 0 and $num_events_in_match_window >= 10 // Could be rewritten as #e >= 10
}

여러 이벤트 규칙

여러 이벤트 규칙을 사용하여 지정된 기간 동안 많은 이벤트를 그룹화하고 이벤트 간 상관관계를 찾습니다. 일반적인 여러 이벤트 규칙에는 다음이 포함됩니다.

  • 이벤트를 그룹화해야 하는 시간 범위를 지정하는 match 섹션
  • 발견 항목을 트리거하고 여러 이벤트의 존재 여부를 확인하는 조건을 지정하는 condition 섹션

예를 들어 다음 규칙은 10분 이내에 최소 10번 이상 로그인한 사용자를 검색합니다.

rule MultiEventRule {
  meta:
    author = "noone@altostrat.com"

  events:
    $e.metadata.event_type = "USER_LOGIN"
    $e.principal.user.userid = $user

  match:
    $user over 10m

  condition:
    #e >= 10
}

IP 주소 범위 내의 단일 이벤트

다음 예시는 2명의 특정 사용자와 특정 IP 주소 범위 사이의 일치를 검색하는 단일 이벤트 규칙을 보여줍니다.

rule OrsAndNetworkRange {
  meta:
    author = "noone@altostrat.com"

  events:
    // Checks CIDR ranges.
    net.ip_in_range_cidr($e.principal.ip, "203.0.113.0/24")

    // Detection when the hostname field matches either value using or.
    $e.principal.hostname = /pbateman/ or $e.principal.hostname = /sspade/

  condition:
    $e
}

모든 규칙 예시

다음 규칙은 모든 소스 IP 주소가 5분 내에 안전한 것으로 알려진 IP 주소와 일치하지 않는 로그인 이벤트를 검색합니다.

rule SuspiciousIPLogins {
  meta:
    author = "alice@example.com"

  events:
    $e.metadata.event_type = "USER_LOGIN"

    // Detects if all source IP addresses in an event do not match "100.97.16.0"
    // For example, if an event has source IP addresses
    // ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
    // it will be detected since "100.97.16.1", "100.97.16.2",
    // and "100.97.16.3" all do not match "100.97.16.0".

    all $e.principal.ip != "100.97.16.0"

    // Assigns placeholder variable $ip to the $e.principal.ip repeated field.
    // There will be one detection per source IP address.
    // For example, if an event has source IP addresses
    // ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
    // there will be one detection per address.

    $e.principal.ip = $ip

  match:
    $ip over 5m

  condition:
    $e
}

규칙의 정규 표현식

다음 YARA-L 2.0 정규 표현식 예시는 altostrat.com 도메인에서 수신된 이메일이 있는 이벤트를 검색합니다. nocase$host 변수 regex 비교 및 regex 함수에 추가되었기 때문에 이 비교는 대소문자를 구분하지 않습니다.

rule RegexRuleExample {
  meta:
    author = "noone@altostrat.com"

  events:
    $e.principal.hostname = $host
    $host = /.*HoSt.*/ nocase
    re.regex($e.network.email.from, `.*altostrat\.com`) nocase

  match:
    $host over 10m

  condition:
    #e > 10
}

슬라이딩 기간 규칙 예시

다음 YARA-L 2.0 슬라이딩 창 예시는 firewall_1 이벤트 후 firewall_2 이벤트의 부재를 검색합니다. after 키워드는 피벗 이벤트 변수 $e1과 함께 사용되어 이벤트의 상관관계를 지정할 때 10분 후에만 각 firewall_1 이벤트를 검사하도록 지정합니다.

rule SlidingWindowRuleExample {
  meta:
    author = "alice@example.com"

  events:
    $e1.metadata.product_name = "firewall_1"
    $e1.principal.hostname = $host

    $e2.metadata.product_name = "firewall_2"
    $e2.principal.hostname = $host

  match:
    $host over 10m after $e1

  condition:
    $e1 and !$e2
}

0 값 제외 예시

규칙 엔진은 match 섹션에 사용된 모든 자리 표시자에 대해 0 값을 암시적으로 필터링합니다. 자세한 내용은 match 섹션의 0 값 처리를 참조하세요. 이는 allow_zero_values에 설명된 대로 allow_zero_values 옵션을 사용하여 사용 중지할 수 있습니다.

하지만 다른 참조된 이벤트 필드의 경우 명시적으로 조건을 지정하지 않으면 0 값이 제외되지 않습니다.

rule ExcludeZeroValues {
  meta:
    author = "alice@example.com"

  events:
    $e1.metadata.event_type = "NETWORK_DNS"
    $e1.principal.hostname = $hostname

    // $e1.principal.user.userid may be empty string.
    $e1.principal.user.userid != "Guest"

    $e2.metadata.event_type = "NETWORK_HTTP"
    $e2.principal.hostname = $hostname

    // $e2.target.asset_id cannot be empty string as explicitly specified.
    $e2.target.asset_id != ""

  match:
    // $hostname cannot be empty string. The rule behaves as if the
    // predicate, `$hostname != ""` was added to the events section, because
    // `$hostname` is used in the match section.
    $hostname over 1h

  condition:
    $e1 and $e2
}

outcome 섹션이 있는 규칙 예시

YARA-L 2.0 규칙에 선택적인 outcome 섹션을 추가하여 각 발견 항목에 대한 추가 정보를 추출할 수 있습니다. 조건 섹션에서 결과 변수에 조건부를 지정할 수도 있습니다. 감지 규칙의 outcome 섹션을 사용해서 다운스트림 소비에 대한 변수를 설정할 수 있습니다. 예를 들어 분석 중인 이벤트의 데이터를 기반으로 심각도 점수를 설정할 수 있습니다.

자세한 내용은 다음을 참조하세요.

결과 섹션이 있는 멀티 이벤트 규칙:

다음 규칙에서는 두 가지 이벤트를 확인하여 $hostname 값을 가져옵니다. $hostname 값이 5분 이상 일치하면 심각도 점수가 적용됩니다. match 섹션에 기간을 포함하면 규칙이 지정된 기간 내에 확인합니다.

rule OutcomeRuleMultiEvent {
    meta:
      author = "Google Cloud Security"
    events:
      $u.udm.principal.hostname = $hostname
      $asset_context.graph.entity.hostname = $hostname

      $severity = $asset_context.graph.entity.asset.vulnerabilities.severity

    match:
      $hostname over 5m

    outcome:
      $risk_score =
        max(
            100
          + if($hostname = "my-hostname", 100, 50)
          + if($severity = "HIGH", 10)
          + if($severity = "MEDIUM", 5)
          + if($severity = "LOW", 1)
        )

      $asset_id_list =
        array(
          if($u.principal.asset_id = "",
             "Empty asset id",
             $u.principal.asset_id
          )
        )

      $asset_id_distinct_list = array_distinct($u.principal.asset_id)

      $asset_id_count = count($u.principal.asset_id)

      $asset_id_distinct_count = count_distinct($u.principal.asset_id)

    condition:
      $u and $asset_context and $risk_score > 50 and not arrays.contains($asset_id_list, "id_1234")
}

rule OutcomeRuleMultiEvent {
    meta:
      author = "alice@example.com"
    events:
      $u.udm.principal.hostname = $hostname
      $asset_context.graph.entity.hostname = $hostname

      $severity = $asset_context.graph.entity.asset.vulnerabilities.severity

    match:
      $hostname over 5m

    outcome:
      $total_network_bytes = sum($u.network.sent_bytes) + sum($u.network.received_bytes)

      $risk_score = if(total_network_bytes > 1024, 100, 50) +
        max(
          if($severity = "HIGH", 10)
          + if($severity = "MEDIUM", 5)
          + if($severity = "LOW", 1)
        )

      $asset_id_list =
        array(
          if($u.principal.asset_id = "",
             "Empty asset id",
             $u.principal.asset_id
          )
        )

      $asset_id_distinct_list = array_distinct($u.principal.asset_id)

      $asset_id_count = count($u.principal.asset_id)

      $asset_id_distinct_count = count_distinct($u.principal.asset_id)

    condition:
      $u and $asset_context and $risk_score > 50 and not arrays.contains($asset_id_list, "id_1234")
}

결과 섹션이 있는 단일 이벤트 규칙:

rule OutcomeRuleSingleEvent {
    meta:
        author = "alice@example.com"
    events:
        $u.metadata.event_type = "FILE_COPY"
        $u.principal.file.size = $file_size
        $u.principal.hostname = $hostname

    outcome:
        $suspicious_host = $hostname
        $admin_severity = if($u.principal.userid in %admin_users, "SEVERE", "MODERATE")
        $severity_tag = if($file_size > 1024, $admin_severity, "LOW")

    condition:
        $u
}

멀티 이벤트 결과 규칙을 단일 이벤트 결과 규칙으로 리팩터링

단일 이벤트 규칙(match 섹션이 없는 규칙) 및 멀티 이벤트 규칙(match 섹션이 있는 규칙) 모두 outcome 섹션을 사용할 수 있습니다. 이전에 결과 섹션을 사용할 수 있도록 규칙을 다중 이벤트로 설계한 경우, match 섹션을 삭제하여 해당 규칙을 선택적으로 리팩터링하면 성능을 개선할 수 있습니다. 규칙에 그룹화를 적용하는 match 섹션이 더 이상 없으므로 더 많은 감지가 수신될 수 있습니다. 이 리팩터링은 다음 예시에 표시된 것처럼 1개 이벤트 변수를 사용하는 규칙에만 가능합니다.

하나의 이벤트 변수만 사용하는 멀티 이벤트 결과 규칙(리팩터링에 적합함):

rule OutcomeMultiEventPreRefactor {
    meta:
      author = "alice@example.com"
      description = "Outcome refactor rule, before the refactor"

    events:
      $u.udm.principal.hostname = $hostname

    match:
      $hostname over 5m

    outcome:
      $risk_score = max(if($hostname = "my-hostname", 100, 50))

    condition:
      $u
}

match 섹션을 삭제하여 규칙을 리팩터링할 수 있습니다. 이제 규칙이 단일 이벤트가 되므로 outcome 섹션에서 집계도 삭제해야 합니다. 집계에 대한 자세한 내용은 결과 집계를 참조하세요.

rule OutcomeSingleEventPostRefactor {
    meta:
      author = "alice@example.com"
      description = "Outcome refactor rule, after the refactor"

    events:
      $u.udm.principal.hostname = $hostname

    // We deleted the match section.

    outcome:
      // We removed the max() aggregate.
      $risk_score = if($hostname = "my-hostname", 100, 50)

    condition:
      $u
}

자리 표시자 규칙 예시 함수

함수 호출의 결과에 자리 표시자 변수를 할당하고 match 섹션, outcome 섹션, condition 섹션과 같은 규칙의 다른 섹션에 자리 표시자 변수를 사용할 수 있습니다. 아래 예시를 참조하세요.

rule FunctionToPlaceholderRule {
    meta:
      author = "alice@example.com"
      description = "Rule that uses function to placeholder assignments"

    events:
        $u.metadata.event_type = "EMAIL_TRANSACTION"

        // Use function-placeholder assignment to extract the
        // address from an email.
        // address@website.com -> address
        $email_to_address_only = re.capture($u.network.email.from , "(.*)@")

        // Use function-placeholder assignment to normalize an email:
        // uid@??? -> uid@company.com
        $email_from_normalized = strings.concat(
            re.capture($u.network.email.from , "(.*)@"),
            "@company.com"
        )

        // Use function-placeholder assignment to get the day of the week of the event.
        // 1 = Sunday, 7 = Saturday.
        $dayofweek = timestamp.get_day_of_week($u.metadata.event_timestamp.seconds)

    match:
        // Use placeholder (from function-placeholder assignment) in match section.
        // Group by the normalized from email, and expose it in the detection.
        $email_from_normalized over 5m

    outcome:
        // Use placeholder (from function-placeholder assignment) in outcome section.
        // Assign more risk if the event happened on weekend.
        $risk_score = max(
            if($dayofweek = 1, 10, 0) +
            if($dayofweek = 7, 10, 0)
        )

    condition:
        // Use placeholder (from function-placeholder assignment) in condition section.
        // Match if an email was sent to multiple addresses.
        #email_to_address_only > 1
}

결과 조건부 예시 규칙

condition 섹션에서 outcome 섹션에 정의된 결과 변수를 사용할 수 있습니다. 다음 예시는 결과 조건부를 사용해서 감지에서 노이즈를 줄이기 위해 위험 점수를 필터링하는 방법을 보여줍니다.

rule OutcomeConditionalRule {
    meta:
        author = "alice@example.com"
        description = "Rule that uses outcome conditionals"

    events:
        $u.metadata.event_type = "FILE_COPY"
        $u.principal.file.size = $file_size
        $u.principal.hostname = $hostname

        // 1 = Sunday, 7 = Saturday.
        $dayofweek = timestamp.get_day_of_week($u.metadata.collected_timestamp.seconds)

    outcome:
        $risk_score =
            if($file_size > 500*1024*1024, 2) + // Files 500MB are moderately risky
            if($file_size > 1024*1024*1024, 3) + // Files over 1G get assigned extra risk
            if($dayofweek=1 or $dayofweek=7, 4) + // Events from the weekend are suspicious
            if($hostname = /highly-privileged/, 5) // Check for files from highly privileged devices

    condition:
        $u and $risk_score >= 10
}