YARA-L의 단일 및 여러 이벤트 규칙
이 문서에서는 YARA-L 2.0으로 작성된 쿼리를 보여줍니다. 각 예시에서는 쿼리 규칙 언어 내에서 이벤트의 상관관계를 지정하여 보안 위협을 식별하고, 엔티티 동작을 모니터링하고, 비즈니스 로직으로 탐지를 보강하는 방법을 보여줍니다.
단일 이벤트 감지, 정규 표현식 일치, 네트워크 범위 필터링을 포함하여 YARA-L 2.0의 구성요소로 예시를 사용합니다. 이러한 예는 기본 로직에서 고급 다중 이벤트 상관관계 및 복합 감지로 진행하는 데 도움이 되도록 기능별 카테고리로 정리되어 있습니다.
기본 문법 및 기본
이 섹션의 예에서는 규칙 언어 내에서 UDM 이벤트를 효과적으로 상호 연관시키고 쿼리를 구조화하는 방법을 보여줍니다.
| 주제 | 예시 |
|---|---|
| 단일 이벤트 쿼리 | 초기 사용자 로그인 검색; 5분 로그인 감지 |
| 쿼리 및 조정 | 제외 기반 프로세스 감지 |
| 네트워크 범위 및 논리 | 단일 이벤트 일치 (IP 범위) |
| 쿼리의 정규 표현식 | 이메일 필터링; 호스트 이름 정규식; 원시 로그 검색 |
| 범용 조건이 있는 반복되는 입력란 | 의심스러운 로그인 IP 유효성 검사 |
단일 이벤트 쿼리
사용 사례: 시간 창에서 상관관계를 지정할 필요 없이 특정 이벤트 유형 (예: USER_LOGIN)을 기본적으로 감지합니다.
키 로직: 이벤트 및 조건 섹션만 사용하여 단일 발생을 식별합니다. 단일 이벤트 규칙은 다음과 같을 수 있습니다.
match섹션이 없는 모든 규칙- 하나의 이벤트 존재만 확인하는
match섹션 및condition섹션의 규칙 (예:$e,#e > 0,#e >= 1,1 <= #e,0 < #e)
예: 초기 사용자 로그인 검색
규칙
다음 규칙 예시는 사용자 로그인 (USER_LOGIN) 이벤트를 검색하고 Google SecOps 계정 내에 저장된 기업 데이터 스토어 내에서 발생하는 첫 번째 항목을 반환합니다.
rule SingleEventRule {
meta:
author = "noone@altostrat.com"
events:
$e.metadata.event_type = "USER_LOGIN"
condition:
$e
}
검색
이 집계되지 않은 검색 예시는 개별 이벤트를 직접 출력합니다. 이 쿼리에는 이벤트 상관관계가 필요하지 않으므로 $e1와 같은 이벤트 변수가 생략됩니다.
metadata.event_type = "USER_LOGIN"
대시보드
이 쿼리 로직은 상관관계가 없는 특정 이벤트를 원시 상태로 표시하는 데 중점을 두므로 대시보드 시각화에 필요한 match 또는 outcome 섹션을 사용하지 않습니다.
예: 5분 로그인 감지
규칙
다음 예에서는 match 섹션을 사용하여 5분 (5m) 시간 범위 내에 로그인 이벤트가 하나 이상 발생한 사용자를 찾는 단일 이벤트 규칙을 보여줍니다. 사용자 로그인 이벤트의 존재를 확인합니다.
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
}
검색
이 통계 검색 예에서는 활동을 5분 (5m) 텀블링 윈도로 집계하며, 창당 사용자당 하나의 행을 출력합니다. 쿼리는 기간당 볼륨 수에 중점을 두므로 결과에 기본적으로 하나 이상의 이벤트가 포함되기 때문에 event 변수와 condition 섹션은 생략됩니다. 이 버전에서는 홉핑 윈도우 대신 텀블링 윈도우을 사용하여 플랫폼 내에서 결과가 올바르게 렌더링되도록 합니다.
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
match:
$user by 5m
대시보드
다음 예시에서는 outcome 섹션을 통합하여 사용자별 총 이벤트 수를 계산합니다. 이를 통해 데이터를 시간 경과에 따른 통계 값으로 표시할 수 있습니다. 이 쿼리는 텀블링 윈도우 대신 홉핑 윈도우를 사용하여 데이터 포인트가 중복되지 않는 개별 버킷에 매핑되도록 하고 대시보드 추세에 대한 더 명확한 시각화를 제공합니다.
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
match:
$user by 5m
outcome:
$event_count = count(metadata.id)
쿼리 및 조정
사용 사례: 비표준 디렉터리에서 실행되는 Windows svchost.exe 감지
주요 로직: 부정 (not)과 정규 표현식 일치 조합
예시: 제외 기반 프로세스 감지
규칙
다음 규칙은 이벤트 데이터의 특정 패턴을 확인하고 패턴이 발견되면 감지를 만듭니다. 이 규칙에는 이벤트 유형과 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
}
검색
이 예에서는 집계되지 않은 검색을 실행하여 개별 이벤트를 출력합니다. 이 검색에는 여러 인스턴스 간의 이벤트 상관관계가 필요하지 않으므로 $e1와 같은 이벤트 변수는 필요하지 않습니다.
metadata.event_type = "PROCESS_LAUNCH"
re.regex(principal.process.command_line, `\bsvchost(\.exe)?\b`) nocase
not re.regex(principal.process.command_line, `\\Windows\\System32\\`) nocase
대시보드
이 구문은 match 및 outcome 섹션을 통합하여 시간 경과에 따른 이벤트 볼륨을 계산합니다. timestamp.get_timestamp() 함수는 추세 시각화를 위해 결과를 일별로 버킷팅합니다.
metadata.event_type = "PROCESS_LAUNCH"
re.regex(principal.process.command_line, `\bsvchost(\.exe)?\b`) nocase
not re.regex(principal.process.command_line, `\\Windows\\System32\\`) nocase
$date = timestamp.get_timestamp(metadata.event_timestamp.seconds)
match:
$date
outcome:
$event_count = count(metadata.id)
네트워크 범위 및 논리
사용 사례: 특정 IP 서브넷 (CIDR)을 기반으로 활동을 필터링하고 가능한 여러 호스트 이름과 일치시킵니다.
핵심 로직: 서브넷 일치에는 net.ip_in_range_cidr() 함수를 사용하고 가능한 호스트 이름이 여러 개인지 확인하려면 OR 연산자를 사용합니다.
net.ip_in_range_cidr(): 이 함수는 지정된 IP 주소가 지정된 클래스 없는 도메인 간 라우팅 (CIDR) 서브넷에 포함되어 있는지 확인합니다.- 논리 연산자
OR: 여러 조건을 결합하는 데 사용됩니다. 이벤트 섹션 내의 조건은AND와 암시적으로 결합됩니다.OR연산자는 가능한 여러 호스트 이름을 확인합니다.
예: 단일 이벤트 일치 (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 주소가 정의된 CIDR 범위 내에 있고 호스트 이름이 특정 사용자 패턴과 일치하는 이벤트를 식별합니다.
net.ip_in_range_cidr(principal.ip, "203.0.113.0/24")
principal.hostname = /pbateman/ or principal.hostname = /sspade/
이는 감지 규칙이 아닌 검색어이므로 필터가 충족되면 전체 이벤트가 자동으로 반환됩니다. match 섹션은 principal.ip 및 principal.hostname별로 데이터를 그룹화합니다. condition 섹션은 필요하지 않으며 이벤트 상관관계가 수행되지 않으므로 이벤트 변수 ($e)가 생략됩니다.
대시보드
다음 예시 쿼리는 고유 IP 및 호스트 이름 쌍을 그룹화하여 결과를 집계합니다.
net.ip_in_range_cidr(principal.ip, "203.0.113.0/24")
principal.hostname = /pbateman/ or principal.hostname = /sspade/
match:
principal.ip, principal.hostname
쿼리의 정규 표현식
사용 사례: 대소문자를 무시하면서 유연한 문자열 패턴 (예: 이메일의 특정 도메인)을 검색합니다. 이 기능은 검색 및 규칙에서 가장 일반적으로 사용됩니다.
핵심 로직: 기본 일치에는 /regex/ nocase를 사용하고 복잡한 필드 분석에는 re.regex() 함수를 사용합니다.
예: 이메일 필터링
규칙
다음 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
}
검색
검색 인터페이스에서 이 로직은 충실도가 높은 위협 헌팅 및 데이터 탐색에 사용됩니다. 분석가는 자동 알림을 기다리는 대신 UDM을 수동으로 쿼리하여 타겟팅된 이메일 도메인과 함께 명명 규칙과 일치하는 호스트 이름의 특정 인스턴스를 발견할 수 있습니다. 이는 이러한 이벤트를 영구 감지 규칙으로 승격하기 전에 이벤트의 양을 검증하는 기본 방법입니다.
principal.hostname = $host
$host = /.*HoSt.*/ nocase
re.regex(network.email.from, `.*altostrat\.com`) nocase
match:
$host over 10m
대시보드
다음 로직은 hostname 및 email 원격 분석을 10분 (10m) 버킷으로 집계하여 관심 패턴을 식별합니다. 대시보드에서 사용되는 경우 이 로직을 통해 분석가는 특정 애셋 (host 일치)에서 altostrat.com 도메인으로의 통신 빈도를 시각화할 수 있습니다. 이 보기는 내부 데이터 이동 추세를 모니터링하고 중요한 기반 시설 전반에서 top talkers를 식별하는 데 필수적입니다.
principal.hostname = $host
$host = /.*HoSt.*/ nocase
re.regex(network.email.from, `.*altostrat\.com`) nocase
match:
$host over 10m
예: 호스트 이름 정규 표현식
규칙
다음 예에서는 보안 주체 hostname가 웹 (webserver) 또는 개발 (devserver) 서버로 식별되는 활동 기록을 식별합니다. 이 도구는 대소문자를 구분하지 않는 정규 표현식을 사용하여 명명 규칙의 변형으로 인해 감지가 누락되지 않도록 합니다.
rule WebServerOrDevServerActivity {
meta:
author = "Alex"
description = "Detects events where the principal hostname is 'webserver' or 'devserver', ignoring case."
severity = "Informational"
events:
$e.principal.hostname = /webserver|devserver/ nocase
condition:
$e
}
검색
다음 예에서 principal.hostname = /webserver|devserver/ nocase은 "WebServer01", "devserver-test", "MyWebServers"과 같은 호스트 이름과 일치합니다. 이는 특정 이벤트를 찾는 일반적인 사용 사례입니다.
// Use /regex/ followed by nocase for a case-insensitive match
principal.hostname = /webserver|devserver/ nocase
대시보드
이 특정 예는 대시보드에 시각화되지 않지만 이 규칙은 활성 알림과 지속적인 감지를 제공합니다. 수동 검토가 필요한 대시보드와 달리, 이렇게 하면 이러한 서버의 모든 활동 인스턴스가 감지 엔진에서 자동으로 플래그가 지정되고 기록되어 즉시 검색할 수 있습니다.
예: 원시 로그 검색
규칙
특정 시점 조사를 위해 수동 검색을 사용하는 동안 감지 규칙은 연중무휴 24시간 지속적인 원격 분석 모니터링을 제공합니다. 성공적인 검색어를 YARA-L 규칙으로 변환하여 알림 프로세스를 자동화할 수 있습니다.
규칙의 주요 이점:
- 실시간 알림: 일치 항목이 시스템에 입력되면 자동으로 플래그를 지정합니다.
- 지속성: 검색어를 수동으로 다시 입력할 필요가 없습니다.
- 결과 작업: 분석가 분류 및 사고 대응을 위해 감지 뷰에 직접 피드합니다.
검색
보안 분석가는 Google SecOps 내에서 파싱되지 않은 원시 로그를 검색하기 위해 regex를 자주 사용합니다. 이 작업을 사용하면 완전히 구조화되거나 색인이 생성되지 않은 특정 아티팩트도 유연한 패턴 일치를 통해 찾을 수 있습니다. 슬래시를 사용하는 문법:
raw = /host/
이 쿼리는 문자 시퀀스 "host"가 표시되는 원시 로그 줄을 반환합니다. 일치하는 원시 로그 콘텐츠의 예로는 "hostname": "myhost123"가 있습니다.
대시보드
이 특정 이벤트 유형에 대한 전용 대시보드 변형은 없습니다. 이러한 감지를 대규모로 시각화하려면 다음을 수행하세요.
- 대시보드 빌더 내에서
metadata.event_type를 막대 또는 원형 차트에 매핑합니다. - 7일, 30일 또는 90일 기간 동안 이러한 이벤트의 빈도를 추적하여 사용자 행동의 이상치를 파악합니다.
유니버설 조건이 있는 반복되는 입력란
사용 사례: 데이터 목록 (반복되는 입력란)이 포함된 이벤트를 감사하여 신뢰할 수 있는 예외가 없는지 확인합니다. 예를 들어 로그인과 연결된 모든 IP 주소가 알려진 보안 범위 외부에 있는지 확인합니다.
핵심 로직: all 연산자를 사용하여 반복되는 입력란의 모든 요소를 특정 조건에 대해 평가하고 반복되는 입력란을 플레이스홀더 변수 (예: $ip)에 할당하면 목록의 각 고유 값에 대해 별도의 감지가 생성되는 방법을 보여줍니다.
예시: 의심스러운 로그인 IP 검증
규칙
다음 규칙은 5분 (5m) 내에 모든 소스 IP 주소가 보안으로 알려진 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
}
검색
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 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.
principal.ip = $ip
match:
$ip over 5m
대시보드
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 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.
principal.ip = $ip
match:
$ip over 5m
고급 윈도우
이 섹션에서는 다른 규칙의 활동으로 트리거되는 다단계 패턴과 감지를 다룹니다.
| 주제 | 예시 |
|---|---|
| 다중 이벤트 상관관계 | 다구간 로그인 감지; 빠른 사용자 만들기 및 삭제 |
| 쿼리의 슬라이딩 기간 | 누락된 순차 이벤트 감지 |
| 다중 이벤트 쿼리 | 고빈도 로그인 감지 |
| 계산된 결과가 포함된 멀티 이벤트 쿼리 | 무차별 대입 공격 후 로그인 성공, 시간대별 호스트 일치 |
다중 이벤트 상관관계
이 섹션에서는 여러 이벤트 또는 기간에 걸쳐 항목 (사용자 또는 호스트)을 추적하여 행동 패턴을 식별하는 방법의 예를 보여줍니다.
사용 사례: 한 사용자가 5분 (5m) 이내에 두 개 이상의 도시에서 로그인하는 불가능한 이동을 감지합니다.
키 논리: match 섹션을 사용하여 $user 및 #city > 1로 그룹화하여 고유한 위치 값을 찾습니다.
예시: 다구간 로그인 감지
규칙
다음 규칙은 5 (5m)분 이내에 두 개 이상의 도시에서 기업에 로그인한 사용자를 검색합니다. 여기서 $user은 match 변수이고, $udm은 이벤트 변수이며, $city 및 $user은 자리표시자 변수입니다.
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)과 그룹으로 묶고 일치가 발견되면 이를 반환($user)합니다. - 기간은 5분 (
5m)입니다. 5분 (5m) 미만으로 떨어진 이벤트만 상관됩니다. - 해당 이벤트 유형이
USER_LOGIN인 이벤트 그룹 ($udm)을 검색합니다. - 이 이벤트 그룹에 대해 이 규칙은 사용자 ID를
$user로 호출하고 로그인 도시를$city로 호출합니다. city값 (#city로 표시됨)의 고유 숫자가 이벤트 그룹 ($udm)에서 5분 (5m) 기간 내에1보다 크면 일치를 반환합니다.
검색
다음 예시 쿼리는 동일한 통계 검색을 실행하여 불가능한 이동 패턴을 식별합니다. 5분 (5m) 기간 내에 사용자별로 USER_LOGIN 이벤트를 그룹화하고 결과를 필터링하여 단일 ID에 대해 여러 개의 고유한 도시가 감지된 인스턴스만 표시합니다.
events:
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
principal.location.city = $city
match:
$user over 5m
condition:
#city > 1
대시보드
다음 예시 쿼리는 잠재적인 계정 도용을 추적하는 것과 동일한 대시보드 시각화를 제공합니다. 5분 (5m) 기간 동안 사용자별로 USER_LOGIN 이벤트를 집계하고 단일 ID가 두 개 이상의 고유 도시 (#city)와 연결된 인스턴스를 필터링하여 시간 경과에 따른 이러한 고위험 지리적 이상치를 표시할 수 있습니다.
events:
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
principal.location.city = $city
match:
$user over 5m
condition:
#city > 1
빠른 사용자 만들기 및 삭제
사용 사례: 4시간 이내에 생성된 후 삭제된 일회용 계정을 식별합니다.
핵심 로직: 공유된 $user 변수에서 두 이벤트 유형 (USER_CREATION 및 USER_DELETION)을 조인하고 타임스탬프를 비교합니다.
예: 빠른 사용자 만들기 및 삭제
규칙
다음 규칙 예에서는 $create 및 $delete가 이벤트 변수이고 $user이 match 변수이며 자리표시자 변수가 없는 경우 4시간 (4h) 이내에 생성된 후 삭제된 사용자를 검색합니다.
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
}
검색
다음 예에서는 빠른 계정 수명 주기 변경사항을 식별하는 데 사용되는 다중 이벤트 통계 검색을 보여줍니다. 이 쿼리는 4시간 내에 사용자당 하나의 행을 출력하여 ID 생성과 삭제를 상호 연관시킵니다.
검색은 기본적으로 지정된 이벤트가 포함된 모든 기간을 반환하므로 condition 섹션이 필요하지 않습니다.
$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
대시보드
다음 예에서는 시간 경과에 따른 계정 수명 주기 추세를 표시하도록 설계된 다중 이벤트 대시보드 검색을 보여줍니다. 텀블링 윈도우 (by 4h)를 활용하면 결과가 중복되지 않는 개별 시간 버킷에 매핑되므로 시각화에 적합합니다.
이 변형에는 각 기간 내의 고유한 생성 이벤트 수를 계산하는 outcome 섹션이 포함되어 있습니다. 이전 검색과 달리 이 버전은 개별 로그 줄이 아닌 집계 통계 값에 중점을 두므로 특정 이벤트 변수를 반환할 필요가 없습니다.
$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 by 4h
outcome:
$event_count = count_distinct($create.metadata.id)
쿼리의 슬라이딩 기간
사용 사례: 특정 기간 내에 동일한 호스트에서 초기 이벤트 (firewall_1) 다음에 예상되는 후속 이벤트 (firewall_2)가 발생하지 않는 잠재적인 보안 문제를 감지합니다.
핵심 로직:
- 피벗 이벤트: 규칙이
firewall_1의 이벤트를 중심으로 하며$e1로 지정됩니다.$e1이벤트가 발생할 때마다 피벗 역할을 합니다. - 시간 범위:
match섹션 ($host over 10m after $e1)은 각$e1이벤트 직후에 시작되는 10분 범위를 정의합니다. 이 기간은 새$e1이벤트가 발생할 때마다 슬라이딩됩니다. - 상관관계: 이벤트가 호스트 이름 (
$host)별로 그룹화됩니다. - 감지 조건 (
$e1및$e2): 다음 조건이 충족되면 특정 호스트에 대한 감지가 트리거됩니다.firewall_1($e1)의 이벤트가 있습니다.AND, 해당$e1이벤트 후 10분 이내에 동일한 호스트에 대해firewall_2($e2)의NO이벤트가 발견되었습니다.
예시: 누락된 순차적 이벤트 감지
규칙
다음 예에서는 기본 트리거 후 보조 이벤트가 발생하지 않는 인스턴스를 식별합니다. 10분 내에 !$e2 조건을 사용하면 이 규칙은 누락된 원격 분석을 신고합니다. 특히 한 위치에서 방화벽 로그가 표시되지만 다음 예상 홉에 표시되지 않는 경우 잠재적인 가시성 격차 또는 트래픽 감소를 나타냅니다.
rule MissingSequentialEvent {
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:
// $e1 is the pivot; the 10-minute window starts at the $e1 timestamp
$host over 10m after $e1
condition:
$e1 and !$e2
}
검색
다음 예에서는 두 소스 간의 원격 분석 격차를 식별하는 데 사용되는 순차적 검색을 보여줍니다. $e1를 피벗으로 사용하면 검색에서 10분 이내에 두 번째 방화벽에서 해당 이벤트가 뒤따르지 않는 기본 방화벽 이벤트를 찾습니다. 이는 조사 중에 네트워크 트래픽의 '블랙홀'을 수동으로 찾거나 오류를 로깅하는 매우 효과적인 방법입니다.
$e1.metadata.product_name = "firewall_1"
$e1.principal.hostname = $host
$e2.metadata.product_name = "firewall_2"
$e2.principal.hostname = $host
match:
// $e1 is the pivot; the 10-minute window starts at the $e1 timestamp
$host over 10m after $e1
condition:
$e1 and !$e2
대시보드
다음 예에서는 대시보드 뷰를 위해 설계된 가시성 격차 분석을 제공합니다. 보조 이벤트가 기본 이벤트를 따르지 않는 인스턴스를 집계하면 시간 경과에 따른 로깅 파이프라인의 안정성을 시각화할 수 있습니다. 이러한 '누락된' 이벤트를 표시하면 네트워크 가시성의 지속적인 데드존이나 특정 호스트 이름의 구성 문제를 식별하는 데 도움이 됩니다.
$e1.metadata.product_name = "firewall_1"
$e1.principal.hostname = $host
$e2.metadata.product_name = "firewall_2"
$e2.principal.hostname = $host
match:
// $e1 is the pivot; the 10-minute window starts at the $e1 timestamp
$host over 10m after $e1
condition:
$e1 and !$e2
다중 이벤트 쿼리
사용 사례: 특정 기간 내에 이벤트가 여러 번 발생하는 동안 단일 항목 (예: 사용자 또는 호스트)을 추적하여 빈도가 높은 활동 또는 무차별 대입 공격 활동을 식별합니다.
핵심 로직:
match섹션을 사용하여 특정 변수별로 이벤트를 그룹화하고condition섹션을 사용하여 정의된 기간 내에서 기준점 수 (예:#e >= 10)를 확인합니다.
일반적인 멀티 이벤트 규칙에는 다음이 포함됩니다.
- 이벤트를 구분하는 이벤트 변수
- 이벤트를 그룹화해야 하는 기간을 지정하는
match섹션 - 발견 항목을 트리거하고 여러 이벤트의 존재 여부를 확인하는 조건을 지정하는
condition섹션
검색에서 다중 이벤트 쿼리는 쿼리에 이벤트가 두 개 이상 있는 것으로 정의됩니다. 규칙의 경우 이를 정의하는 방법은 두 가지입니다.
여러 이벤트: (예:
event1 = successful login, event2 = failed login)조건 기반 트리거: 여러 이벤트가 기준을 충족하는 경우에만 트리거되도록 조건이 명시됩니다 (예:
event1 > 10). 이 유형의 규칙에는outcome섹션도 포함되어야 합니다.
예: 빈도가 높은 로그인 감지
규칙
다음 규칙은 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
}
검색
다음 예에서는 다중 이벤트 통계 검색을 사용하여 빈번한 로그인 활동을 식별합니다. 단일 사용자가 10분 (10m) 내에 10개 이상의 로그인 이벤트를 생성하는 인스턴스에 플래그를 지정합니다.
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user by 10m
condition:
#e >= 10
대시보드
다음 예에서는 다중 이벤트 검색을 사용하여 계정 도용 가능성을 모니터링합니다. 10분 슬라이딩 윈도우 내에서 로그인 시도를 상호 연관시켜 특정 사용자와 호스트가 여러 번의 로그인 실패 후 성공적인 로그인을 경험하는 인스턴스를 식별하여 고위험 인증 패턴을 실시간으로 시각화할 수 있습니다.
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user by 10m
condition:
#e >= 10
계산된 결과가 있는 멀티 이벤트 쿼리
사용 사례: 조건부 논리를 적용하여 애셋 심각도 또는 네트워크 볼륨에 따라
risk_score을 설정합니다.핵심 로직:
outcome섹션을 사용하여 변수를 계산하고 조건 섹션을 사용하여 해당 변수로 필터링합니다.
예: 무차별 대입 후 로그인 성공
다음 예시에서는 outcome 섹션을 사용하여 match 기간 내의 이벤트를 집계합니다. 이 쿼리는 표준 멀티 이벤트 쿼리와 동일한 출력을 생성하지만 계산된 변수를 감지 로직에 통합하는 방법을 보여줍니다.
규칙
rule PossibleBruteForceThenSuccessfulLogin {
meta:
author = "Alex"
description = "Detects multiple failed login attempts followed by a successful login for the same user and host within a 10-minute window."
severity = "High"
tactic = "Credential Access"
events:
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a time window: by 10m.
// The rule will evaluate all events matching $failed or $success that share the same $user and $hostname within any given 10-minute period.
$user, $hostname by 10m
outcome:
// Calculate aggregate values from the events within the match window.
$failed_login_count = count($failed.metadata.id)
$successful_login_count = count($success.metadata.id)
condition:
// The conditions that must be met *within each matched group* ($user, $hostname over 10m).
// - #failed >= 5: There must be 5 or more events matching the $failed criteria.
// - #success >= 1: There must be at least 1 event matching the $success criteria.
#failed >= 5 and #success >= 1
}
검색
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
대시보드
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
예: 시간 창 호스트 일치
규칙
다음 규칙에서는 두 가지 이벤트를 확인하여 $hostname 값을 가져옵니다. $hostname 값이 5분 (5m) 이상 일치하면 심각도 점수가 적용됩니다. 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")
}
검색
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
outcome:
// Calculate aggregate values from the events within the match window.
$failed_login_count = count($failed.metadata.id)
$successful_login_count = count($success.metadata.id)
```
대시보드
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
outcome:
// Calculate aggregate values from the events within the match window.
$failed_login_count = count($failed.metadata.id)
$successful_login_count = count($success.metadata.id)
컨텍스트 인식 분석
YARA-L 규칙을 사용하여 침해 지표 (IOC) 컨텍스트 데이터에 대해 UDM 이벤트 데이터를 평가할 수 있습니다. 이 문맥 데이터는 Google SecOps에 항목 컨텍스트로 저장됩니다.
이 섹션의 예시에서는 이벤트 필드를 Google 세이프 브라우징 및 커스텀 피드와 같은 IOC 피드와 조인하여 상황 인식 분석을 만드는 방법을 보여줍니다.
침해 지표와 파일 해시 매칭
사용 사례: 위협 피드에서 침해 지표 (IOC)로 식별된 파일의 실행 또는 존재를 감지합니다.
핵심 로직: 항목 그래프의 IOC 피드에서 UDM 프로세스 또는 파일 이벤트를 파일 항목 (해시)과 조인합니다.
예: 의심스러운 프로세스 실행
규칙
다음 규칙은 일반 IoC 문맥 데이터에 대해 UDM 프로세스 데이터를 평가합니다. 프로세스 실행 이벤트를 검색하고 이벤트의 파일 해시를 IOC 엔티티 그래프에서 찾은 파일 해시와 조인합니다.
rule ProcessLaunch {
meta:
events:
$ioc.graph.metadata.vendor_name = "ACME"
$ioc.graph.metadata.product_name = "IOCs"
$ioc.graph.metadata.entity_type = "FILE"
$ioc.graph.entity.file.sha256 = $hash
$process.metadata.event_type = "PROCESS_LAUNCH"
$process.principal.hostname = $hostname
(
not $process.target.process.file.sha256 = "" and
$process.target.process.file.sha256 = $hash
)
match:
$hash over 15m
condition:
$ioc and $process
}
예시: 세이프 브라우징 위협 목록
규칙
Google SecOps는 파일 해시와 관련된 위협 목록에서 데이터를 수집하며, 이 데이터는 항목으로 저장됩니다. 다음 규칙은 Google 세이프 브라우징에서 악성으로 간주하는 파일의 실행을 감지합니다.
rule safe_browsing_file_execution {
meta:
author = "Google Security Operations"
description = "Example usage of Safe Browsing data, to detect execution of a file that's been deemed malicious"
severity = "LOW"
events:
// find a process launch event, match on hostname
$execution.metadata.event_type = "PROCESS_LAUNCH"
$execution.principal.hostname = $hostname
// join execution event with Safe Browsing graph
$sb.graph.entity.file.sha256 = $execution.target.process.file.sha256
// look for files deemed malicious
$sb.graph.metadata.entity_type = "FILE"
$sb.graph.metadata.threat_intel.severity = "SEVERITY_HIGH"
$sb.graph.metadata.product_name = "Google Safe Browsing"
$sb.graph.metadata.source_type = "GLOBAL_CONTEXT"
match:
$hostname over 1h
outcome:
$risk_score = max(if($sb.graph.metadata.threat_intel.severity = "SEVERITY_HIGH", 90, 0))
condition:
$execution and $sb
}
침해 지표와 일치하는 IP 주소
사용 사례: 위협 피드에 저장된 알려진 악성 IP 주소와의 네트워크 통신을 감지합니다.
핵심 로직: 엔티티 그래프에서 UDM 네트워크 이벤트를 IOC 피드의 IP 주소 엔티티와 조인합니다.
예: Tor 출구 노드 감지
규칙
알려진 Tor 종료 노드에서 시작되거나 연결되는 네트워크 트래픽을 식별합니다. 이 예에서는 모든 고객이 사용할 수 있는 GCTI 피드의 Tor 출구 노드 피드와 UDM 이벤트를 조인합니다.
rule TorExitNodeMatch {
meta:
author = "Google Security Operations"
description = "Detects traffic from known Tor exit nodes."
severity = "MEDIUM"
events:
// Event based on IP
$principal_ip = $e.principal.ip
// Filter Entity Graph to extract feed specific details
$gcti.graph.metadata.entity_type = "IP_ADDRESS"
$gcti.graph.metadata.feed = "Tor Exit Nodes"
$gcti.graph.metadata.product_name = "GCTI Feed"
// Join the IP data
$principal_ip = $gcti.graph.entity.ip
match:
$principal_ip over 30m
outcome:
$threat_feed = array_distinct($gcti.graph.metadata.feed)
condition:
$e and $gcti
}
예시: Tor 네트워크에서 여러 번의 로그인 실패
규칙
Tor 네트워크에서 여러 번의 로그인 시도가 실패했는지 감지합니다. 이 예에서는 login_failure 이벤트를 Tor 출구 노드 피드와 연관시키고 개수 구문 (#)을 사용하여 빈도 기준점을 적용합니다.
rule TorMultiLoginFailure {
meta:
author = "Google Security Operations"
description = "Detects multiple failed logins from a Tor network."
severity = "HIGH"
events:
$failed_login_event.metadata.product_name = "login"
$failed_login_event.metadata.product_event_type = "login_failure"
$failed_login_event.metadata.vendor_name = "Google Workspace"
$sus_ip = $failed_login_event.principal.ip
$sus_target_account = $failed_login_event.target.user.email_addresses
$sus_target_account != ""
$gcti.graph.metadata.entity_type = "IP_ADDRESS"
$gcti.graph.metadata.feed = "Tor Exit Nodes"
$gcti.graph.metadata.product_name = "GCTI Feed"
// Join the IP data
$sus_ip = $gcti.graph.entity.ip
match:
$sus_ip over 1h
outcome:
$event_count = count_distinct($failed_login_event.metadata.id)
condition:
$failed_login_event and #failed_login_event >= 10 and $gcti
}
침해 지표에 대한 도메인 일치
사용 사례: 침해 지표 (IOC)로 표시된 도메인 또는 발생률이 낮은 도메인에 대한 네트워크 활동을 감지합니다.
핵심 로직: 도메인 이름 또는 호스트 이름을 사용하여 엔티티 그래프의 IoC 피드 또는 발생률 데이터와 UDM 네트워크 이벤트를 조인합니다.
예: 낮은 발생률 도메인
규칙
다음 규칙은 보급률이 낮은 도메인을 알려진 IOC와 비교하여 감지 알림을 생성합니다. DNS 쿼리 이벤트를 발생률 데이터 및 ET_PRO_IOC 피드의 IOC 항목과 조인합니다.
rule network_prevalence_uncommon_domain_ioc_match {
meta:
author = "Google Security Operations"
description = "Lookup Network DNS queries against Entity Graph for low prevalence domains with a matching IOC entry."
severity = "MEDIUM"
events:
$e.metadata.event_type = "NETWORK_DNS"
$e.network.dns.questions.name = $hostname
//only match FQDNs, such as: exclude chrome dns access tests and other internal hosts
$e.network.dns.questions.name = /(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/
//prevalence entity graph lookup
$p.graph.metadata.entity_type = "DOMAIN_NAME"
$p.graph.entity.domain.prevalence.rolling_max > 0
$p.graph.entity.domain.prevalence.rolling_max <= 3
$p.graph.entity.domain.name = $hostname
//ioc entity graph lookup
$i.graph.metadata.vendor_name = "ET_PRO_IOC"
$i.graph.metadata.entity_type = "DOMAIN_NAME"
$i.graph.entity.hostname = $hostname
match:
$hostname over 10m
outcome:
$risk_score = max(
//increment risk score based upon rolling_max prevalence
if ( $p.graph.entity.domain.prevalence.rolling_max = 3, 50) +
if ( $p.graph.entity.domain.prevalence.rolling_max = 2, 70) +
if ( $p.graph.entity.domain.prevalence.rolling_max = 1, 90)
)
condition:
$e and $p and $i
}
침해 지표에 대한 URL 일치
사용 사례: 위협 피드에 저장된 악성 URL과 관련된 네트워크 활동을 감지합니다.
주요 로직: URL 데이터가 포함된 UDM 네트워크 이벤트를 항목 그래프의 IOC 피드에 있는 URL 항목과 조인합니다.
예시: 위협 피드의 악성 URL 일치
규칙
다음 규칙은 요청된 URL이 위협 인텔리전스 피드의 알려진 악성 URL과 일치하는 네트워크 이벤트를 감지합니다. 이벤트의 target.url를 항목 그래프의 URL 항목과 상호 연결합니다.
rule ioc_url_threat_feed_match {
meta:
author = "Google Security Operations"
description = "Matches network events against known malicious URLs from threat intelligence feeds."
severity = "HIGH"
events:
// Filter for network traffic containing URL information
$network_event.metadata.event_type = "NETWORK_HTTP" or $network_event.metadata.event_type = "NETWORK_CONNECTION"
$url = $network_event.target.url
// Entity Graph Join: URL Indicators
$ioc.graph.metadata.entity_type = "URL"
$ioc.graph.metadata.product_name = "GCTI Feed"
$ioc.graph.metadata.feed = "Malicious URLs"
$ioc.graph.entity.url = $url
match:
$url over 5m
outcome:
$risk_score = 85
$threat_feed = array_distinct($ioc.graph.metadata.feed)
$principal_ip = array_distinct($network_event.principal.ip)
$principal_user = array_distinct($network_event.principal.user.userid)
condition:
$network_event and $ioc
}
동시 다중 피드 일치
사용 사례: 단일 이벤트 쿼리 내에서 여러 속성 (예: IP 주소 및 파일 해시)을 별도의 위협 피드와 일치시켜 고급 위협을 상호 연관시킵니다.
주요 로직: 단일 UDM 이벤트를 여러 항목 그래프 자리표시자와 조인합니다. 각 자리표시자는 특정 threat_feed_name (예: Google Cloud 위협 인텔리전스 피드)를 필터링합니다.
예: Tor 출구 노드 및 원격 액세스 도구 감지
규칙
다음 규칙은 원격 모니터링 및 관리 (RMM)와 연결되어 있고 Tor 종료 노드에서 시작되거나 Tor 종료 노드에 연결되는 것으로 보이는 네트워크 트래픽에 플래그를 지정합니다. NETWORK_CONNECTION 이벤트를 IP 피드 (Tor 종료 노드)와 파일 해시 피드 (원격 액세스 도구)에 모두 조인합니다.
rule tor_and_remote_access_tool_match {
meta:
author = "Google Security Operations"
description = "Matches network connections involving both a Tor exit node and Remote Access Tool malware hashes."
severity = "CRITICAL"
events:
// Filter for network connection events, often recorded by EDR agents
$e.metadata.event_type = "NETWORK_CONNECTION"
$target_ip = $e.target.ip
$process_hash = $e.principal.process.file.sha256
// IP Feed Join: Tor Exit Nodes
$gcti_ip.graph.metadata.entity_type = "IP_ADDRESS"
$gcti_ip.graph.metadata.product_name = "GCTI Feed"
$gcti_ip.graph.metadata.feed = "Tor Exit Nodes"
$gcti_ip.graph.entity.ip = $target_ip
// Hash Feed Join: Remote Access Tools
$gcti_hash.graph.metadata.entity_type = "FILE"
$gcti_hash.graph.metadata.product_name = "GCTI Feed"
$gcti_hash.graph.metadata.feed = "Remote Access Tools"
$gcti_hash.graph.entity.file.sha256 = $process_hash
match:
$target_ip over 10m
outcome:
$risk_score = 95
$ip_feed = array_distinct($gcti_ip.graph.metadata.feed)
$hash_feed = array_distinct($gcti_hash.graph.metadata.feed)
condition:
$e and $gcti_ip and $gcti_hash
}
복합 감지
복합 탐지는 복합 규칙을 사용하여 위협 탐지를 강화합니다. 이러한 복합 규칙은 다른 규칙의 감지를 입력으로 사용합니다. 이를 통해 개별 규칙에서 감지하지 못할 수 있는 복잡한 위협을 감지할 수 있습니다. 자세한 내용은 복합 감지 개요를 참고하세요.
| 주제 | 예시 |
|---|---|
| 고위험 필터링 | 관리 권한 사용자 감지 |
| 집계 및 기준점 설정 | 위험 집계 |
| 전술 집계 | MITRE 전술 집계 |
| 순차적 복합 감지 | 무차별 대입 시도 후 로그인 성공 |
| 컨텍스트 인식 감지 | 위협 인텔리전스 보강 |
| 동시 발생 감지 | 권한 에스컬레이션 및 유출 동시 발생 |
고위험 필터링
사용 사례: 관리 계정과 관련된 활동과 같은 고위험 속성에 대해 기존 감지를 필터링합니다.
핵심 로직: 기존 발견 항목 내의 결과 또는 메타데이터 필드에서 작동합니다.
위험도가 높은 필터링 복합 감지는 결과 변수나 규칙 메타데이터와 같은 감지 결과 내 필드에서 작동하는 가장 간단한 형태의 복합 감지입니다. 관리자 사용자 또는 프로덕션 환경과 같이 위험이 더 높을 수 있는 조건에 대한 감지를 필터링하는 데 도움이 됩니다.
예시: 관리 권한 사용자 감지
규칙
다음 복합 규칙은 행위자가 관리 권한 사용자로 식별된 기존 감지를 검색하고 표준화된 리스크 스코어링을 적용합니다.
rule composite_admin_detection {
meta:
rule_name = "Detection with Admin User"
author = "Google Cloud Security"
description = "Composite rule that looks for any detections where the actor is an admin user"
severity = "Medium"
events:
$rule_name = $d.detection.detection.rule_name
$principal_user = $d.detection.detection.variables["principal_users"]
$principal_user = /admin|root/ nocase
match:
$principal_user over 1h
outcome:
$risk_score = 75
$upstream_rules = array_distinct($rule_name)
condition:
$d
}
검색
다음 통계 검색은 권한이 높은 계정의 활동을 식별하고 집계합니다. 'admin' 또는 'root' 사용자와 관련된 감지를 트리거한 모든 고유한 규칙 이름을 찾도록 설계되었습니다.
이 특정 쿼리에서는 선택한 기간 내의 모든 감지 항목에 대해 단일 통계 분석을 실행하기 위해 기간이 삭제됩니다. 또한 기존 감지 데이터에 초점을 맞춘 집계되지 않은 검색이므로 이벤트 섹션, 이벤트 변수, 조건 섹션은 필요하지 않습니다.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_users"]
$principal_user = /admin|root/ nocase
match:
$principal_user
outcome:
$upstream_rules = array_distinct($rule_name)
대시보드
이 대시보드 쿼리를 사용하면 관리자 관련 활동을 가장 자주 감지하는 특정 규칙을 시각화할 수 있습니다. 환경 전반의 감지 추세를 대략적으로 파악할 수 있도록 설계되었습니다.
이전 예시와 비교하여 match 변수와 outcome 집계가 변경되었음을 확인하세요. 이 쿼리는 규칙 이름별로 결과를 그룹화하고 각 규칙에 대해 감지된 관리자 수를 계산합니다.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_users"]
$principal_user = /admin|root/ nocase
match:
$rule_name
outcome:
$admin_detections = count($principal_user)
집계 및 기준점 설정
사용 사례: 시간이 지남에 따라 많은 수의 알림을 생성하거나 상당한 리스크 스코어링을 누적하는 사용자 또는 호스트를 식별합니다.
핵심 로직: sum() 또는 count_distinct()을 사용하여 집계된 감지 데이터를 분석합니다.
집계 컴포지트 감지 규칙을 사용하면 호스트 이름이나 사용자 이름과 같은 공유 속성을 기반으로 감지 결과를 그룹화하고 집계된 데이터를 분석할 수 있습니다. 일반적인 사용 사례는 다음과 같습니다.
- 보안 알림 또는 집계된 위험을 많이 생성하는 사용자를 식별합니다.
- 관련 탐지를 집계하여 비정상적인 활동 패턴이 있는 호스트를 감지합니다.
예시: 위험 집계
규칙
이 규칙은 48시간 동안 단일 사용자의 위험 점수를 집계합니다. 여러 감지에서 누적된 위험이 특정 기준점을 초과하는 사용자를 식별합니다.
이 업데이트된 로직에서 detection.detection.outcomes는 match 및 outcome 변수를 모두 저장하는 맵 필드 변수로 대체됩니다. 또한 각 감지에는 이미 캡처된 정확히 하나의 일치 변수 값이 포함되어 있으므로 $principal_users 결과 변수가 삭제됩니다.
rule composite_risk_aggregation {
meta:
rule_name = "Risk Aggregation Composite"
author = "Google Cloud Security"
description = "Composite detection that aggregates risk of a user over 48 hours"
severity = "High"
events:
$rule_name = $d.detection.detection.rule_name
$principal_user = $d.detection.detection.outcomes["principal_users"]
$risk = $d.detection.detection.risk_score
match:
$principal_user over 48h
outcome:
$risk_score = 90
$cumulative_risk = sum($risk)
$upstream_rules = array_distinct($rule_name)
condition:
$d and $cumulative_risk > 500
}
검색
이 통계 검색은 감지 데이터를 집계하여 48시간 동안의 사용자의 총 위험을 계산합니다. 각 기간에 대해 주 구성원 사용자당 하나의 행을 출력하여 여러 감지 유형에 걸친 계정 위험에 대한 개략적인 보기를 제공합니다.
이 변형에서는 이벤트 변수가 필요하지 않습니다. 규칙 엔진은 주 사용자가 없는 감지를 자동으로 필터링하지만 이 검색에서는 결과에 채워진 데이터만 포함되도록 명시적 필터 ($principal_user != "")가 필요합니다. 기본적으로 쿼리는 특정 사용자에 대해 하나 이상의 감지가 있는 경우에만 결과를 반환합니다.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_user"]
$principal_user != ""
$risk = detection.detection.risk_score
match:
$principal_user over 48h
outcome:
$risk_score = 90
$cumulative_risk = sum($risk)
$upstream_rules = array_distinct($rule_name)
condition:
$cumulative_risk > 500
대시보드
이 변형은 시간 경과에 따른 사용자 위험 및 탐지 활동을 표시하는 대시보드를 위해 특별히 설계되었습니다. 데이터를 개별 버킷으로 집계하므로 트리거된 고유 규칙의 수 또는 사용자당 총 감지 수와 같은 추세를 시각화하는 데 적합합니다.
이 쿼리에서는 기간이 슬라이딩 (홉) 기간에서 텀블링 윈도우 (by 48h)로 전환됩니다. 이렇게 하면 데이터 포인트가 겹치지 않는 시간 세그먼트에 매핑되어 시계열 차트의 시각화가 더 깔끔해집니다. 다른 집계되지 않은 검색과 마찬가지로 이벤트 변수는 필요하지 않으며 outcome 섹션이 확장되어 규칙 이름과 감지 ID 모두에 대한 개별 개수가 포함됩니다.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_user"]
$principal_user != ""
$risk = detection.detection.risk_score
match:
$principal_user by 48h
outcome:
$cumulative_risk = sum($risk)
$rule_count = count_distinct($rule_name)
$detection_count = count_distinct(detection.id)
condition:
$cumulative_risk > 500
전술 집계
사용 사례: 여러 개의 서로 다른 MITRE ATT&CK 전술에서 감지를 트리거한 사용자를 식별하여 공격 수명 주기가 진행되고 있음을 시사합니다 (예: 초기 액세스에서 유출로 전환).
주요 로직: count_distinct($tactic)를 사용하여 48시간 내에 사용자가 다양한 전략의 특정 기준점을 넘을 때만 트리거됩니다.
예: MITRE 전술 집계
규칙
rule composite_tactic_aggregation {
meta:
rule_name = "MITRE Tactic Aggregation Composite"
author = "Google Cloud Security"
description = "Composite detection that detects if a user has triggered detections over multiple mitre tactics."
severity = "Medium"
events:
$principal_user = $d.detection.detection.outcomes["principal_users"]
$tactic = $d.detection.detection.outcomes["mitre_tactic"]
$rule_name = $d.detection.detection.rule_name
match:
$principal_user over 48h
outcome:
$mitre_tactics_count = count_distinct($tactic)
$mitre_tactics = array_distinct($tactic)
$calculated_risk = 50 + (15 * $mitre_tactics_count)
$upstream_rules = array_distinct($rule_name)
condition:
$d and $mitre_tactics_count > 1 }
검색
다음 예는 기존 감지를 상호 연관시키고 동적 위험 가중치를 적용해야 하는 보안 개발자를 위해 설계된 검색 변형을 보여줍니다. 이 쿼리 로직은 detection 데이터 소스에서 MITRE ATT&CK 전술과 사용자 정보를 추출하고, 보안 주체 사용자를 기준으로 활동을 그룹화하고, 관찰된 전술의 다양성을 기반으로 맞춤 리스크 스코어링을 계산합니다.
detection.detection.outcomes.key = "principal_users"
detection.detection.outcomes.key = "mitre_tactic"
$principal_user = detection.detection.outcomes["principal_users"]
$tactic = detection.detection.outcomes["mitre_tactic"]
$rule_name = detection.detection.rule_name
match:
$principal_user
outcome:
$mitre_tactics_count = count_distinct($tactic)
$mitre_tactics = array_distinct($tactic)
$upstream_rules = array_distinct($rule_name)
$calculated_risk = 50 + (15 * $mitre_tactics_count)
$risk_score = if($calculated_risk > 100, 100, $calculated_risk)
대시보드
다음 예는 동일한 감지 분석 로직의 대시보드 변형을 보여줍니다. Google SecOps 대시보드 내에서 사용되는 이 쿼리를 통해 개발자는 다양한 규칙의 감지 결과를 상호 연관시켜 위험도가 높은 사용자를 시각화할 수 있습니다. 이 로직은 보안 주체 사용자와 MITRE 전술을 추출하고, 발견 사항을 합산하고, 상한 리스크 스코어링을 적용하여 대시보드 위젯 내에서 직접 조사 노력의 우선순위를 지정하는 데 도움을 줍니다.
detection.detection.outcomes.key = "principal_users"
detection.detection.outcomes.key = "mitre_tactic"
$principal_user = detection.detection.outcomes["principal_users"]
$tactic = detection.detection.outcomes["mitre_tactic"]
$rule_name = detection.detection.rule_name
match:
$principal_user
outcome:
$mitre_tactics_count = count_distinct($tactic)
$mitre_tactics = array_distinct($tactic)
$upstream_rules = array_distinct($rule_name)
$calculated_risk = 50 + (15 * $mitre_tactics_count)
$risk_score = if($calculated_risk > 100, 100, $calculated_risk)
```
순차적 복합 감지
사용 사례: 작업 순서가 중요한 주요 공격 패턴을 식별합니다. 예를 들어 동일한 IP 주소에서 일련의 무차별 대입 시도 알림이 발생한 후에만 발생하는 성공적인 계정 로그인을 감지합니다.
핵심 로직: 공통 변수 (예: $bruteforce_ip)에서 조인하고 타임스탬프 비교를 사용하여 이벤트가 올바른 순서로 발생했는지 확인하여 이전 감지와 후속 원시 UDM 이벤트를 상호 연관시킵니다.
순차적 복합 감지는 무차별 대입 로그인 시도 감지 후 성공적인 로그인과 같이 감지 순서가 중요한 관련 이벤트의 패턴을 식별합니다. 이러한 패턴에는 여러 기본 감지 또는 기본 감지와 이벤트의 조합이 포함될 수 있습니다.
예: 무차별 대입 시도 후 로그인 성공
규칙
다음 복합 규칙은 시퀀스가 중요한 관련 이벤트의 패턴을 식별합니다. 특히 Google Workspace 무차별 대입 감지 후 24시간 이내에 동일한 소스 IP에서 로그인 이벤트가 성공적으로 수행되는지 확인합니다.
rule composite_bruteforce_login {
meta:
rule_name = "Bruteforce Login Composite"
author = "Google Cloud Security"
description = "Detects when an IP address associated with a Workspace brute force attempt successfully logs in"
severity = "High"
events:
$bruteforce_detection.detection.detection.rule_name = /Workspace Anomalous Failed Logins/
$bruteforce_ip = $bruteforce_detection.detection.detection.variables["principal_ips"]
$login_event.metadata.product_name = "login"
$login_event.metadata.product_event_type = "login_success"
$login_event.metadata.vendor_name = "Google Workspace"
$login_ip = $login_event.principal.ip
// Ensure the brute force detection and successful login occurred from the same IP
$login_ip = $bruteforce_ip
$target_account = $login_event.target.user.email_addresses
// Ensure the brute force detection occurred before the successful login
$bruteforce_detection.detection.detection_time.seconds < $login_event.metadata.event_timestamp.seconds
match:
$bruteforce_ip over 24h
outcome:
$risk_score = 90
$principal_users = array_distinct($target_account)
condition:
$bruteforce_detection and $login_event
}
검색
$bruteforce_detection.detection.detection.rule_name = /Workspace Anomalous Failed Logins/
$bruteforce_ip = $bruteforce_detection.detection.detection.variables["principal_ips"]
$login_event.metadata.product_name = "login"
$login_event.metadata.product_event_type = "login_success"
$login_event.metadata.vendor_name = "Google Workspace"
$login_ip = $login_event.principal.ip
// Ensure the brute force detection and successful login occurred from the same IP
$login_ip = $bruteforce_ip
$target_account = $login_event.target.user.email_addresses
// Ensure the brute force detection occurred before the successful login
$bruteforce_detection.detection.detection_time.seconds < $login_event.metadata.event_timestamp.seconds
match:
$bruteforce_ip over 24h
outcome:
$principal_users = array_distinct($target_account)
condition:
$bruteforce_detection and $login_event
대시보드
대시보드는 원시 이벤트 데이터를 시각화하는 데 중점을 두는 반면, 복합 감지 로직은 기존 감지 알림을 후속 이벤트와 상호 연관시킵니다. 이 다층 분석은 실시간 대시보드 위젯이 아닌 감지 엔진에 최적화되어 있습니다.
컨텍스트 인식 탐지
사용 사례: 외부 위협 인텔리전스로 기존 감지를 보강하여 알림에 알려진 악성 항목이 포함되어 있는지 확인합니다. 예를 들어 보안 감지에서 신고된 IP 주소가 전역 TOR 종료 노드 위협 피드에도 나열되어 있는지 확인합니다.
핵심 로직: 복합 규칙을 사용하여 IP 주소와 같은 공유 속성을 일치시켜 감지 결과를 GLOBAL_CONTEXT 그래프 데이터 (예: Google Cloud 위협 인텔리전스 피드)와 조인합니다.
컨텍스트 인식 복합 감지는 위협 피드에서 발견된 IP 주소와 같은 추가 컨텍스트로 감지를 보강합니다.
예: 위협 인텔리전스 보강
규칙
다음 복합 규칙은 TOR 인텔 피드의 추가 컨텍스트를 기존 탐지에 자동으로 추가합니다. 이전 감지에서 발견된 IP 주소를 TOR 종료 노드 피드와 상호 연관시켜 발견 사항의 심각도와 리스크 스코어링을 높입니다.
rule composite_tor_enrichment {
meta:
rule_name = "Detection with IP from TOR Feed"
author = "Google Cloud Security"
description = "Adds additional context from the TOR intel feed to detections"
severity = "High"
events:
$rule_name = $d.detection.detection.rule_name
$gcti.graph.metadata.entity_type = "IP_ADDRESS"
$gcti.graph.metadata.vendor_name = "Google Cloud Threat Intelligence"
$gcti.graph.metadata.source_type = "GLOBAL_CONTEXT"
$gcti.graph.metadata.product_name = "GCTI Feed"
$gcti.graph.metadata.feed = "Tor Exit Nodes"
$detection_ip = $d.detection.detection.variables["principal_ips"]
$detection_ip = $gcti.graph.entity.ip
match:
$detection_ip, $rule_name over 1h
outcome:
$risk_score = 80
condition:
$d and $gcti
}
검색
$rule_name = $d.detection.detection.rule_name
$gcti.graph.metadata.entity_type = "IP_ADDRESS"
$gcti.graph.metadata.vendor_name = "Google Cloud Threat Intelligence"
$gcti.graph.metadata.source_type = "GLOBAL_CONTEXT"
$gcti.graph.metadata.product_name = "GCTI Feed"
$gcti.graph.metadata.feed = "Tor Exit Nodes"
$detection_ip = $d.detection.detection.variables["principal_ips"]
$detection_ip = $gcti.graph.entity.ip
match:
$detection_ip, $rule_name over 1h
condition:
$d and $gcti
대시보드
동시 발생 감지
사용 사례: 특정 기간 내에 동일한 항목에 의해 트리거된 관련 전술의 조합을 감지합니다. 예를 들어 48시간 이내에 권한 에스컬레이션 감지와 데이터 유출 감지를 모두 트리거한 사용자를 식별합니다.
키 로직: 집계 형식을 사용하여 match 섹션 내에서 공유 항목 변수 (예: $pe_user)에 조인하여 여러 고유 감지 유형을 상호 연결합니다.
동시 발생 복합 감지는 사용자가 트리거한 권한 에스컬레이션과 데이터 유출 감지의 조합과 같은 관련 이벤트의 조합을 감지할 수 있는 집계의 한 형태입니다.
예: 권한 에스컬레이션 및 유출 동시 발생
규칙
다음 복합 규칙은 48시간 동안 동일한 사용자와 연결된 특정 감지 시퀀스 또는 조합(권한 에스컬레이션 후 유출)을 검색합니다.
rule composite_privesc_exfil_sequential {
meta:
rule_name = "Privilege Escalation and Exfiltration Composite"
author = "Google Cloud Security"
description = "Looks for a detection sequence of privilege escalation followed by exfiltration."
severity = "High"
events:
$privilege_escalation.detection.detection.rule_labels["tactic"] = "TA0004"
$exfiltration.detection.detection.rule_labels["tactic"] = "TA0010"
$privesc_user = $privilege_escalation.detection.detection.variables["principal_users"]
$exfil_user = $exfiltration.detection.detection.variables["principal_users"]
$privesc_user = $exfil_user
$privilege_escalation.detection.detection_time.seconds < $exfiltration.detection.detection_time.seconds
match:
$privesc_user over 48h
outcome:
$risk_score = 75
$privesc_rules = array_distinct($privilege_escalation.detection.detection.rule_name)
$exfil_rules = array_distinct($exfiltration.detection.detection.rule_name)
condition:
$privilege_escalation and $exfiltration
}
검색
$privilege_escalation.detection.detection.rule_labels["tactic"] = "TA0004"
$exfiltration.detection.detection.rule_labels["tactic"] = "TA0010"
$privesc_user = $privilege_escalation.detection.detection.variables["principal_users"]
$exfil_user = $exfiltration.detection.detection.variables["principal_users"]
$privesc_user = $exfil_user
$privilege_escalation.detection.detection_time.seconds < $exfiltration.detection.detection_time.seconds
match:
$privesc_user over 48h
outcome:
$privesc_rules = array_distinct($privilege_escalation.detection.detection.rule_name)
$exfil_rules = array_distinct($exfiltration.detection.detection.rule_name)
condition:
$privilege_escalation and $exfiltration
대시보드
결과 및 변수 관리
이 섹션에서는 위험을 계산하고 다운스트림 소비를 위해 데이터를 정규화하는 예를 보여줍니다.
| 주제 | 예시 |
|---|---|
| 결과 조건부 | 계산된 리스크 스코어링으로 필터링 |
| 결과가 있는 단일 이벤트 쿼리 | 특정 시점 심각도 태그 지정 |
| 네트워크 기반 리스크 스코어링 | 네트워크 기반 리스크 스코어링 규칙 |
| 다중 이벤트 로직 리팩터링 (리팩터링 전) | 결과 리팩터링 (리팩터링 전) |
| 다중 이벤트 로직 리팩터링 (리팩터링 후) | 결과 리팩터링 (리팩터링 후) |
| 함수-자리표시자 할당 |
outcome 섹션이 있는 쿼리
YARA-L 2.0 규칙에 선택적인 outcome 섹션을 추가하여 각 발견 항목에 대한 추가 정보를 추출할 수 있습니다. condition 섹션에서 결과 변수에 조건부를 지정할 수도 있습니다. 감지 규칙의 outcome 섹션을 사용해서 다운스트림 소비에 대한 변수를 설정할 수 있습니다. 예를 들어 분석 중인 이벤트의 데이터를 기반으로 심각도 점수를 설정할 수 있습니다.
자세한 내용은 다음을 참조하세요.
결과 조건
사용 사례: 계산된 리스크 스코어링을 기반으로 감지를 필터링하여 노이즈를 줄이고 신뢰도가 높거나 심각도가 높은 이벤트만 알림을 트리거하도록 합니다. 이는 특정 비즈니스 기준점을 충족하지 않는 위험도가 낮은 활동을 억제하는 데 유용합니다.
핵심 로직: 조건부 수학 (예: 파일 크기 또는 시간대에 따라 위험 추가)을 사용하여 outcome 섹션에서 변수를 정의한 다음 condition 섹션에서 해당 변수를 참조하여 감지를 제어합니다.
예: 계산된 위험 점수로 필터링
규칙
condition 섹션에서 outcome 섹션에 정의된 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
}
검색
metadata.event_type = "FILE_COPY"
principal.file.size = $file_size
principal.hostname = $hostname
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week(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
대시보드
이 쿼리는 $hostname 결과 변수를 추가하여 각 리스크 스코어링과 연결된 호스트를 시각화합니다.
metadata.event_type = "FILE_COPY"
principal.file.size = $file_size
principal.hostname = $hostname
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week(metadata.collected_timestamp.seconds)
outcome:
$host = $hostname
$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
결과가 있는 단일 이벤트 쿼리
사용 사례: 시간대나 이벤트 상관관계 없이 사용자 목록 또는 파일 속성을 기반으로 심각도 태그를 할당하는 등 즉각적인 컨텍스트로 특정 시점 감지를 보강합니다.
핵심 로직: match 섹션이 없는 규칙에서 outcome 섹션을 사용합니다. 이렇게 하면 기준을 충족하는 모든 개별 이벤트에 대해 메타데이터를 추출하고 조건부 논리 (예: 참조 목록에 대한 사용자 확인)를 실행할 수 있습니다.
예: 특정 시점 심각도 태그 지정
규칙
다음 예에서는 단일 이벤트 규칙에서 outcome 섹션을 사용하여 다운스트림 소비를 위한 변수를 설정하는 방법을 보여줍니다. 예를 들어 파일 복사 이벤트에 관련된 특정 사용자 및 파일 크기에 따라 심각도 점수를 설정합니다.
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.user.userid in %admin_users, "SEVERE", "MODERATE")
$severity_tag = if($file_size > 1024, $admin_severity, "LOW")
condition:
$u
}
검색
다음 예에서는 파일 생성 이벤트를 식별하고 outcome 섹션을 사용하여 각 결과에 심각도 수준을 동적으로 할당합니다. 멀티 이벤트 규칙과 달리 이 집계되지 않은 검색에는 이벤트 변수나 match 섹션이 필요하지 않습니다. 대신 각 로그를 개별적으로 처리하여 파일 크기 및 사용자 권한을 기반으로 맞춤 로직이 적용된 1 row per event를 출력합니다.
metadata.event_type = "FILE_CREATION"
principal.file.size = $file_size
principal.hostname = $hostname
outcome:
$suspicious_host = $hostname
$admin_severity = if(principal.user.userid in %a1, "SEVERE", "MODERATE")
$severity_tag = if($file_size > 1024, $admin_severity, "LOW")
대시보드
기본 의도가 개별 이벤트를 태그하고 보강하는 것이므로 이 예시에는 대시보드 변형이 적용되지 않습니다. 대시보드에서 이러한 이벤트를 집계할 수 있지만 (예: 심각도 태그별 총 이벤트 수 계산) 이렇게 하면 집계되지 않은 이 검색에서 찾도록 설계된 세부적인 행 수준 세부정보가 가려집니다.
네트워크 기반 리스크 스코어링
사용 사례: 이벤트 그룹 전체의 누적 네트워크 트래픽을 계산하여 위험도가 높은 데이터 전송을 식별합니다. 이를 통해 총 데이터 기준점이 특정 한도 (예: 1024바이트)를 초과하는 위협을 식별하는 동시에 관련 애셋의 취약점 심각도를 고려할 수 있습니다.
핵심 로직: outcome 섹션에서 sum() 집계 함수를 사용하여 match 기간의 모든 이벤트에서 sent_bytes와 received_bytes를 결합합니다. 규칙의 경우 합계가 정의된 기준을 초과하면 더 높은 리스크 스코어링을 적용하기 위해 쿼리에서 if 문을 사용합니다.
예시: 네트워크 기반 리스크 스코어링 규칙
규칙
다음 예시에서는 outcome 섹션을 사용하여 네트워크 활동에 기반한 동적 리스크 스코어링을 계산하는 방법을 보여줍니다. 이벤트 그룹에서 전송된 총 바이트를 합산하여 규칙은 특정 데이터 기준점 (1024바이트)을 초과하는 일치 항목에 더 높은 우선순위를 적용하는 동시에 관련 애셋의 취약점 심각도를 고려합니다.
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")
}
검색
다음 예에서는 UDM 네트워크 이벤트를 항목 컨텍스트 그래프 (ECG)의 애셋 컨텍스트와 연관시키는 검색 변형을 보여줍니다. 5분 match 기간을 활용하여 호스트 이름별로 네트워크 트래픽을 집계하고, 데이터 볼륨과 취약점 심각도를 기반으로 위험 점수를 계산하며, 조건부 필터를 적용하여 최종 결과 집합에서 특정 애셋 ID를 제외합니다.
$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")
대시보드
다음 예시에서는 애셋 취약점 데이터로 실시간 네트워크 원격 분석을 보강하는 대시보드 변형을 보여줍니다. 이 쿼리는 5분 동안의 슬라이딩 윈도우에서 호스트 이름을 일치시켜 개발자가 애셋 위험 수준을 시각화하는 대시보드 위젯을 빌드할 수 있도록 합니다. 이 로직은 네트워크 처리량과 애셋에서 발견된 가장 심각한 취약점을 기반으로 리스크 스코어링을 동적으로 조정하여 잠재적으로 손상된 시스템의 우선순위가 지정된 뷰를 제공합니다.
$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")
멀티 이벤트 outcome 규칙 리팩터링 (리팩터링 전)
사용 사례: 멀티 이벤트 규칙을 단일 이벤트 규칙으로 변환하여 시스템 성능을 개선하고 처리 지연 시간을 줄입니다. 이는 결과 섹션을 사용 설정하기 위해 원래 일치 섹션으로만 설계되었지만 실제로 여러 개별 이벤트 간의 상관관계가 필요하지 않은 규칙에 적합합니다.
핵심 로직: outcome 섹션에서 match 섹션과 집계 함수 (예: max(), sum(), count())를 삭제합니다. 이 전환을 통해 규칙은 시간 경과에 따른 이벤트 그룹화에서 이벤트가 도착할 때마다 개별적으로 평가하는 방식으로 변경됩니다.
match 섹션이 없는 규칙) 및 멀티 이벤트 규칙 (match 섹션이 있는 규칙) 모두 섹션을 사용할 수 있습니다.
단일 이벤트 규칙(
섹션이 없는 규칙) 및 멀티 이벤트 규칙( 섹션이 있는 규칙) 모두 outcome 섹션을 사용할 수 있습니다. 이전에
결과 섹션을 사용할 수 있도록 규칙을 다중 이벤트로 설계한 경우, match 섹션을 삭제하여 해당 규칙을 선택적으로 리팩터링하면 성능을 개선할 수 있습니다. 규칙에 그룹화를 적용하는 match 섹션이 더 이상 없으므로 더 많은 감지가 수신될 수 있습니다.
예: 결과 리팩터링 (리팩터링 전)
규칙
다음 예시는 하나의 이벤트 변수만 사용하는 멀티 이벤트 결과 규칙을 보여줍니다. match 섹션을 사용하므로 규칙 엔진은 결과를 계산하기 전에 5분 동안 이벤트를 그룹화해야 하며, 이로 인해 단일 이벤트 평가보다 더 많은 리소스가 소모됩니다.
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
}
검색
통계 쿼리 동등 항목
events:
$u.udm.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
$risk_score = max(if($hostname = "my-hostname", 100, 50))
condition:
$u
대시보드
events:
$u.udm.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
$risk_score = max(if($hostname = "my-hostname", 100, 50))
condition:
$u
멀티 이벤트 outcome 규칙 리팩터링 (리팩터링 후)
사용 사례: 처리 속도를 개선하기 위해 쿼리 최적화 마무리 그룹화 요구사항을 삭제하면 이제 일치하는 단일 이벤트가 도착하는 즉시 쿼리가 감지를 트리거하므로 규칙 엔진에 훨씬 더 효율적입니다.
핵심 로직: match 섹션을 삭제하고 outcome 변수 할당에서 aggregate 함수 (예: max())를 삭제합니다. if 문 내의 논리는 동일하게 유지되지만 이제 그룹이 아닌 단일 이벤트에 적용됩니다.
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
}
검색
events:
$u.udm.principal.hostname = $hostname
outcome:
$risk_score = if($hostname = "my-hostname", 100, 50)
대시보드
events:
$u.udm.principal.hostname = $hostname
outcome:
$risk_score = if($hostname = "my-hostname", 100, 50)
함수-자리표시자 할당
사용 사례: 데이터를 정규화 (예: 이메일 도메인 표준화)하여 일치 섹션의 그룹화가 정확한지 확인합니다.
핵심 로직: re.capture() 또는 strings.concat()의 결과를 자리표시자 변수에 할당합니다.
예: 함수-자리표시자 변수 할당
함수 호출의 결과에 자리 표시자 변수를 할당하고 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.to , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// address@-> address@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 or $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
}
검색
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(network.email.from , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// address@??? -> address@company.com
$email_from_normalized = strings.concat(
re.capture(network.email.to , "(.*)@"),
"@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(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 or $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
대시보드
다음 예는 시계열 시각화에 최적화된 대시보드 변형을 보여줍니다. 분 단위 세부사항 대신 1일 텀블링 윈도우를 사용하면 이 쿼리는 장기간에 걸쳐 리스크 스코어링을 차트로 표시하는 데 적합한 안정적이고 중복되지 않는 데이터 포인트를 생성합니다. 이 로직은 이메일 항목을 정규화하고 주말 거래에 더 높은 위험 가중치를 적용하여 장기 모니터링을 위한 의심스러운 이메일 활동의 명확한 일일 추세를 제공합니다.
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(network.email.from , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// address@??? -> address@company.com
$email_from_normalized = strings.concat(
re.capture(network.email.to , "(.*)@"),
"@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(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 or $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
최적화 및 필터링
효과적인 규칙 최적화는 정확한 데이터 필터링을 통해 감지 엔진이 의미 있는 정보만 처리하도록 하는 데 달려 있습니다. '노이즈'가 있거나 불완전한 데이터를 제외하면 규칙 성능을 크게 개선하고 생성된 알림을 실행 가능하게 만들 수 있습니다.
| 주제 | 예시 |
|---|---|
| 0 값 제외 | 명시적 및 암시적 0값 제외 |
0 값 제외
사용 사례: 실행 가능한 보안 데이터를 제공하지 않는 빈 문자열, null 값 또는 일반 자리표시자 계정('게스트' 등)을 명시적으로 필터링하여 규칙 정확도를 높이고 오탐을 줄입니다.
핵심 로직: match 섹션에 사용된 변수에 대해 규칙 엔진의 암시적 0 값 필터링을 활용하는 동시에 다른 이벤트 필드에 명시적 부등호 연산자 (!= "")를 사용하여 값이 입력된 데이터만 감지를 트리거하도록 합니다.
규칙 엔진은 match 섹션에 사용된 모든 자리 표시자에 대해 0 값을 암시적으로 필터링합니다. 사용 중지하려면 allow_zero_values 옵션을 사용합니다. 하지만 다른 참조된 이벤트 필드의 경우 명시적으로 조건을 지정하지 않으면 0 값이 제외되지 않습니다. 자세한 내용은 일치 섹션의 0 값을 참조하세요.
예: 명시적 및 암시적 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
}
검색
match 섹션의 자리표시자에는 암시적 0 값 필터가 없으므로 hostname가 빈 문자열일 수 없음을 명시적으로 명시해야 합니다.
$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 and hostname cannot be empty string as explicitly specified.
$e2.target.asset_id != ""
$hostname != ""
match:
$hostname over 1h
대시보드
match 섹션의 자리표시자에 대한 암시적 0 값 필터가 없으므로 hostname가 빈 문자열일 수 없음을 명시적으로 지정해야 합니다.
$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 and hostname cannot be empty string as explicitly specified.
$e2.target.asset_id != ""
$hostname != ""
match:
$hostname over 1h
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가에게 문의하여 답변을 받으세요.