ルール一本を六回書き直す
한국어 원문으로 표시합니다.
목표
합성한 프로세스 실행 기록 24건을 놓고 Sigma 규칙을 여섯 편 씁니다. 매번 실제로 돌려 몇 건을 맞히고 몇 건을 놓쳤는지 숫자로 확인하면서 규칙을 좁혀 갑니다.
왜 중요한가
탐지 규칙이 현장에서 죽는 이유는 대개 문법이 아닙니다. 너무 넓어서 오탐이 쌓이면 사람들이 알림을 안 보게 되고, 너무 좁아서 주소 하나가 바뀌면 아무것도 못 잡습니다. 둘 사이를 찾는 유일한 방법은 자료에 돌려 보는 것입니다. 규칙을 쓰고 눈으로 읽어 "맞겠지" 하고 배포하는 습관이 탐지 공백의 가장 큰 원인입니다.
그래서 이 실습은 규칙을 쓸 때마다 사건 기록에 적용해 봅니다. 맞은 건과 잘못 잡은 건이 숫자로 나오면, "이 조건을 넣으면 무엇이 함께 들어오는가" 를 감이 아니라 관찰로 알게 됩니다. 이것이 탐지 엔지니어링이 보안 업무 중에서도 유난히 소프트웨어 공학을 닮은 이유입니다 — 규칙은 코드이고, 사건 기록은 시험 자료입니다.
단계
- 사건 기록을 부모 프로세스별로 세어
/root/sigma/01-parents.txt에 저장하세요. - cron 이 부모인 실행을 전부 잡는 최소 규칙을
/root/sigma/02-cron-any.yml에 쓰세요. - 그중 명령줄에
curl이나wget이 있는 것만 잡도록 좁혀/root/sigma/03-outbound.yml에 쓰세요. selection 은 하나만 씁니다. - 사내 저장소(
repo.corp.internal)로 보내는 정상 백업을 필터로 덜어 내/root/sigma/04-filtered.yml에 쓰세요.condition에not을 씁니다. - 주소를 쓰지 말고 행위로 잡는 규칙을
/root/sigma/05-behaviour.yml에 쓰세요. 받아 온 것을 셸에 파이프로 넘기는 것과 셸을 붙여 바깥으로 연결하는 것, 두 가지입니다.|re수식어를 반드시 씁니다. - 예약 작업의 외부 요청과 역방향 셸을
selection_으로 시작하는 두 선택으로 나누고1 of selection_*로 묶어/root/sigma/06-combined.yml에 쓰세요. 사내 저장소 필터는 공통으로 겁니다. - 6번 규칙에 배포용 메타데이터를 채워
/root/sigma/07-release.yml에 쓰세요. - 3번과 7번 규칙의 판정 결과를
/root/sigma/08-review.md에 숫자와 판단으로 적으세요.
참고
- 재료는
/opt/lab/fixtures/detection/proc_events.jsonl이고 한 줄이 사건 하나입니다.jq -r '.ParentImage' <파일>처럼 읽으세요. - 규칙을 돌려 보는 도구가 함께 있습니다.
python3 /opt/lab/fixtures/detection/sigma_eval.py --lint <규칙>은 구조만 보고,--ids <규칙> <사건파일>은 맞은 사건의 id 를 냅니다.--help로 지원 범위를 확인하세요. - 흔한 실수:
|contains로 짧은 낱말을 찾는 것.nc는rsync안에도 들어 있습니다. - 흔한 실수: selection 안에서 값을 리스트로 준 것을 AND 로 착각하는 것. 한 필드 안의 리스트는 OR 입니다.
- 6~7번의 진실 집합(실제 침해 4건)은 5번과 3번에서 본 것을 합치면 보입니다.
자료부터 훑는다
/opt/lab/fixtures/detection/proc_events.jsonl 을 부모 프로세스(ParentImage)별로 세어 /root/sigma/01-parents.txt 에 저장하세요. 한 줄에 경로 하나와 그 건수가 함께 있으면 됩니다.
jq -r '.ParentImage' 로 값만 뽑은 뒤 sort | uniq -c 로 셉니다. 규칙을 쓰기 전에 무엇이 얼마나 있는지 보는 습관을 들이는 단계입니다.
가장 작은 규칙
cron 이 부모인 실행을 전부 잡는 최소 Sigma 규칙을 /root/sigma/02-cron-any.yml 에 쓰세요. 필수 항목(title·logsource·detection)만 있으면 됩니다.
detection 아래에 이름 붙인 선택 하나를 두고 condition 에 그 이름을 적습니다. 사건의 ParentImage 값이 무엇인지는 1단계 결과에 있습니다.
바깥으로 나간 것만
2번 규칙을 좁혀 명령줄에 curl 이나 wget 이 있는 것만 잡게 /root/sigma/03-outbound.yml 에 쓰세요. detection 의 선택은 하나만 둡니다.
한 선택 안에서 필드를 두 개 적으면 AND 이고, 한 필드에 값을 리스트로 주면 OR 입니다. 부분 문자열을 보려면 필드 이름 뒤에 수식어를 붙입니다.
정상 하나를 덜어 낸다
3번 결과에서 사내 저장소(repo.corp.internal)로 보내는 정상 백업을 빼고 /root/sigma/04-filtered.yml 에 쓰세요. 선택을 좁히지 말고 필터를 따로 두어 condition 에 not 으로 붙입니다.
detection 아래에 선택을 두 개 두고 이름을 다르게 붙입니다. condition 은 selection and not <필터이름> 꼴이 됩니다.
주소가 아니라 행위로
받아 온 것을 셸에 파이프로 넘기는 것과, 셸을 붙여 바깥으로 연결하는 것 두 가지를 잡는 규칙을 /root/sigma/05-behaviour.yml 에 쓰세요. |re 수식어를 쓰고, 규칙 안에 목적지 주소를 적지 마세요.
정규식 수식어는 필드|re: '<정규식>' 꼴입니다. 파이프 문자와 -e /bin/sh 같은 조각이 무엇을 뜻하는지 1단계에서 본 명령줄을 다시 읽어 보세요. 두 갈래는 선택 두 개로 나누고 1 of 로 묶어도 됩니다.
두 갈래를 하나로
예약 작업의 외부 요청과 역방향 셸을 selection_ 으로 시작하는 선택 두 개로 나누고 1 of selection_* 로 묶어 /root/sigma/06-combined.yml 에 쓰세요. 사내 저장소 필터는 두 갈래 모두에 걸립니다.
1 of selection_* 는 이름이 그 패턴에 맞는 선택들을 OR 로 묶습니다. 필터는 그 묶음 전체에 and not 으로 붙입니다. 목표는 실제 침해 4건입니다.
배포할 수 있는 규칙으로
6번 규칙에 id·status·description·author·date·references·falsepositives·level·tags 를 채워 /root/sigma/07-release.yml 에 쓰세요. 잡는 사건은 6번과 같아야 합니다.
id 는 UUID 꼴이어야 합니다(python3 -c 'import uuid;print(uuid.uuid4())'). status 는 아직 운영 자료로 돌려 보지 않은 규칙의 값을 고르고, level 은 역방향 셸까지 잡는 규칙에 맞게 정하세요. tags 에는 attack. 으로 시작하는 항목이 하나 이상 필요합니다.
숫자로 남긴다
3번과 7번 규칙을 진실 집합(실제 침해 4건)과 대조해 /root/sigma/08-review.md 에 적으세요. 규칙마다 한 줄에 파일 이름과 matched=·tp=·fp=·fn= 네 값을 적고, ## 판단 절에 120자 이상으로 무엇을 내보낼지와 그 이유를 쓰세요.
값은 --ids 결과와 진실 집합을 비교해 직접 세면 됩니다. 진실 집합은 5단계에서 찾은 두 건과 3단계에서 찾은 것 중 사내 저장소가 아닌 것을 합친 것입니다.