LabHub
배우기 러닝패스 코스

Cron Ran curl at Three in the Morning

A rule is a data structure, not a sentence

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

Sigma 는 "이 로그에서 이런 것을 찾아라" 를 도구와 무관한 YAML 로 적는 형식이다. 필수 항목은 title·logsource·detection 셋뿐이고, 탐지의 본체는 detection 안의 이름 붙은 선택(selection)들과 그것을 조립하는 condition 한 줄이다.

Concept map: 도구와 무관한 YAML · 규칙을 공유할 수 없다 · 코드 · 매핑과 리스트가 뜻이 다르다

왜 이게 필요했나

탐지 규칙은 도구마다 문법이 다르다. 같은 "예약 작업이 바깥으로 요청했다" 를 검색 엔진에서는 질의문으로, 로그 파이프라인에서는 필터로, EDR 에서는 자기 화면의 폼으로 적는다. 회사를 옮기면 규칙을 처음부터 다시 쓴다. 더 나쁜 것은 규칙을 공유할 수 없다는 점이다. 어떤 팀이 새 수법을 잡는 규칙을 만들어도 다른 팀은 자기 도구 말로 옮겨 적어야 하고, 옮기면서 조건 하나가 조용히 빠진다.

Sigma 는 그 중간에 형식을 하나 둔다. 규칙은 YAML 로 쓰고, 백엔드가 그것을 각 도구의 질의문으로 번역한다. 그래서 규칙은 사람이 읽고 리뷰하고 git 에 올리는 코드가 되고, 도구는 갈아 끼울 수 있는 뒷단이 된다.

어떻게 동작하나

규칙 한 편의 뼈대는 이렇다.

title: 예약 작업이 사외로 요청했다
id: 8b2f5f4a-1d3c-4c22-9f0e-6a7b1c2d3e4f
status: test
description: cron 밑에서 curl·wget 이 실행되고 목적지가 사내가 아닌 경우
logsource:
  product: linux
  category: process_creation
detection:
  selection:
    ParentImage|endswith: '/cron'
    CommandLine|contains:
      - 'curl'
      - 'wget'
  filter_internal:
    CommandLine|contains: 'repo.corp.internal'
  condition: selection and not filter_internal
level: high

규칙 문서가 못 박는 규칙은 세 가지다. title·logsource·detection 은 필수이고, statusstable·test·experimental·deprecated·unsupported 중 하나이며, levelcritical·high·medium·low·informational 중 하나다. logsourcecategory(웹서버·방화벽 같은 갈래)·product(windows·linux 같은 제품)·service(그 제품 안의 서비스)로 어떤 로그를 대상으로 하는지 좁힌다(로그 원천 문서).

가장 헷갈리는 자리는 매핑과 리스트가 뜻이 다르다는 점이다. 선택 안에서 필드 여러 개를 나열하면 AND 이고, 한 필드에 값을 여러 개 주면 OR 이다. 위 예에서 ParentImageCommandLine 은 둘 다 맞아야 하지만 curlwget 은 둘 중 하나만 맞으면 된다.

condition 은 그 선택들을 조립하는 작은 언어다(조건 문서). and·or·not 과 괄호를 쓰고, 이름이 여러 개일 때는 1 of selection_*(하나라도)과 all of selection_*(전부)로 묶는다. 1 of them·all of them 도 있지만 문서는 공유용 규칙에서 이 둘을 권하지 않는다 — 나중에 선택 하나를 더했을 때 규칙의 뜻이 소리 없이 바뀌기 때문이다.

값을 비교하는 방법은 필드 이름 뒤의 수식어가 정한다(수식어 문서). |contains·|startswith·|endswith·|re 가 자주 쓰이고, |contains|all 처럼 이어 붙이면 "나열한 값이 전부 들어 있어야 한다" 가 된다. 여기서 초보자가 반드시 한 번은 데는 곳이 |contains 의 낱말 경계다. nc 를 찾으려고 CommandLine|contains: 'nc' 라고 쓰면 rsync 가 걸린다. 문자열 안에 n 다음에 c 가 있기 때문이다.

현장에서 만나는 모습

현장에서 규칙이 죽는 이유는 대개 둘 중 하나다. 하나는 오탐이 많아서 아무도 안 본다. 처음에는 알림이 울리면 사람들이 확인하지만, 열 번 중 아홉 번이 정상 백업 작업이면 열한 번째부터는 아무도 열어 보지 않는다. 그래서 규칙을 넓게 써 두고 "일단 다 잡고 나중에 줄이자" 는 계획은 거의 언제나 실패한다.

다른 하나는 주소를 규칙에 박아 두는 것이다. 이번 사건의 목적지가 cdn.updates-cache.net 이었다고 그 문자열을 규칙에 넣으면, 다음 주에 도메인이 바뀌는 순간 규칙은 아무것도 못 잡는다. 오래 사는 규칙은 주소가 아니라 행위를 적는다 — 받아 온 것을 곧바로 셸에 파이프로 넘긴다든가, 셸을 붙여서 바깥으로 연결한다든가.

그리고 메타데이터를 장식으로 여기지 않는 것이 중요하다. falsepositives 에 "정상 백업 작업" 이라고 한 줄 적혀 있으면 새벽 세 시에 호출받은 사람이 3분 만에 판단한다. 그 줄이 없으면 30분을 쓴다. referencestags 도 같은 값을 한다.

다음 실습에서 할 것

실습에서는 합성한 프로세스 실행 기록 24건을 받아 규칙을 여섯 편 쓴다. 가장 작은 규칙에서 시작해 점점 좁히고, 정상 백업 작업을 필터로 덜어 내고, 주소 대신 행위로 잡는 규칙을 쓰고, 두 갈래를 1 of 로 묶는다. 규칙은 그때마다 실제로 사건 기록에 돌려 보고, 몇 건을 맞히고 몇 건을 놓쳤는지 숫자로 확인한다. 이 실습은 파일만 다루므로 실습 파드에서 돈다.