Splunk for CSIRT: 로그 온보딩부터 위협 헌팅까지
CIM 정규화, SPL 탐지 쿼리, tstats, 위험 기반 탐지와 사고 타임라인 구축 실무
TL;DR
Splunk를 보안에 활용할 때 가장 어려운 부분은 SPL 문법이 아니다. 정확한 시간, 일관된 필드, 충분한 보존기간, 데이터 누락 감시가 먼저다. 탐지 쿼리는 이 기반 위에서만 의미가 있다.
이 글의 목표는 다음 흐름을 실제 운영 가능한 형태로 만드는 것이다.
source
└─ collection
└─ parsing·timestamp
└─ index·retention
└─ CIM normalization
└─ detection
└─ risk/notable
└─ investigation timeline
아래 SPL은 시작점이다. 실제 index, sourcetype, event code와 field는 조직의 Add-on 및 CIM mapping에 맞게 검증해야 한다.
1. 탐지보다 먼저 데이터 계약을 만든다
각 데이터 소스에 다음 항목을 문서화한다.
| 항목 | 예시 | 실패 시 영향 |
|---|---|---|
| owner | IAM 팀 | 장애·스키마 변경 대응 불가 |
| transport | UF, HEC, syslog relay | 유실 구간 판단 불가 |
| index/sourcetype | winevent, WinEventLog:Security |
검색 범위·파싱 불일치 |
| timestamp/timezone | event time, UTC+9 | 공격 타임라인 왜곡 |
| required fields | user, src, dest, action |
탐지 조건 실패 |
| expected volume | 5–8 GB/day | 수집 중단·폭증 미탐 |
| retention | hot 30d, searchable 365d | dwell time 조사 불가 |
| sensitive fields | command line, email, URL | 접근통제·마스킹 필요 |
Splunk의 기본 필드 중 역할이 자주 혼동되는 세 가지는 다음과 같다.
host: 이벤트가 발생한 장치source: 파일 경로, stream 또는 inputsourcetype: 이벤트 형식과 parsing 규칙
같은 형식의 Windows 이벤트가 여러 서버에서 들어오더라도 sourcetype은 같고 host가 달라야 한다.
2. 수집 상태를 탐지한다
보안 로그가 끊기면 모든 탐지 규칙이 조용히 성공한 것처럼 보인다. 먼저 sourcetype별 마지막 수집 시각을 감시한다.
| metadata type=sourcetypes index=*
| eval lag_minutes=round((now()-recentTime)/60,1)
| where lag_minutes > 15
| convert ctime(recentTime)
| table sourcetype totalCount recentTime lag_minutes
| sort - lag_minutes
metadata는 빠르지만 역할의 index 접근권한과 환경에 따라 결과 범위가 달라진다. 중요 source는 실제 이벤트 검색과 ingestion delay도 별도로 본다.
index=winevent earliest=-30m
| eval ingest_delay_sec=_indextime-_time
| stats count p50(ingest_delay_sec) AS p50
p95(ingest_delay_sec) AS p95
max(ingest_delay_sec) AS max BY host sourcetype
| where count < 10 OR p95 > 300
이 쿼리는 로그가 늦게 도착하는지 확인한다. 장치 시간 오류와 batch forwarding도 지연으로 보일 수 있으므로 NTP 상태와 수집 방식을 함께 확인한다.
3. CIM은 필드 이름을 통일하는 계약이다
Common Information Model(CIM)은 서로 다른 제품의 로그를 Authentication, Endpoint, Network_Traffic 같은 공통 data model과 field로 정규화한다.
예를 들어 VPN과 Windows 로그가 서로 다른 원본 필드를 사용해도 최종적으로 다음 의미가 일치해야 한다.
| 의미 | CIM field |
|---|---|
| 행위 주체 | user |
| 출발지 | src, src_ip |
| 대상 | dest, dest_ip |
| 결과 | action |
| 애플리케이션 | app |
필드가 존재한다고 mapping이 정확한 것은 아니다. 다음을 표본 검증한다.
- 성공과 실패가 올바른
action으로 매핑되는가 - NAT 이전·이후 IP 중 무엇이
src인가 - service account와 computer account가
user에 섞이는가 - hostname의 short/FQDN 표기가 통일되는가
- vendor field가 손실되지 않고 원본에도 남아 있는가
Data Model Acceleration은 검색을 빠르게 하지만 indexer 저장공간과 background search 부하를 사용한다. 모든 model을 무조건 가속하지 말고 탐지 사용량, summary range와 build completion을 모니터링한다.
4. SPL 검색 성능의 기본
느린 검색은 대개 너무 많은 이벤트를 가져온 뒤 필터링한다.
index=winevent sourcetype=WinEventLog:Security EventCode=4625 earliest=-15m
| fields _time host user src_ip Logon_Type
| stats count dc(host) AS dest_count values(host) AS dest BY user src_ip
성능 원칙은 단순하다.
- 가장 좁은 time range를 사용한다.
- 첫 구문에
index,sourcetype과 선택도가 높은 조건을 넣는다. - 필요한 field만 유지한다.
join, leading wildcard, 광범위한NOT은 사용 이유를 검토한다.- CIM 가속 모델은
tstats로 집계한다.
가속된 Authentication data model의 예시:
| tstats summariesonly=t count min(_time) AS first max(_time) AS last
FROM datamodel=Authentication.Authentication
WHERE Authentication.action=failure
BY Authentication.user Authentication.src Authentication.dest span=5m
| rename Authentication.* AS *
| where count >= 10
| convert ctime(first) ctime(last)
summariesonly=t는 가속 summary에 아직 포함되지 않은 최신 이벤트를 놓칠 수 있다. 지연 허용 범위와 acceleration health를 확인하고 필요하면 raw search 또는 summariesonly=f와 결과를 비교한다.
5. 비밀번호 스프레이 탐지
Password spraying은 하나의 source가 여러 계정에 적은 횟수로 실패하므로 “계정별 100회 실패” 규칙을 피할 수 있다.
| tstats count dc(Authentication.user) AS user_count
values(Authentication.user) AS users
min(_time) AS first max(_time) AS last
FROM datamodel=Authentication.Authentication
WHERE Authentication.action=failure
BY Authentication.src span=10m
| rename Authentication.* AS *
| where user_count >= 10 AND count >= 15
| eval duration=last-first
| convert ctime(first) ctime(last)
분석가는 이후 동일 source의 성공 이벤트를 연결한다.
| tstats count min(_time) AS first max(_time) AS last
FROM datamodel=Authentication.Authentication
WHERE Authentication.src="<suspicious_src>"
BY Authentication.action Authentication.user Authentication.dest
| rename Authentication.* AS *
| convert ctime(first) ctime(last)
오탐 후보는 vulnerability scanner, SSO health check, 잘못 설정된 service와 VPN concentrator NAT다. 단순 allowlist보다 자산 소유자, 정상 시간대, 대상 계정 유형을 조건화한다.
6. Office에서 시작된 의심 프로세스
Follina, 악성 매크로, loader 캠페인에서는 Office가 script interpreter 또는 LOLBin을 생성하는 계보가 강한 신호다.
index=endpoint earliest=-24h
(parent_process_name=WINWORD.EXE OR parent_process_name=EXCEL.EXE
OR parent_process_name=POWERPNT.EXE OR parent_process_name=OUTLOOK.EXE)
(process_name=powershell.exe OR process_name=cmd.exe OR process_name=mshta.exe
OR process_name=wscript.exe OR process_name=cscript.exe OR process_name=rundll32.exe)
| stats earliest(_time) AS first latest(_time) AS last
values(process) AS command_line values(user) AS users count
BY host parent_process_name process_name
| convert ctime(first) ctime(last)
환경에 따라 field가 ParentImage, Image, CommandLine 또는 CIM의 Processes.parent_process_name 등으로 다르다. 먼저 실제 endpoint Add-on mapping을 확인한다.
오탐은 Office add-in, 문서관리 시스템과 관리자 자동화에서 발생할 수 있다. signer, command line, child의 외부 연결, user-writable path의 생성 파일을 결합한다.
7. 희귀 외부 연결 헌팅
악성코드는 새로운 도메인이나 조직에서 처음 보는 목적지로 통신하는 경우가 많다. “rare” 자체는 악성이 아니므로 process·DNS·asset context가 필요하다.
긴 DNS label이나 encoding된 HTTP parameter를 조사할 때는 Shannon 엔트로피를 활용한 자료유출 조사의 길이·빈도·기준선 결합 방법을 사용할 수 있다.
index=proxy earliest=-24h
| stats count dc(src) AS src_count values(src) AS src
sum(bytes_out) AS bytes_out BY dest_domain
| where src_count <= 2 AND count <= 10
| lookup known_domains domain AS dest_domain OUTPUT category owner
| where isnull(category)
| sort - bytes_out
새 SaaS, 광고·CDN과 개발 도메인이 대량 오탐을 만든다. 등록일, domain prevalence, TLS certificate, parent process, DNS history를 enrichment하되 외부 reputation 결과를 자동 차단 근거로 단독 사용하지 않는다.
8. 랜섬웨어 암호화 전 행위
복구 방해 명령은 암호화 전에 탐지할 수 있는 중요한 신호다.
index=endpoint earliest=-24h
(process_name=vssadmin.exe command_line="*delete*shadows*")
OR (process_name=wmic.exe command_line="*shadowcopy*delete*")
OR (process_name=wbadmin.exe command_line="*delete*catalog*")
OR (process_name=bcdedit.exe command_line="*recoveryenabled*")
| stats min(_time) AS first max(_time) AS last
values(command_line) AS commands values(parent_process_name) AS parents
BY host user
| convert ctime(first) ctime(last)
백업 제품과 운영 작업도 같은 명령을 쓸 수 있다. 실행 계정, maintenance window, signer, 중앙 배포 도구와 change ticket을 검토한다. 동시에 여러 호스트에서 발생하거나 EDR·백업 서비스 중지가 동반되면 위험도를 높인다.
9. 위험 기반 탐지로 약한 신호를 결합한다
단일 이벤트마다 높은 심각도의 alert를 만들면 분석가가 지친다. Risk-Based Alerting은 사용자·장치 같은 risk object에 여러 행위의 점수를 누적한다.
예시:
| 행위 | risk object | 점수 예시 |
|---|---|---|
| Password spray 의심 | source IP | 20 |
| 의심 source에서 로그인 성공 | user | 35 |
| Office → PowerShell | host | 40 |
| LSASS 접근 | host/user | 60 |
| Shadow copy 삭제 | host | 70 |
점수는 CVSS처럼 고정된 진리가 아니다. 데이터 신뢰도, 정상 빈도, 공격 단계와 탐지 정확도를 반영해 조직에서 보정한다. 동일 원인에서 파생된 중복 탐지가 점수를 부풀리지 않도록 deduplication window를 둔다.
10. 사고 타임라인 만들기
조사 대상 사용자·호스트·IP가 정해지면 각 index를 따로 보는 대신 핵심 field를 공통 schema로 맞춘다.
(
index=auth earliest=-7d (user="<user>" OR src_ip="<ip>")
)
OR (
index=endpoint earliest=-7d (user="<user>" OR host="<host>")
)
OR (
index=proxy earliest=-7d (src="<host>" OR src_ip="<ip>")
)
| eval actor=coalesce(user,src_user),
asset=coalesce(host,dest,dvc),
remote=coalesce(dest_ip,dest_domain,url),
activity=coalesce(process_name,action,signature,event_name)
| table _time index sourcetype actor asset activity remote _raw
| sort 0 _time
이 쿼리는 넓기 때문에 대상과 시간을 반드시 제한한다. _raw에는 민감정보가 포함될 수 있으므로 export 권한과 case attachment 보존 정책을 적용한다.
조사 결과에는 다음을 함께 남긴다.
- SPL과 실행 time range
- 결과 건수와 export hash
- 사용한 lookup·macro·data model 버전
- time zone과 clock skew
- 누락되거나 지연된 데이터 소스
- 분석 결론과 반증 가능한 대안
11. 탐지 규칙을 운영하는 방법
좋은 탐지 규칙은 SPL 한 줄이 아니라 운영 가능한 작은 제품이다.
name / owner / purpose
data source + required fields
ATT&CK mapping
SPL + schedule + lookback
threshold and suppression
known false positives
triage steps
response action
test dataset
version and change history
스케줄이 5분마다 실행되지만 ingestion이 최대 8분 늦는다면 earliest=-15m@m latest=-5m@m처럼 lookback을 겹치고 event ID로 중복 제거한다. 검색 실행 주기와 데이터 도착 지연을 맞추지 않으면 경계 구간 이벤트를 잃는다.
12. 안전한 검증
Splunk Attack Range는 격리된 로컬·클라우드 환경에서 공격 시뮬레이션과 telemetry 수집을 검증하는 데 사용할 수 있다. 운영 도메인이나 업무 계정을 대상으로 공격 기술을 실행하지 않는다.
검증 절차:
- 기대하는 원본 이벤트를 정의한다.
- 격리 랩에서 benign simulation을 실행한다.
- source에서 event가 발생했는지 확인한다.
- Splunk 수집·parsing·CIM mapping을 단계별로 확인한다.
- 탐지가 발생하는지, 필요한 context가 포함되는지 본다.
- 정상 관리 작업을 재생해 오탐과 성능을 측정한다.
- 수정 후 regression test를 자동화한다.
탐지가 발생하지 않았을 때 SPL만 고치지 않는다. 원본 로그 미생성, forwarder, queue, parsing, timestamp, field extraction, acceleration 순서로 실패 지점을 찾는다.
References
- Splunk Common Information Model
- Splunk: Search Using Default Fields
- Splunk: Data Model Acceleration
- Splunk Security Content
- Splunk Attack Range
이 글의 SPL은 운영 환경의 schema와 Add-on에 맞게 수정해야 한다. 실행 전 검색 time range와 index 범위를 제한하고, 자동 대응은 별도 승인·예외·rollback 절차를 갖춘 뒤 적용한다.