ASIM 파서 만들기

완료됨

ASIM(Advanced Security Information Model) 사용자는 쿼리에서 테이블 이름 대신 통합 파서를 사용하여 데이터를 정규화된 형식으로 보고 스키마와 관련된 모든 데이터를 쿼리에 포함시킵니다. 그러면 통합 파서가 원본별 파서를 사용하여 각 원본의 특정 세부 정보를 처리합니다.

Microsoft Sentinel은 여러 데이터 원본에 대한 원본별 기본 제공 파서를 제공합니다. 다음과 같은 상황에서 이러한 원본별 파서를 수정하거나 개발할 수 있습니다.

디바이스가 ASIM 스키마에 맞는 이벤트를 제공하지만 디바이스 및 관련 스키마에 대한 원본별 파서를 Microsoft Sentinel에서 사용할 수 없습니다.

ASIM 원본별 파서를 디바이스에 사용할 수 있지만 디바이스가 이벤트를 보내는 방법 또는 형식이 ASIM 파서에 필요한 것과 다릅니다. 예:

표준이 아닌 방식으로 이벤트를 보내도록 원본 디바이스가 구성될 수 있습니다.

디바이스 버전이 ASIM 파서에서 지원하는 버전과 다를 수 있습니다.

이벤트를 중개자 시스템이 수집, 수정 및 전달할 수 있습니다.

사용자 지정 파서 개발 프로세스

다음 워크플로는 원본별 파서인 사용자 지정 ASIM을 개발하는 대략적인 단계를 설명합니다.

  1. 샘플 로그 수집.

  2. 원본에서 보낸 이벤트가 나타나는 스키마를 식별합니다.

  3. 원본 이벤트 필드를 식별된 스키마에 매핑합니다.

  4. 원본에 대한 하나 이상의 ASIM 파서를 개발합니다. 원본과 관련된 각 스키마에 대해 필터링 파서와 매개 변수가 없는 파서를 개발해야 합니다.

  5. 파서를 테스트합니다.

  6. Microsoft Sentinel 작업 영역에 파서를 배포합니다.

  7. 새로운 사용자 지정 파서를 참조하도록 관련 ASIM 통합 파서를 업데이트합니다.

  8. 기본 ASIM 배포에 파서를 제공할 수도 있습니다. 적용된 파서는 모든 작업 영역에서 기본 제공 파서로 사용할 수도 있습니다.

샘플 로그 수집

효과적인 ASIM 파서를 빌드하려면 대표적인 로그 집합이 필요하며, 대부분의 경우 원본 시스템을 설정하고 이를 Microsoft Sentinel에 연결해야 합니다. 원본 디바이스를 사용할 수 없는 경우 클라우드 종량제 서비스를 사용하여 개발 및 테스트를 위해 많은 디바이스를 배포할 수 있습니다.

또한 로그에 대한 공급업체 설명서 및 샘플을 찾는 것은 광범위한 로그 형식 범위를 보장함으로써 개발을 가속화하고 실수를 줄이는 데 도움이 될 수 있습니다.

대표적인 로그 집합에는 다음이 포함되어야 합니다.

  • 이벤트 결과가 다른 이벤트.
  • 대응 작업이 다른 이벤트.
  • 사용자 이름, 호스트 이름 및 ID, 값 정규화가 필요한 기타 필드에 대한 다양한 형식.

매핑

파서를 개발하기 전에 원본 이벤트에서 사용 가능한 정보를 식별한 스키마에 매핑합니다.

  • 모든 필수 필드와 권장되는 필드도 매핑합니다.
  • 원본에서 사용 가능한 모든 정보를 정규화된 필드에 매핑해 보세요. 선택한 스키마의 일부로 사용할 수 없는 경우 다른 스키마에서 사용할 수 있는 필드에 매핑하는 것을 고려합니다.
  • 원본의 필드 값을 ASIM에서 허용하는 정규화된 값에 매핑합니다. 원래 값은 EventOriginalResultDetails와 같은 별도의 필드에 저장됩니다.

파서 개발

각각의 관련 스키마에 대해 필터링 및 매개 변수 없는 파서를 모두 개발합니다.

사용자 지정 파서는 Microsoft Sentinel 로그 페이지에서 개발된 KQL 쿼리 입니다. 파서 쿼리는 다음과 같은 세 부분으로 구성됩니다.

필터 > 구문 분석 > 준비 필드

관련 레코드 필터링

대부분의 경우 Microsoft Sentinel의 테이블에는 여러 유형의 이벤트가 포함됩니다. 예를 들면 다음과 같습니다.

  • Syslog 테이블에는 여러 원본의 데이터가 있습니다.
  • 사용자 지정 테이블은 둘 이상의 이벤트 유형을 제공하는 단일 원본의 데이터를 포함하고 다양한 스키마에 적합할 수 있습니다.

따라서 파서는 먼저 대상 스키마와 관련된 레코드만 필터링해야 합니다.

KQL의 필터링은 where 연산자를 사용하여 수행합니다. 예를 들어 Sysmon event 1은 프로세스 생성을 보고하며 따라서 ProcessEvent 스키마로 정규화됩니다. Sysmon event 1 이벤트는 Event 테이블에 포함되므로 다음 필터를 사용해야 합니다.

Event | where Source == "Microsoft-Windows-Sysmon" and EventID == 1

중요

파서는 시간으로 필터링하면 안 됩니다. 파서를 사용하는 쿼리는 시간 범위를 적용합니다.

관심 목록을 사용하여 원본 유형별 필터링

이벤트 자체에는 특정 원본 유형에 대한 필터링을 허용하는 정보가 포함되지 않는 경우가 있습니다.

예를 들어 Infoblox DNS 이벤트는 Syslog 메시지로 전송되며, 다른 원본에서 전송된 Syslog 메시지와 구별하기 어렵습니다. 이러한 경우 파서는 관련 이벤트를 정의하는 원본 목록을 사용합니다. 이 목록은 ASimSourceType 관심 목록에 유지됩니다.

파서에서 ASimSourceType 관심 목록을 사용하려면 다음을 수행합니다.

  • 파서의 시작 부분에 다음 줄을 포함합니다.
let Sources_by_SourceType=(sourcetype:string){_GetWatchlist('ASimSourceType') | where SearchKey == tostring(sourcetype) | extend Source=column_ifexists('Source','') | where isnotempty(Source)| distinct Source };
  • 파서 필터링 섹션에서 관심 목록을 사용하는 필터를 추가합니다. 예를 들어 Infoblox DNS 파서의 필터링 섹션에는 다음이 포함됩니다.
| where Computer in (Sources_by_SourceType('InfobloxNIOS'))

파서에서 이 샘플을 사용하려면 다음을 수행합니다.

  • Computer를 원본에 대한 원본 정보를 포함하는 필드 이름으로 바꿉니다. Syslog 기반의 파서인 경우 Computer로 두어도 됩니다.

  • InfobloxNIOS 토큰을 파서에 원하는 값으로 바꿉니다. 파서 사용자에게 ASimSourceType 관심 목록을 선택한 값으로 업데이트하고, 이 유형의 이벤트를 보내는 원본 목록을 업데이트해야 한다고 알립니다.

파서 매개 변수를 기반으로 필터링

필터링 파서를 개발할 때, 해당 스키마에 대한 참조 문서에 설명된 대로 파서가 관련 스키마에 대한 필터링 매개 변수를 허용하는지 확인해야 합니다. 기존 파서를 시작점으로 사용하면 파서에 올바른 함수 서명이 포함됩니다. 대부분의 경우 실제 필터링 코드는 동일한 스키마에 대한 파서를 필터링하는 경우에도 유사합니다.

필터링할 때 다음 사항을 지켜야 합니다.

  • 실제 필드를 사용하여 구문 분석하기 전에 필터링합니다. 필터링된 결과가 충분히 정확하지 않은 경우 구문 분석 후 테스트를 반복하여 결과를 미세 조정합니다. 자세한 내용은 필터링 최적화를 참조하세요.
  • 매개 변수가 정의되지 않았고 여전히 기본값을 사용하는 경우 필터링하지 마세요.

다음 예제에서는 기본값이 일반적으로 '*'인 문자열 매개 변수와 기본값이 일반적으로 빈 목록인 목록 매개 변수에 대한 필터링을 구현하는 방법을 보여줍니다.

srcipaddr=='*' or ClientIP==srcipaddr
array_length(domain_has_any) == 0 or Name has_any (domain_has_any)

필터링 최적화

파서의 성능을 보장하려면 다음 필터링 권장 사항을 참고하세요.

  • 항상 구문 분석된 필드가 아닌 기본 제공 필드를 기준으로 필터링합니다. 구문 분석된 필드를 사용하여 필터링하는 것이 더 쉬운 경우도 있지만 성능에 큰 영향을 줍니다.
  • 최적화된 성능을 제공하는 연산자를 사용합니다. 특히 ==, has 및 startswith가 여기에 해당합니다. contains 또는 matches regex와 같은 연산자를 사용해도 성능에 큰 영향을 미칩니다.

성능에 대한 필터링 권장 사항을 따르기 어려울 때도 있습니다. 예를 들어, has를 사용하는 것은 contains보다 덜 정확합니다. 다른 경우에는 SyslogMessage와 같은 기본 제공 필드와 일치시키는 것은 DvcAction과 같은 추출된 필드와 비교하는 것보다 덜 정확합니다. 이러한 경우에는 기본 제공 필드에 대해 성능 최적화 연산자를 사용하여 미리 필터링하고 구문 분석 후에 더 정확한 조건을 사용하여 필터를 반복하는 것이 좋습니다.

예제를 보려면 다음 Infoblox DNS 파서 코드 조각을 참조하세요. 파서는 먼저 SyslogMessage 필드에 단어 client가 있는지 확인합니다. 그러나 이 용어는 메시지의 다른 위치에서 사용될 수 있으므로 Log_Type 필드를 구문 분석한 후 파서는 해당 단어 client가 실제로 필드의 값이었는지 다시 확인합니다.

Syslog | where ProcessName == "named" and SyslogMessage has "client"
…
      | extend Log_Type = tostring(Parser[1]),
      | where Log_Type == "client"

구문 분석

쿼리에서 관련 레코드를 선택한 후에는 해당 레코드를 구문 분석해야 할 수 있습니다. 일반적으로 단일 텍스트 필드에서 여러 이벤트 필드가 전달되는 경우 구문 분석이 필요합니다.

구문 분석을 수행하는 KQL 연산자는 성능 최적화를 기준으로 정렬되어 아래에 나열됩니다. 첫 번째는 가장 많이 최적화된 성능을 제공하는 반면, 마지막은 가장 덜 최적화된 성능을 제공합니다.

연산자 Description
분할 구분된 값의 문자열을 구문 분석합니다.
parse_csv CSV(쉼표로 구분된 값) 줄로 형식이 지정된 값의 문자열을 구문 분석합니다.
parse 패턴을 사용하여 임의 문자열에서 여러 값을 구문 분석합니다. 이 패턴은 성능이 향상된 단순화된 패턴이거나 정규식일 수 있습니다.
extract_all 정규식을 사용하여 임의 문자열에서 단일 값을 구문 분석합니다. 정규식을 사용하는 경우 extract_all은 parse와 비슷한 성능을 나타냅니다.
extract 정규식을 사용하여 임의 문자열에서 단일 값을 추출합니다. extract를 사용하면 단일 값이 필요한 경우 parse 또는 extract_all보다 더 나은 성능이 제공됩니다. 그러나 동일한 원본 문자열에서 extract의 여러 활성화를 사용하는 것은 단일 parse 또는 extract_all보다 덜 효율적이므로 피해야 합니다.
parse_json JSON으로 형식이 지정된 문자열의 값을 구문 분석합니다. JSON에서 몇 개의 값만 필요한 경우 parse, extract 또는 extract_all을 사용하면 더 나은 성능이 제공됩니다.
parse_xml XML로 형식이 지정된 문자열의 값을 구문 분석합니다. XML에서 몇 개의 값만 필요한 경우 parse, extract 또는 extract_all을 사용하면 더 나은 성능이 제공됩니다.

문자열 구문 분석 외에도 구문 분석 단계에서 다음을 포함하여 원래 값을 더 많이 처리해야 할 수 있습니다.

  • 형식 지정 및 형식 변환. 원본 필드는 추출된 후 대상 스키마 필드에 맞게 형식을 지정해야 할 수 있습니다. 예를 들어, 날짜와 시간을 나타내는 문자열을 날짜/시간 필드로 변환해야 할 수 있습니다. 이러한 경우 todatetime 및 tohex와 같은 함수가 유용합니다.

  • 값 조회. 원본 필드의 값은 추출된 후 대상 스키마 필드에 대해 지정된 값 세트에 매핑해야 할 수 있습니다. 예를 들어, 일부 원본은 숫자 DNS 응답 코드를 보고하지만 스키마는 더 일반적인 텍스트 응답 코드를 요구합니다. iff 및 case 함수는 소수의 값을 매핑하는 데 유용할 수 있습니다.

    예를 들어, Microsoft DNS 파서는 다음과 같이 iff 문을 사용하여 이벤트 ID와 응답 코드를 기반으로 EventResult 필드를 할당합니다.

    extend EventResult = iff(EventId==257 and ResponseCode==0 ,'Success','Failure')
    

    여러 값의 경우 동일한 DNS 파서에서 설명된 대로 datatable 및 lookup을 사용합니다.

    let RCodeTable = datatable(ResponseCode:int,ResponseCodeName:string) [ 0, 'NOERROR', 1, 'FORMERR'....];
    ...
     | lookup RCodeTable on ResponseCode
     | extend EventResultDetails = case (
     isnotempty(ResponseCodeName), ResponseCodeName,
     ResponseCode between (3841 .. 4095), 'Reserved for Private Use',
     'Unassigned')
    

매핑 값

많은 경우 추출된 원래 값을 정규화해야 합니다. 예를 들어 ASIM에서 MAC 주소는 콜론을 구분 기호로 사용하는 반면 원본은 하이픈으로 구분된 MAC 주소를 보낼 수 있습니다. 값을 변환하는 기본 연산자는 extend이며, 위의 파싱 섹션에서 설명한 것처럼 광범위한 KQL 문자열, 숫자 및 날짜 함수 집합과 함께 사용됩니다.

값 집합을 대상 필드에서 허용하는 값에 매핑해야 하는 경우 case, iff 및 lookup 문을 사용합니다.

각 원본 값이 대상 값에 매핑될 때 매핑할 datatable 연산자와 lookup을 사용하여 매핑을 정의합니다. 예를 들면 다음과 같습니다.

let NetworkProtocolLookup = datatable(Proto:real, NetworkProtocol:string)[
        6, 'TCP',
        17, 'UDP'
   ];
    let DnsResponseCodeLookup=datatable(DnsResponseCode:int,DnsResponseCodeName:string)[
      0,'NOERROR',
      1,'FORMERR',
      2,'SERVFAIL',
      3,'NXDOMAIN',
      ...
   ];
   ...
   | lookup DnsResponseCodeLookup on DnsResponseCode
   | lookup NetworkProtocolLookup on Proto

매핑에 가능한 값이 두 개뿐인 경우에도 조회가 유용하고 효율적입니다.

매핑 조건이 더 복잡한 경우 iff 또는 case 함수를 사용합니다. iff 함수를 사용하면 다음 두 값을 매핑할 수 있습니다.

| extend EventResult = 
      iff(EventId==257 and ResponseCode==0,'Success','Failure’)

case 함수는 두 개 이상의 대상 값을 지원합니다. 아래 예는 lookupcase를 결합하는 방법을 보여 줍니다. 위의 lookup 예는 lookup 값을 찾을 수 없는 경우 DnsResponseCodeName 필드에 빈 값을 반환합니다. 아래의 case 예는 가능한 경우 lookup 작업의 결과를 사용하고 그렇지 않은 경우 추가 조건을 지정하여 이를 보강합니다.

| extend DnsResponseCodeName = 
      case (
        DnsResponseCodeName != "", DnsResponseCodeName,
        DnsResponseCode between (3841 .. 4095), 'Reserved for Private Use',
        'Unassigned'
      )

결과 집합에서 필드 준비

파서는 정규화된 필드가 사용되는지 확인하기 위해 결과 집합에서 필드를 준비해야 합니다.

다음 KQL 연산자는 결과 집합의 필드를 준비하는 데 사용됩니다.

연산자 설명 파서에서 사용하는 경우
project-rename 필드 이름을 바꿉니다. 필드가 실제 이벤트에 있고 이름만 바꿔야 하는 경우 project-rename을 사용합니다. 이름을 바꾼 필드는 계속 기본 제공 필드처럼 동작하고 필드에 대한 작업은 훨씬 더 나은 성능을 제공합니다.
project-away 필드를 제거합니다. 결과 집합에서 제거하려는 특정 필드에는 project-away를 사용합니다. 혼동을 일으키거나 매우 크고 성능에 영향을 미칠 수 있는 경우가 아니면 결과 집합에서 정규화되지 않은 원래 필드를 제거하지 않는 것이 좋습니다.
프로젝트 이전에 존재했거나 명령문의 일부로 만들어진 필드를 선택하고, 그 외의 필드를 모두 제거합니다. 파서가 정규화되지 않은 다른 필드를 제거하지 않아야 하므로 파서에서 사용하지 않는 것이 좋습니다. 구문 분석 중에 사용되는 임시 값과 같은 특정 필드를 제거해야 하는 경우에는 project-away를 사용하여 결과에서 제거합니다.
extend 별칭을 추가합니다. 계산된 필드를 생성하는 역할 외에도 extend 연산자는 별칭을 만드는 데도 사용됩니다.

구문 분석 변형 처리

대부분의 경우 이벤트 스트림의 이벤트는 다른 구문 분석 논리가 필요한 변형을 포함합니다. 단일 파서에서 다양한 변형을 구문 분석하려면 iff 및 case와 같은 조건문을 사용하거나 통합 구조를 사용합니다.

union을 사용하여 여러 변형을 처리하려면 각 변형에 대해 별도의 함수를 만들고 결합 문을 사용하여 결과를 결합합니다.

let AzureFirewallNetworkRuleLogs = AzureDiagnostics
    | where Category == "AzureFirewallNetworkRule"
    | where isnotempty(msg_s);
let parseLogs = AzureFirewallNetworkRuleLogs
    | where msg_s has_any("TCP", "UDP")
    | parse-where
        msg_s with           networkProtocol:string 
        " request from "     srcIpAddr:string
        ":"                  srcPortNumber:int
    …
    | project-away msg_s;
let parseLogsWithUrls = AzureFirewallNetworkRuleLogs
    | where msg_s has_all ("Url:","ThreatIntel:")
    | parse-where
        msg_s with           networkProtocol:string 
        " request from "     srcIpAddr:string
        " to "               dstIpAddr:string
    …
union parseLogs,  parseLogsWithUrls…

중복 이벤트 및 과도한 처리를 방지하려면 네이티브 필드를 사용하여 구문 분석하려는 이벤트만 필터링하여 각 함수를 시작해야 합니다. 또한 필요한 경우 공용 구조체를 사용하기 전에 각 지점에서 project-away를 사용합니다.

파서 배포

파서를 Azure Monitor 로그 페이지에 복사하고 쿼리를 함수로 저장하여 수동으로 배포합니다. 이 방법은 테스트에 유용합니다. 자세한 내용은 함수 만들기를 참조하세요.

파서를 대량으로 배포하려면 다음과 같이 파서 ARM 템플릿을 사용하는 것이 좋습니다.

  1. 각 스키마에 대한 관련 템플릿을 기반으로 YAML 파일을 만들고 이 파일에 쿼리를 포함시킵니다. 스키마 및 파서 형식, 필터링 또는 매개 변수 없는 파서와 관련된 YAML 템플릿으로 시작합니다.

  2. ASIM Yaml-ARM 템플릿 변환기를 사용하여 YAML 파일을 ARM 템플릿으로 변환합니다.

  3. 업데이트를 배포하는 경우 포털 또는 함수 삭제 PowerShell 도구를 사용하여 이전 버전의 함수를 삭제합니다.

  4. Azure Portal 또는 PowerShell을 사용하여 템플릿을 배포합니다.

연결된 템플릿을 사용하여 여러 템플릿을 단일 배포 프로세스에 결합할 수도 있습니다.