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

Splunk 실습 - HTTP Log 분석 (미완)

su. 2024. 1. 11. 16:57
[SK shieldus Rookies 16기] 클라우드 기반 스마트 융합보안 과정
클라우드기반 시스템 운영 구축 실무 #07-08

 

[ Web 구조 이해 ] 포스팅 삽입

 

 

HTTP 네트워크 현황분석과 이상징후 분석

1)  HTTP Log 실습환경 구성
2)  HTTP 네트워크 현황분석
3)  HTTP 이상징후

 

 

 

1)  HTTP Log 실습환경 구성

  ❶ Index 만들기
  ❷ SourceType 지정
  ❸ 데이터 추가

 

 

 

 

 

 

 

 

# 검색 시작 초기 검색어

source="http.zip:*" host="ZeekHTTP" index="httplog" sourcetype="httplog"

 

 

HTTP 로그 필드 - 분석 도구에 따라 달라질 수 있다
method 통계
status_code 통계
status_msg 통계
proxied 통계
referrer 통계 -  유입 경로(이전 페이지 URL)
request_body_len 통계
dpt 통계

 

 

 

2)  HTTP 네트워크 현황 분석

• 내부 인트라넷 서비스/ 인터넷 기반 서비스도 대부분 HTTP로 동작
• HTTP를 분석 시 목적지가 인터넷인지 인트라넷 서비스인지 구분 필요
• 공격자가 내부망에 침투했다면 중요 데이터의 외부유출을 시도함
    - 따라서 목적지가 인터넷으로 향하는 HTTP인지 아니면 기업 내부망으로 향하는지가 중요한 기준이 될 수 있음

 

< 분석 항목 >

❶  Top 10 접속 도메인 & 국가
❷  HTTP 메소드

❸  Top 10 클라이언트 오류
❹  Top 10 서버 오류
❺  HTTP 상태 코드

 

 

❶  Top 10 접속 도메인 & 국가

 

⦁  사용자들이 가장 많이 접속하는 도메인을 추출해서 접속현황 분석
⦁  특정 도메인과 국가로 많은 데이터가 전송되거나 낯선 국가명 등을 모니터링
⦁  접속 국가 정보를 이상징후의 주요 항목으로 사용

 

index=httplog sourcetype=httplog domain!="(empty)" 
| iplocation dst 
| where NOT cidrmatch("0.0.0.0/0", domain) 
| stats sum(request_body_len) as "Outbound", sum(response_body_len) as "Inbound" by domain, Country 
| eval Outbound=round(Outbound/(1024*1024),2) 
| eval Inbound=round(Inbound/(1024*1024),2) 
| sort Outbound desc 
| head 10

 

Top 10 접속 도메인 & 국가

 

index=httplog sourcetype=httplog domain!="(empty)" 

-  httplog 중 도메인이 없는 것 제외

 

| iplocation dst 

-  국가(Country) 정보를 iplocation 명령어로 검색

 

| where NOT cidrmatch("0.0.0.0/0", domain) 

-  도메인이 IP 주소가 아닌 이벤트 검색 (IP이면 False)

 

| stats sum(request_body_len) as "Outbound", sum(response_body_len) as "Inbound" by domain, Country 

-  외부로의 전송량을 더해 Outbound 필드에, 내부로의 전송량을 더해 Inbound 필드에 저장 (byte 단위)

-  (회사 입장에서 요청은 outbound / 응답은 inbound이다)

 

| eval Outbound=round(Outbound/(1024*1024),2) 
| eval Inbound=round(Inbound/(1024*1024),2) 

-  MB 단위로 변환, 소수점 2자리까지 표시

 

| sort Outbound desc 
| head 10

-  Outbound 역순으로, 10개 정렬

 

 

가장 많이 방문한 사이트 > 이벤트 보기

 

 

두 번째로 많이 방문한 사이트 > 이벤트 보기

 

 

희귀 방문 국가나 사이트도 관제 대상이 된다

 

 

 

❷  HTTP 메소드

 

⦁  클라이언트가 서버에 자원을 요청하는 방식
⦁  클라이언트는 서버에서 Get 또는 Post를 이용해서 자원 요청
⦁  HTTP 표준에서는 많은 메소드를 지원하지만 사용자가 많이 사용하지 않는 메소드는 공격자의 정보 수집 행위 등 정상적인 사용 범위가 아닐 수 있음

 

index=httplog sourcetype=httplog uri!="-" 
| top method limit=10 showperc=f

-  uri 필드 값이 없는 이벤트 제외

-  상위 10개의 메소드, 퍼센트 표기 x 검색

 

GET 메소드가 가장 많다

 

 

❸  Top 10 클라이언트 오류

⦁  작업하는 도중 클라이언트 측 오류가 난 경우

 

index=httplog sourcetype=httplog uri!="-" uri!="/" 
(status_code >=400 AND status_code < 500
| top domain, status_code limit=10 showperc=f

-  uri가 없거나 "/"인 경우 제외

 * HTTP status code: 클라이언트가 요청한 내용을 서버에서 처리한 결과

-  status_code가 400 이상, 500 미만인 이벤트 검색

-  도메인, 상태 코드 필드 기준 가장 많은 이벤트 Top 10 출력

 

Top 10 클라이언트 오류

 

 


❹  Top 10 서버 오류

 

index=httplog sourcetype=httplog uri!="-" status_code >= 500
| top domain, status_code limit=10 showperc=f

* 500: 

* 502: proxy 자체의 문제

* 503: 리소스 문제

(상태코드 어느 포스팅 or pdf에 있는지 확인)

Top 10 서버 오류

 

 

 

❺  HTTP 상태 코드

 

index=httplog sourcetype=httplog domain!="(empty)" status_code!="-" 
| top limit=10 showperc=f status_code

 

HTTP 상태 코드

 

 

3)  HTTP 이상징후

  ❶  비정상 메소드 사용
  ❷  외부행 데이터 전송
  ❸  Mime-type과 파일 확장자 불일치
  ❹  사이트 이동 후 실행파일 다운로드
  ❺  프록시 서버 접속

 

 

 

❶  비정상 메소드 사용

 

•  Head, Delete, Trace, Option 과 같은 메소드가 네트워크에서 지속적으로 보인다는 것은 정상적인 사용자의 활동이라 보기 어렵다

•  송신지 IP를 기준으로 접속에 사용한 메소드의 종류별 사용 횟수 검색

index=httplog sourcetype=httplog
| stats count(method) by src

 

•  송신지 IP를 기준으로 options 메소드를 사용한 횟수 검색

index=httplog sourcetype=httplog
| stats count(eval(method=“OPTIONS”)) AS option_count by src
| where option_count > 10
| sort option_count desc

 

 

| stats count(eval(method=“OPTIONS”)) AS option_count by src

-  OPTIONS: 열어놓은 메소드 체크

-  OPTIONS 메소드 이벤트를 송신지 IP를 기준으로 개수를 세고 options_count 필드에 할당

 

| where option_count > 10

-  option_count가 10개 이상인 송신지 IP를

 

| sort option_count desc

-  option_count 기준 내림차순 정렬

 

 

•  

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

 

 

 

 

 


❷  외부행 데이터 전송

 

index=httplog sourcetype=httplog (request_body_len!=0 OR response_body_len!="0") domain!="-" 
| stats sum(request_body_len) as outTotal sum(response_body_len) as inTotal by src, dst 
| eval oMB=round(outTotal/(1024*1024),2) 
| eval iMB=round(inTotal/(1024*1024),2) 
| search oMB!=0 AND iMB!=0 
| iplocation dst 
| eval isUp=if((oMB/iMB)>1, "Yes","No") 
| where isUp="Yes" 
| table src,dst, iMB, oMB, Country, City

 

 

(request_body_len!=0 OR response_body_len!="0") domain!="-" 

-  요청 본문이 있는 경우(POST, PUT) 검색

-  응답 본문이 있는 경우(200 OK) 검색

-  도메인이 없는 비정상 경우 제외

 

| stats sum(request_body_len) as outTotal sum(response_body_len) as inTotal by src, dst 

-  송신지, 수신지 주소를 기준으로

-  요청 본문 길이 총량을 outTotal 필드

-  응답 본문 길이 총량을 inTotal 필드에 할당

 

| eval oMB=round(outTotal/(1024*1024),2) 

| eval iMB=round(inTotal/(1024*1024),2) 

-  byte를 MB 단위로 변경하고 소수점 둘째자리까지 표기

-  outTotal ➞ oMB / inTotal ➞ iMB 필드에 할당

 

| search oMB!=0 AND iMB!=0 

-  

 

| iplocation dst

-  

 

| eval isUp=if((oMB/iMB)>1, "Yes","No")

-  

 

| where isUp="Yes" 

-  

 

| table src,dst, iMB, oMB, Country, City

-  

 

 

파일 업로드 데이터를 검색한다고 볼 수 있다?

 

 


❸  Mime-type과 파일 확장자 불일치

 

•  첨부된 파일의 내용과 확장자가 다른 것을 검색한다

index=httplog sourcetype=httplog resp_mime_types="application/x-dosexec" uri!="-" 
| eval filename1=mvindex(split(uri,"/"),-1) 
| eval filename=if(like(filename1,"%?%"), mvindex(split(filename1,"?"),0),filename1) 
| eval filetype=if(match(filename,"(.exe|.bat|.ps1|.dll|.ocx)$"), "PE", "Not_PE") 
| table domain, uri, filename, filetype, resp_mime_types 
| where filetype=="Not_PE" 
| dedup filename

 

 

resp_mime_types="application/x-dosexec" uri!="-" 

-  mime type(content type)이 dos 실행파일인 이벤트를 검색한다 

-  uri가 없는 경우 제외

 

| eval filename1=mvindex(split(uri,"/"),-1) 

-  uri를 "/"로 분할하고 마지막 요소를 새 filename1 필드에 할당

| eval filename=if(like(filename1,"%?%"), mvindex(split(filename1,"?"),0),filename1) 

-  filename1 필드가 "?"를 포함하면 filename1을 "?"로 분할해 첫 번째 요소를 filename 필드에 할당

-  "?"를 포함하지 않으면 그대로 filename1에 할당한다

? 앞은 경로 or 파일명 / 뒤는 DB 질의(get) 조건&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(보통 ?는 한 개가 나온다)

| eval filetype=if(match(filename,"(.exe|.bat|.ps1|.dll|.ocx)$"), "PE", "Not_PE") 

-  filename 필드가 “.exe”, “.bat”, “.ps1”, “.dll”, “.ocx” 중 하나로 끝나는지 확인하고

-  그렇다면 filetype 필드를 "PE"로 / 그렇지 않으면 "Not_PE"로 설정한다

 

| table domain, uri, filename, filetype, resp_mime_types 

-  domain, uri, filename, filetype, resp_mime_types 필드를 테이블로 표시한다

 

| where filetype=="Not_PE" 

-  filetype이 "Not_PE"인 이벤트만 필터링


| dedup filename

-  filename 필드를 기준으로 중복 제거

 

 

mime type(≒content type): 브라우저가 body를 보고 인식한 type 
filetype: 클라이언트가 파일 첨부 시 지정한 파일 확장자

➞  mime type이 x-dosexec(.exe)인데 확장자명은 다른 경우를 검색한다

 

 


❹  사이트 이동 후 실행파일 다운로드

 

•  방문한 사이트에서 실행파일을 다운받았는지 확인

index=httplog sourcetype=httplog referrer!="-" status_code=200 
| eval filename1=mvindex(split(uri,"/"),-1) 
| eval filename=if(like(filename1,"%?%"), mvindex(split(filename1,"?"),0),filename1) 
| where cidrmatch("0.0.0.0/0",domain) 
| where match(resp_mime_types,"application/x-dosexec") OR match(filename,"(exe|dll|com|src)$") 
| eval URL=domain+" :: " + filename 
| stats count by src, URL 
| stats list(URL) as Target list(count) as Source by src

 

 

referrer!="-" status_code=200 

-  유입 경로(이전 페이지 URL)가 비어있지 않은 것

-  상태코드가 200인 = 성공한 응답 이벤트 검색

 


| eval filename1=mvindex(split(uri,"/"),-1) 

-  uri를 "/"로 분할하고 마지막 요소를 새 filename1 필드에 할당

  * uri: 경유지를 통해 들어온 현재 사이트


| eval filename=if(like(filename1,"%?%"), mvindex(split(filename1,"?"),0),filename1) 

-  filename1 필드가 "?"를 포함하면 filename1을 "?"로 분할해 첫 번째 요소를 filename 필드에 할당

-  "?"를 포함하지 않으면 그대로 filename1에 할당한다


| where cidrmatch("0.0.0.0/0",domain) 

-  도메인이 IP주소 체계인 이벤트 검색


| where match(resp_mime_types,"application/x-dosexec") OR match(filename,"(exe|dll|com|src)$") 

-  mime type(content type)이 dos 실행파일이거나,

-  filename이 “.exe”, “.dll”, “.com”, “.src” 중 하나로 끝나는 이벤트 검색


| eval URL=domain+" :: " + filename 

-  새로운 필드 URL에 "domain :: filename" 형식의 내용 할당


| stats count by src, URL 

-  src와 URL 필드를 기준으로 이벤트 수 계산


| stats list(URL) as Target list(count) as Source by src

-  송신지 IP를 기준으로

-  URL을 Target 필드에, 이벤트 수를 Source 필드에 리스트 형태로 할당

  (필드명을 바꾸고 테이블로 출력하기 위함)

 

 

*  DBD (Drive by Download) 공격

공격자는 유포지에 웹 쉘과 같은 악성 스크립트 파일을 삽입

보안이 취약한 사이트를 경유지로 설정, 중계지로 Redirect 되도록 걸어 놓는다

중계지가 없을수도, 여러 개일 수도 있다.

중계지가 많을수록 유포지(공격자)를 찾기 어렵다

 

이 공격에서 유포지와 중계지(경유지)가 존재하고,

uri는 유포지, referrer는 바로 이전 중계지(경유지)가 된다.

 

유포지: 기존 사이트를 이용하거나 공격자가 만든다(domain=ip주소)

   * 도메인명을 등록하려면 자신을 드러내야 하므로, 공격자 자체 제작의 경우 ip주소가 도메인이다.

 

 


❺  프록시 서버 접속

 

•  Proxy Server

(ppt로 채우기)

 

다이렉트로 가는데 웹 Client에 proxy를 설치하는 경우

-  서버의 취약점이나 구조를 파악

•  

index=httplog sourcetype=httplog (uri="http://*" OR method="connect"
| table src, domain, uri

-  Client 자체 proxy를 통해 접속한 경우 또는 connect 메소드를 사용한 이벤트 검색

-  송신지IP, 접속한 도메인, uri 필드를 테이블로 출력