콘텐츠로 이동

데이터 탐색과 시간 다루기

분류: Splunk · 최종 수정: 2026-09-17

한 줄 요약

헌팅을 하려면 먼저 어떤 데이터가, 어떤 이름으로, 어떤 필드를 갖고, 어떤 시간 범위에 걸쳐 있는지 알아야 합니다. 이 페이지는 새 Splunk 인스턴스(또는 BOTSv3 같은 새 데이터셋)에서 돌리는 10분짜리 정찰과, 엉뚱한 날짜를 검색하지 않게 해 주는 시간 규칙입니다.

1단계 — 어떤 인덱스와 소스타입이 있나?

| eventcount summarize=false index=* | table index, count

eventcount는 인덱스 메타데이터만 읽기 때문에 여기서는 index=*가 가볍고 써도 됩니다 — 이벤트를 건드리지 않고 모든 인덱스와 이벤트 수를 나열합니다.

| metadata type=sourcetypes index=botsv3
| eval first=strftime(firstTime,"%Y-%m-%d %H:%M"), last=strftime(lastTime,"%Y-%m-%d %H:%M")
| table sourcetype, totalCount, first, last
| sort - totalCount

metadata는 각 소스타입의 전체 개수와 처음/마지막 이벤트 시각을 인덱스에서 바로 돌려줍니다(즉시, 시간 범위 무관). strftime은 epoch 숫자를 읽기 쉬운 날짜로 바꿉니다. first/last 열이 검색해야 할 시간 창을 알려 줍니다 — BOTSv3 데이터는 2018년 8월에 있으므로 "최근 24시간" 검색은 아무것도 돌려주지 않습니다.

더 빠르고 유연한 대안:

| tstats count, min(_time) as first, max(_time) as last where index=botsv3 by sourcetype, host
| convert ctime(first) ctime(last)
| sort - count

tstats는 원시 이벤트를 읽지 않고 색인된 필드(sourcetype, host, source, _time)로 돌아갑니다 — "호스트별로 X가 얼마나 있나" 같은 질문에 쓰세요.

2단계 — 이벤트 하나는 어떻게 생겼나?

index=botsv3 sourcetype=WinEventLog earliest=0 | head 5

earliest=0 = 인덱스의 맨 처음부터. 데이터셋의 날짜 범위를 아직 모를 때 유용합니다. 이벤트를 펼치고 왼쪽 Interesting Fields 패널을 보세요 — Splunk가 이미 추출한 필드들입니다. 필드를 클릭하면 상위 값 10개가 나옵니다.

index=botsv3 sourcetype=WinEventLog earliest=0
| fieldsummary maxvals=10
| table field, count, distinct_count, values
| sort - count

fieldsummary는 필드마다 한 행을 줍니다: 몇 개 이벤트에 있는지, 고유 값이 몇 개인지, 개수와 함께 값 샘플. 소스타입마다 한 번 돌리면 무엇으로 stats by를 할 수 있는지 알게 됩니다.

3단계 — 침해사고 대응에 중요한 소스타입

소스타입 (흔한 이름) 무엇 주요 필드
WinEventLog / WinEventLog:Security / XmlWinEventLog:Security Windows Security 로그 EventCode, Account_Name, Logon_Type, Source_Network_Address, Logon_ID, New_Process_Name, Process_Command_Line, host
WinEventLog:System, WinEventLog:Application System/App 로그 EventCode, Service_Name, Service_File_Name (7045), Message
XmlWinEventLog:Microsoft-Windows-Sysmon/Operational Sysmon EventID (또는 EventCode), Image, CommandLine, ParentImage, ParentCommandLine, User, Hashes, DestinationIp, DestinationPort, TargetFilename, TargetObject, QueryName, ProcessGuid
XmlWinEventLog:Microsoft-Windows-PowerShell/Operational PowerShell 스크립트 블록 4104 ScriptBlockText, Path, EventCode
WinEventLog:Microsoft-Windows-TaskScheduler/Operational, …TerminalServices-LocalSessionManager/Operational 작업, RDP 세션
stream:tcp, stream:http, stream:dns, stream:smb Splunk Stream (BOTS의 와이어 데이터) src_ip, dest_ip, dest_port, site, uri_path, http_user_agent, query, bytes_in/out
zeek:conn, zeek:dns, zeek:http, zeek:ssl, zeek:files (또는 bro:*:json, corelight_*) Zeek 네트워크 참고
suricata IDS 경보 alert.signature, alert.severity, src_ip, dest_ip
osquery:results, osquery_results osquery name, columns.*
aws:cloudtrail AWS API eventName, userIdentity.arn, sourceIPAddress, errorCode
o365:management:activity, ms:aad:signin, ms:o365:reporting:messagetrace M365 / Entra Operation, UserId, ClientIP, ResultStatus
linux_secure, syslog, linux_audit, auditd Linux 인증/감사 process, user, src_ip
iis, access_combined, nginx:plus:access 웹 서버 cs_uri_stem, sc_status, c_ip, cs_User_Agent
symantec:ep:*, ms:defender:atp:*, crowdstrike:events:sensor, carbonblack:* EDR/AV 벤더별
pan:traffic, pan:threat, cisco:asa, fortigate_traffic 방화벽 src, dest, dest_port, action, bytes_out

내 인스턴스에서 metadata를 돌려서 나만의 표를 만들어 두세요 — 이름은 애드온마다 다릅니다.

애드온 없는 Sysmon XML

Sysmon 소스타입에 Image, CommandLine, EventID가 없다면 Splunk Add-on for Sysmon(또는 같은 역할의 props/transforms)이 설치되지 않은 것입니다 — Splunk는 큰 XML 덩어리 하나로 봅니다. 랩에서 빠른 해결: 애드온 설치. 검색 안에서 급한 해결: | spath 또는 | rex field=_raw "<Data Name='Image'>(?<Image>[^<]+)" — rex와 필드 참고.

4단계 — 필드 이름: Account_Name vs user, 그리고 CIM

원시 Windows 필드는 Account_Name이고, Common Information Model(CIM) 이름은 user입니다. 애드온이 검색 시점에 원시 → CIM으로 매핑하므로(필드 별칭과 계산 필드로), 잘 설정된 인스턴스에서는 둘 다 동작합니다. 아무것도 안 나올 때는 어느 이름이 있는지 확인하세요:

index=botsv3 sourcetype=WinEventLog EventCode=4624 | head 100
| stats count(Account_Name) as raw, count(user) as cim, count(src_ip) as cim_ip, count(Source_Network_Address) as raw_ip

다중값 함정: 4624와 4688에는 Account_Name 값이 두 개(Subject와 Target) 있습니다. Account_Name만 쓰면 둘 다 나옵니다. 대상 계정은 mvindex(Account_Name, 1)이나 애드온의 user/src_user 구분을 쓰세요.

index=botsv3 sourcetype=WinEventLog EventCode=4624
| eval subject=mvindex(Account_Name,0), target=mvindex(Account_Name,1)
| stats count by target, Logon_Type, host

mvindex(field, n)은 다중값 필드에서 n번째 값(0부터 시작)을 고릅니다. 4624에서 0번 값은 로그온을 수행한 계정(대개 -나 컴퓨터), 1번 값은 로그온한 계정입니다.

시간 다루기

수식어 의미
earliest=-24h 지금부터 24시간 전
earliest=-7d@d latest=@d 7일 전 자정부터 지난 자정까지 (하루 단위)
earliest=@w1 이번 주 시작 (월요일)
earliest=-1mon@mon latest=@mon 지난 달력 월
earliest=0 지금까지 색인된 전부 (랩 데이터는 괜찮지만 운영 환경에서는 절대 금지)
earliest="08/20/2018:00:00:00" latest="08/21/2018:00:00:00" 절대 시각, %m/%d/%Y:%H:%M:%S
_index_earliest=-1h 색인 시각 기준 (Splunk가 받은 시각) — 늦게 도착한 이벤트 찾기
\| where _time > relative_time(now(), "-1h") 파이프라인 안에서 같은 일

맞춤(@) 단위: s m h d w mon q y, 요일은 @w0…@w6 (@w1 월요일). 수식어는 왼쪽에서 오른쪽으로 이어집니다: -7d@d+9h = 7일 전 09:00.

시간대: Splunk는 _time을 UTC epoch으로 저장하고 사용자 시간대(Settings → Account → Time zone)로 표시합니다. Windows 이벤트는 이미 UTC이고, Zeek ts는 UTC epoch이며, 일부 앱 로그는 시간대 표시 없이 로컬 시간입니다 — props.conf의 TZ를 확인하세요. 사고 보고서에는 시간대를 명시하세요. 프로필과 상관없이 UTC로 보이려면:

| eval utc=strftime(_time, "%Y-%m-%dT%H:%M:%SZ")

_time vs _indextime: _time은 이벤트가 일어난 시각, _indextime은 Splunk가 받은 시각입니다. 차이(eval lag=_indextime-_time)는 포워더 지연, 또는 침해 상황에서 로그 전송을 멈췄다가 밀린 것을 한꺼번에 보낸 호스트를 보여 줍니다.

시간으로 묶기:

| bin _time span=1h        →  then  | stats count by _time, host        (table)
| timechart span=1h count by host                                       (chart, one column per host, top 10 + OTHER)
| timechart span=1h limit=0 useother=f count by host                   (all hosts, no OTHER bucket)

timechart = bin + stats + 피벗을 한 번에, 항상 _time으로 구간을 나눕니다. span=은 생략하면 자동으로 정해지니 일관된 보고서를 위해 명시하세요.

레시피

# Log gap detection: hosts that stopped logging (last event per host older than 2 hours)
| tstats latest(_time) as last where index=botsv3 sourcetype=WinEventLog by host
| eval age_h=round((now()-last)/3600,1)
| where age_h > 2
| convert ctime(last)

tstats latest(_time)은 원시 데이터를 훑지 않고 인덱스에서 읽은 호스트별 최신 이벤트이고, age_h는 그게 몇 시간 전인지입니다.

# Business hours vs after hours
index=botsv3 sourcetype=WinEventLog EventCode=4624 Logon_Type=10
| eval hour=tonumber(strftime(_time,"%H")), dow=strftime(_time,"%a")
| eval when=if(hour>=8 AND hour<18 AND NOT dow IN ("Sat","Sun"),"business","after-hours")
| stats count by when, Account_Name
| sort when, - count

strftime(_time,"%H")는 시를 텍스트로 주고, tonumber는 비교할 수 있게 숫자로 바꾸며, %a는 요일 약어를 주고, if가 로그온마다 레이블을 붙입니다.

# Sort a specific day's events into a clean timeline
index=botsv3 (sourcetype=WinEventLog OR sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational") host=FYODOR-L
        earliest="08/20/2018:00:00:00" latest="08/21/2018:00:00:00"
| eval what=coalesce(CommandLine, Process_Command_Line, New_Process_Name, Message)
| table _time, sourcetype, EventCode, EventID, User, Account_Name, what
| sort 0 _time

coalesce는 여러 후보 중 존재하는 첫 필드를 골라서 서로 다른 소스타입이 what 열 하나를 공유하게 하고, sort 0 _time은 1만 행 제한 없이 모든 것을 시간순으로 정렬합니다.

참고 자료