SK shieldus Rookies/클라우드 기반 시스템 운영ㆍ구축 실무

SIEM - Splunk 실습(Splunk Enterprise) & 검색 명령어 (미완)

su. 2024. 1. 10. 18:03
[SK shieldus Rookies 16기] 클라우드 기반 스마트 융합보안 과정
클라우드기반 시스템 운영 구축 실무 #06

 

학습 내용

-  SIEM 복습
-  Splunk Enterprise 설치
-  Search & Reporting
-  Splunk 검색 명령어

 

(12.SIEM_Splunk.pdf + 01.03 필기 p8~로 내용 정리)

 

 

가상환경에서 진행할 경우의 구조

 

원활한 실습을 위해 이번에는 가상환경이 아닌 windows 환경에서 Splunk를 설치하고,

제공되는 튜토리얼 데이터를 이용해 실습을 진행해보았다

 

 

Splunk Enterprise 설치

splunk-9.0.1-...x64-release.msi

 

 

 

Splunk Web 실행

설치 마지막에 위처럼 체크를 했다면 자동으로 진입된다

설치 시 설정한 사용자 이름과 암호로 로그인한다

 

여기에서 Search & Reporting 앱을 이용할 것이다

 

 

 

 

Search & Reporting

먼저, 수집한 데이터가 없으니 튜토리얼 데이터를 추가해준다

설정 > 데이터 추가

 

업로드
tutoriadata.zip 파일 선택
host 필드 값: TestHost
제출

 

제출 후 업로드 과정에서 정규화, 필터링 작업까지 완료된다.

 

 

검색 시작을 누르면 다음 화면을 볼 수 있다.

 

그래프에 마우스 가져가기
테이블로 보기

 

하나를 선택하면, 테이블에 표시하지 않은 필드 포함 전체 정보를 볼 수 있다

 

 

 

 

 

업로드했던 로그 zip 파일을 압축 해제해서 봐 보자.

다섯 개 폴더로 이루어져 있다

 

mailv\secure 로그

로그 날짜 / 시간 / 컴퓨터 이름 / 프로세스id /

: 인증 실패 관련 로그

 

vendor_sales 로그

vendorID, Code: 상품 이름, 코드 값

AcctID: 계정 ID

 

www1\access 로그

: 웹 트래픽 로그

www1\secure 로그

: Fail 관련 로그

 

 

 

Splunk 페이지에서 검색 기능을 좀 더 살펴보자.

먼저, 왼쪽 상단의 검색 > 데이터 요약을 선택한다

 

 

Source > 특정 로그 파일을 선택해서 볼 수 있다

 

www1/access.log
예전 로그 파일이니, 전체 시간으로 결과를 본다
method = GET / status = 200

 

리스트 테이블 오른쪽 필드 목록에서 필드를 선택하면,

해당하는 값에 따른 간단한 분석 결과를 볼 수 있다.

 

 

여기에서도 값을 클릭할 수 있고,

클릭하면 검색 조건에 추가되어 결과를 보여준다.

아래 결과에서 막대 그래프를 클릭하면, 해당 날짜의 이벤트만 보이게 된다.

 

 

다시 상단의 검색 > 검색 내역을 보면, 지금까지 검색한 필터 목록을 볼 수 있고

검색에 추가하여 다시 검색할 수 있다.

 

 

 

검색 방법

(3:20~)

1. 특별한 형식 없이 키워드만으로도 검색 가능하다 (대소문자 구분x)

400

 

2. 필드명과 필드 값으로 함께 검색하는 것이 더 정확하다

status=400

 

3. 둘 이상의 조건은 공백으로 구분해 작성하면 되는데,

   이 때 둘 사이에 AND 연산이 있는 것과 같다

 status=400 categoryId=Null

(status=400 AND categoryId=Null)

 

 

4. 공백을 AND가 아닌 한 문장으로 취급하려면 따옴표를 사용한다

"access denied"

 

 

5. 다른 연산자를 이용해 검색 가능하다

- OR, NOT

- 대문자로 입력

- 괄호를 이용해 우선순위 지정 가능

  401 AND (402 OR 403)

 

 

 

Splunk 검색 명령어

  • 데이터 나열, 변환: 
  • 통계 계산: 
  • 차트 시각화: 
  • 비교 분석: 
  • 다중 문자열과 시간: 

 

table

• 필드명과 결합해 검색 결과를 테이블 형태로 보여줌
• table 명령어 다음에 확인하고 싶은 필드명 적음
• 필드명을 잘못 적으면 행당 필드에 공란이 보임
- 다른 필드에는 값이 보이는데 특정 필드가 공백이면 필드명 입력오류 의심
• 필드명은 대소문자를 구분
• 여러 개의 필드는 쉼표(,) 또는 띄어쓰기로 구분
table <<필드1>> <<필드2>> …<<필드n>>

 

index=main sourcetype=access_combined_wcookie

table clientip, method, productId, status

전체 시간 > 검색

 

 

rename

• 필드명을 다른 이름으로 변경
• 필드명을 띄어쓰기로 구분하고 싶으면 원하는 필드명을 따옴표로 표시
rename <<원래 필드명>> AS <<변경 필드명>>

 

index=main sourcetype=access_combined_wcookie
| table clientip, action, productId, status
rename action AS "Customer Action", productId AS ProductID, status AS "HTTP Status"

 

 

 

sort

• 검색 결과를 정렬
        sort (+|-) << 필드명1>>
• 기본 값은 오름차순 정렬
• 내림차순으로 정렬하려면 필드 앞에 마이너스(-)를 붙임

 

index=main sourcetype=access_combined_wcookie
| table clientip, action, productId, status
| sort action, -productId

 

 

index=main sourcetype=access_combined_wcookie
| table clientip, action, productId, status
| sort -ip(clientip)

* IP주소를 정렬할 경우 함수 ip() 또는 num( ) 사용

 

 

 

dedup

• 검색 결과에서 중복을 제거
        dedup << 필드명1>>, << 필드명2>>
• 중복 제거는 지정한 필드를 기준으로 실행
• 두 개 이상의 필드에서 중복을 제거하려면 쉼표(,)로 구분
• 중복을 제거하면 해당 필드의 유일한 값을 기준으로 결과를 보여줌
• 중복을 제거하면 다른 필드의 값은 중복이 아닌데도 중복된 결과를 제거하면서 사라짐
• dedup은 상세 분석이 아니리 해당 필드에 존재하는 값을 확인하는 정도로 사용

 

index=main sourcetype=access_combined_wcookie status=404

 

 

index=main sourcetype=access_combined_wcookie status=404 | dedup host

 

 

 

 

stats

• 각종 통계 함수를 이용해서 데이터 계산
stats [count|dc|sum|avg|list|values] by [필드명]

 

 

index=main sourcetype="access_combined_wcookie"
stats sum(bytes), avg(bytes), max(bytes), median(bytes), min(bytes) by clientip

➞ 사용자마다 외부로 전송하는 바이트의 총합, 평균, 최대, 중간, 최소값을 검색

 

 

 

top

• 지정한 필드에서 가장 많이 나오는 값을 보여주는 명령어

  | top limit=<숫자> [showperc=T/F] [showcount=T/F] [useother=T/F] 필드1, 필드2 by 필드
• 검색 결과를 파이프로 입력 받아서 계산 한 후에 결과를 반환
• showperc와 showcount의 기본값이 T이므로 지정하지 않더라고 비율과 개수를 보여줌

 

 

* | top useother=T clientip by method

method 필드를 기준으로 상위 10개의 clientip를 보여준다

useother=T에 의해 그 외(OTHER) 이벤트 개수를 보여준다

 

 

 

rare

• top과 반대의 결과인 빈도가 적은 값의 순서를 추출
  | rare limit=<숫자> [showperc=T/F] [showcount=T/F] [useother=T/F] 필드1, 필드2 by 필드

 

* | rare useother=T clientip by method

- method 필드를 기준으로 하위 10개의 clientip과 그 외(OTHER) 이벤트 개수를 보여준다

 

 

 

차트 시각화

•  원본 로그 데이터로 차트를 그리는 것은 불가능하다
    - 데이터를 차트로 만들려면 먼저 관련 데이터를 추출하고
    - 시각화 원본 데이터를 통계 테이블로 변환한다
•  관련 명령어 : chart, timechart

 

 

timechart

• 시간에 따른 통계 테이블 생성
timechart span=[시간범위] 통계함수 by [필드명]
• 시간 필드가 X축, 실제데이타가 Y축에 표시되는 차트 시각화 형식으로 나타냄
• 시간에 따른 통계의 추세를 표시할 때 많이 사용
• Span : 시간 계산단위 설정
• Count : 전체 개수를 계산하는데 12시간 단위로 총 숫자를 구해서 보여줌

 

index=main sourcetype="access_combined_wcookie"
timechart span=12h count(clientip) as "Access Count"

 

 

 

chart

• Timechart와 유사한 방식으로 동작
chart [통계함수] over X축 by [기준필드]
• Timechart는 span 옵션을 사용해서 시간 단위를 지정할 수 있지만 char명령어는 해당 옵션을 사용할 수 없음

 

index=main sourcetype="access_combined_wcookie"
chart dc(clientip) as "Unique Count" over date_wday

 

 

 

비교 분석

명령어: eval, case, cidrmatch, if, like, match

 

eval

• 검사 결과 값의 변환, 검증을 수행하며 함수 실행 결과 값을 반환
eval [반환값_저장변수] = 함수(인자1, 인자2..)

 

sourcetype=access_*
eval status_code=if(status==200, "OK", "Error")
| table clientip status_code

 

index=httplog sourcetype=httplog
eval list="mozilla"
| `ut_parse_extended(url,list)`
| table ut_netloc, ut_domain, ut_subdomain, ut_domain_without_tld, ut_tld
| dedup ut_netloc

 

 

case(X,”Y”,…)

• 여러 개의 조건을 검증할 때 사용
• 두 개의 인자가 한 그룹으로 동작
• 첫 번째 인자가 참인 경우 두 번째 인자의 내용이 반환, 세번째 인자가 참이면 네 번째 인자가 수행

 

index=httplog sourcetype=httplog
| eval description=case(error==404, "Not found", error==500, "internal Server Error")
| table clientip description

 

| eval quarter=case(date_month=="January", "1Q", date_month=="April", "2Q")

 

 

cidmatch(“X”,Y)

• 네트워크 범위 X에 IP주소 Y가 존재하는지 확인
• 반환 값은 참 또는 거짓이며 두 개의 인자가 사용
• 첫 번째 CIDR 형식의 네트워크 주소 범위, 두 번째는 검사를 위한 IP 주소가 입력

 

| eval local=cidrmatch("10.0.0.0/8", "10.10.0.100")

➞ IP 주소 10.10.0.100이 10.0.0.0/8 대역에 포함되면 true 아니면 false 반환

 

• cidrmatch 함수는 검색 필터로 사용할 수 있음

| where (cidrmatch("10.0.0.0/8", ip) OR
(cidrmatch("172.16.0.0/12", ip) OR
(cidrmatch("192.16.0.0/16", ip)

 

 

if(X,Y,Z)

• X가 참이면 Y를 실행하고, 거짓이면 Z 실행

 

* | eval ip1="10.10.0.100", ip2="100.10.0.100"
| eval network1=if(cidrmatch("10.10.0.0/24", ip1),"local", "external"),
network2=if(cidrmatch("10.10.0.0/24", ip2),"local", "external")
| table ip1, network1, ip2, network2

➞ IP 필드 값이 10.10.0.100이라면 10.10.0.0/24 네트워크에 포함되므로 network1 필드에 “local” 문자열 저장
➞ IP 필드 값이 100.10.0.100이라면 network2 필드에는 “external”이 할당된다

 

 

like(X,“Y”)

•  Like 함수의 X필드에서 일부 문자열인 Y를 검색
•  첫 번째 인자는 대상 필드, 두 번째 인자는 정규 표현식의 탐색 패턴
•  X필드에 Y값이 일부라도 있으면 참을 반환
•  like 함수의 와일드 카드 문자열은 ‘%’

    …| where like(field, "add%")

➞ field 변수가 addr로 시작하는지 검사

 

 

match(X,“Y”)

•  Like 함수가 일부라도 맞는 값을 찾는다면 match함수는 함수명과 같이 정확한 일치여부를 비교

match(filename, "malicious.exe")

➞ Filename이 malicious.exe와 정확히 같으면 참, 그렇지 않으면 거짓을 반환
➞ 문자열 비교에 대소문자는 구분하지 않음

 

match(filename, "(.jpg|.gif|.png)$")

➞ $는 종결자로써 ‘$’ 앞의 문자로 단어가 끝난다는 의미
➞ ‘|’는 다중 선택을 의미
➞ Filename 필드 값이 .jpg, gif, .png로 종결($)하는지 검사
    =  파일 확장자가 jpg, gif, png 여부 검사

 

match(method, "(GET|POST|-)")

➞ method가 GET, POST, 또는 “-” 인지 검사

 

NOT match(method, "(GET|POST|-)")

➞ method가 GET, POST, 또는 “-” 가 아닌 메소드들을 검사

 

index=httplog sourcetype=httplog
| where NOT match(method, "(GET|POST|-)")
| stats count(src) as src_count by method
| sort - src_count

❶ method가 GET, POST, 또는 “-” 가 아닌 메소드들을 검사
❷ 검사된 메소드 별로 송신지 주소 개수를 세서 결과 반환
❸ 개수 Count(src)를 src_count에 저장
❹ 저장된 src_count 값을 기준으로 내림 차순 정렬

 

 

다중 문자열과 시간

명령어: split, mvindex, substr, round, urldecode, strftime, strptime, now

 

split(X,”Y”)

•  X를 구분자 Y를 이용해서 분할해 다중값 형식으로 변환한다
•  구분자로 분리한 문자열은 여러 개의 토큰이 발생하므로 주로 mvindex()에서 사용
•  이벤트에서 특정 값을 추출할 때 사용

 

 

mvindex(X,Y,Z)

•  필드 X에 있는 Y 번째 값은 반환 (Z 생략가능)
•  Y는 인덱스 번호 ( 0 : 첫 번째 값, -1 : 인덱스를 뒤에서 시작, -2 : 끝에서 두 번째)
•  세 번째 인자인 Z는 선택적으로 사용
  - Z값을 지정하면 함수는 Y부터 Z까지의 값을 반환

 

 

substr(X,Y,Z)

•  세 번째 인자인 Z가 없다면 필드 X의 Y부터 시작해서 문자열 끝까지 반환
•  Z가 주어지면 Y부터 Z개의 문자열을 반환

 

*| eval passwd_str="lightdm:x:107:117:Light Display M a n a g e r :/var/lib/lightdm:/bin/false"
| eval uid=mvindex(split(passwd_str,":"),0)
| eval subuid1=substr(uid,2)
| eval subuid2=substr(uid,2,4)
| table uid, subuid1, subuid2

 

 

round(X,Y)

• X를 Y 자리 수 기준으로 반올림
• 나누기 계산을 할 경우 소수점 자리가 급격히 늘어나는 것을 방지

 

 

urldecode(X)

• URL 인코딩이 있는 X를 디코딩해 반환
• 웹 주소에 한글이 사용되는 경우 대부분 URL인코딩이 되어 바로 확인이 힘듦
• 인코딩 문자열을 디코딩해서 한글이 있는 경우라도 바로 확인 할 수 있음

 

 

strftime(X,Y)

• 유닉스 타임 X를 지정한 Y형식으로 출력
• 주로 사용자가 읽기 편한 형식으로 변환할 때 사용
• 유닉스 타임(에포크 타임) 계산법은 1970년 1월 1일 0시를 기준으로 초를 계산

 


striptime(X,Y)

• strftime과 반대로 Y형식으로 된 X 시간 문자열을 입력받아서 유닉스 타임을 반환