Skip to content
piroimPublic

About

Event Log Analysis Tool

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

PIRO EVTX Analyzer

Windows 이벤트 로그(.evtx)를 웹 화면에서 한눈에 보고 분석하는 Flask 기반 도구입니다. 이벤트 로그는 파일을 읽어서 분석하는 작업이므로 PowerShell(ps1) 대신 Python으로 구현했습니다.

실행 방법

cd piro-evtx
pip install -r requirements.txt
python main.py                          # http://127.0.0.1:5000
python main.py --port 8080 --host 0.0.0.0
python main.py --port 5050              # http://127.0.0.1:5050

서버 종료

실행한 터미널에서는 Ctrl+C로 종료합니다. 여러 터미널에 띄워 둔 서버를 한 번에 모두 끄려면 아래 명령을 사용합니다.

# main.py로 실행 중인 python 프로세스를 모두 종료
Get-CimInstance Win32_Process -Filter "Name like 'python%'" |
  Where-Object { $_.CommandLine -like '*main.py*' } |
  ForEach-Object { Stop-Process -Id $_.ProcessId -Force }

# 특정 포트(예: 5050)를 쓰는 서버만 종료
Get-NetTCPConnection -LocalPort 5050 -State Listen -ErrorAction SilentlyContinue |
  ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }
  • 실행하면 첫 화면인 분석 대상에서 스캔된 대상을 고르고 대시보드·분석으로 들어갑니다.
  • 폴더 내용을 바꿨다면 대상 카드의 재스캔 또는 사이드바의 전체 재스캔을 누릅니다. 새 파일과 크기·수정시각이 바뀐 파일만 다시 파싱하고, 삭제된 파일·폴더는 캐시에서도 지웁니다.
  • 요구 사항: Python 3.10 이상, flask, evtx(Rust 기반 고속 파서)

분석 대상 (폴더 단위)

여러 PC의 로그를 함께 다루기 위해 .evtx가 직접 들어 있는 폴더 하나를 분석 대상 하나로 봅니다. 대상명은 그 폴더명입니다.

Target_eventlog/            ← 도구 폴더 기준 기본 스캔 경로
├─ HA26/                    → 대상 "HA26"
│  └─ Windows PowerShell.evtx
└─ HA38/                    → 대상 "HA38"
   ├─ ...LocalSessionManager%4Operational.evtx
   └─ ...RDPClient%4Operational.evtx
  • 스캔 경로: 도구 폴더 기준 기본 경로 Target_eventlog와, 화면에서 등록한 외부 폴더(data/targets.json). targets.json의 상대 경로도 도구 폴더를 기준으로 해석합니다. 스캔 경로 아래를 하위 폴더까지 찾습니다.
  • Target_eventlog 폴더가 없으면 서버 또는 evtx-copy.ps1을 처음 실행할 때 자동으로 만듭니다.
  • 외부 폴더 등록: 로그를 복사하지 않고 원래 위치에서 바로 분석합니다. 등록 해제해도 원본 파일은 지우지 않습니다.
  • 기본 경로 대상 등록 해제: 대상 카드의 등록 해제를 누르면 그 폴더는 전체 재스캔에서 제외됩니다(targets.json의 excluded). 다시 분석하려면 외부 폴더 등록에 그 경로를 입력해야 합니다. 등록 해제된 폴더 목록의 경로 입력을 누르면 입력칸이 채워집니다. 등록 해제된 대상명으로는 업로드할 수 없습니다.
  • 업로드: 대상명을 입력하고 .evtx를 올리면 Target_eventlog/<대상명>/에 저장됩니다.
  • 대상 카드에는 로그 파일 수, 이벤트 수, 호스트, 기간, 채널, 탐지 결과 건수(등급별)와 주요 탐지 항목이 보입니다.
  • 하나 또는 여러 대상을 체크하고 선택 대상 대시보드 / 분석으로 들어가면 모든 화면·API가 선택한 대상으로만 제한됩니다. 여러 개를 고르면 로그 파일과 이벤트 옆에 대상명이 함께 표시됩니다.
  • 선택은 세션(쿠키)에 저장되고, 세션 키는 data/secret.key에 있어 서버를 다시 켜도 유지됩니다.
  • 같은 이름의 폴더가 여러 곳에 있으면 Logs (HA26)처럼 상위 폴더명을 붙여 구분합니다.
옵션 기본값 설명
--host 127.0.0.1 바인딩 주소. 다른 PC에서 접속하려면 0.0.0.0
--port 5000 포트
--debug 끔 Flask 디버그 모드(코드 변경 시 자동 재시작)

폴더 구조

piro-evtx/
├─ main.py                  # 실행 진입점. 앱 생성, 시작 시 폴더 동기화
├─ targets.py               # 분석 대상: 폴더별 스캔 결과, 선택, 재스캔, 외부 폴더 등록, 업로드
├─ dashboard.py             # 대시보드: 선택한 대상의 전체 현황
├─ evtxlist.py              # 로그 목록: 채널별 설명, Event ID 분포
├─ analysis.py              # 분석: 탐지 규칙, RDP 세션 재구성, 이벤트 조회 API
├─ store.py                 # evtx 파싱, SQLite 캐시, 필터·시간 분포 집계
├─ reference.py             # Event ID 레퍼런스 조회·검색, /reference 화면과 API
├─ refdata/                 # Event ID 레퍼런스 데이터. 모듈 하나 = 카테고리 하나 (자동 로드)
│  ├─ __init__.py           # 모듈 형식 설명, load_categories()
│  ├─ logon.py, rdp.py, powershell.py, wmi.py, winrm.py ...
├─ requirements.txt
├─ templates/               # base, targets, dashboard, logs, analysis, reference (Jinja2)
├─ static/
│  ├─ css/app.css           # 다크 테마
│  ├─ fonts/                # 로컬 폰트(woff2, 가변 굵기). 영문 Noto Sans, 한글 Noto Sans KR, 코드 Noto Sans Mono
│  └─ js/
│     ├─ charts.js          # 외부 라이브러리 없는 SVG 차트 (타임라인, 도넛)
│     ├─ analysis.js        # 분석 화면: 탭, 이벤트 탐색, 상세 패널
│     └─ reference.js       # 레퍼런스 화면: 검색, 주제·등급·로그 파일 필터
├─ Target_eventlog/         # 기본 스캔 경로. 하위 폴더 하나 = 분석 대상 하나
│  ├─ PC-01/Security.evtx
│  └─ PC-02/...
├─ data/
│  ├─ cache.db              # 파싱 결과 캐시 (자동 생성, 삭제하면 다시 파싱)
│  ├─ targets.json          # {"roots": 외부 스캔 경로, "excluded": 기본 경로에서 등록 해제한 폴더}
│  └─ secret.key            # 세션 키 (자동 생성)

현재 PC의 이벤트 로그 수집

관리자 PowerShell에서 evtx-copy.ps1을 실행하면 VSS 스냅샷으로 현재 PC의 EVTX 파일을 복사하고 SHA-256 해시를 검증합니다. 기본 저장 위치는 실행 위치가 아니라 스크립트가 있는 도구 폴더의 Target_eventlog입니다.

.\evtx-copy.ps1 -TargetName PC-01

# 외부 증거 드라이브를 쓸 때만 경로를 지정
.\evtx-copy.ps1 -TargetName PC-01 -DestinationRoot 'E:\EVTX'

분석 방식

"중요한 부분만 볼지, 전체 값을 볼지"는 둘 다 하는 방식으로 구현했습니다.

  1. 전체 저장: 모든 레코드의 System 정보(시간, Event ID, 레벨, 채널, 컴퓨터, SID)와 EventData/UserData를 키=값 형태로 펼쳐 SQLite에 저장합니다. 그래서 어떤 필드든 키워드로 검색할 수 있습니다.
  2. 중요 항목 강조: 그 위에 다음 두 가지를 얹어 볼 것을 좁혀 줍니다.
    • refdata/의 등급(침해주의/주의/정보)과 설명. 같은 Event ID라도 채널별로 구분합니다(예: RDPClient 1102와 Security 1102).
    • analysis.py의 탐지 규칙(RULES)이 찾아낸 항목을 출발지 IP·계정 등으로 묶은 결과.
  • 시간은 UTC로 저장하고 화면에는 KST(UTC+9)로 표시합니다. 바꾸려면 store.py의 TZ_HOURS, TZ_LABEL을 수정합니다.
  • 손상된 로그는 읽을 수 있는 부분까지만 적재하고, 오류 내용은 로그 목록과 대시보드에 표시합니다.

화면 구성

분석 대상 (/)

  • 스캔 경로별로 대상 카드를 보여주고, 체크한 대상만 분석합니다(위 "분석 대상" 참고).
  • 다른 화면 상단에 현재 선택한 대상이 표시되고, 변경을 누르면 이 화면으로 돌아옵니다.
  • 대상을 고르지 않고 다른 화면에 들어가면 이 화면으로 돌아옵니다.

대시보드 (/dashboard)

  • 핵심 지표: 등록 로그 수, 전체 이벤트, 오류·경고, 탐지 결과, 원격 RDP 출처 IP, 로그 기간
  • 이벤트 타임라인: 기간에 따라 시간/일/주/월 단위로 자동 집계하고 레벨별로 쌓아 표시. 막대를 클릭하면 그 구간의 이벤트로 이동
  • 탐지 결과 요약, Event ID Top 12, 로그 파일별 레벨 비율, 레벨 분포, 레퍼런스 주의 이벤트

로그 목록 (/logs)

  • 로그별로 무엇을 기록하는지와 분석 포인트(채널 설명은 evtxlist.py의 CHANNELS)
  • Event ID 분포(이름, 등급, 건수, 최초/최종 시각). 행을 클릭하면 이벤트 탐색으로 이동
  • 레퍼런스상 주요 ID 중 이 로그에 없는 항목 표시
  • 선택한 대상별로 묶어서 표시

분석 (/analysis)

  • 화면 오른쪽 상단 KST / UTC 전환: 기본은 KST(다른 화면과 동일). UTC를 고르면 탐지 결과·이벤트 탐색·RDP 세션·시간 필터·히스토그램·상세 패널이 UTC 기준으로 표시됩니다. URL에 ?tz=utc가 붙습니다.
  • 탐지 결과: 규칙별 등급, MITRE ATT&CK, 묶음 표. 표의 행을 클릭하면 그 값(IP 등)으로 이벤트를 검색
  • 이벤트 탐색
    • 필터: 로그 파일, Event ID(4624,4625 포함 / -5858 제외), 레벨, 키워드(EventData 전체), 시작·종료 시각
    • 시작·종료는 날짜와 시간을 따로 입력하며 시간은 생략할 수 있습니다. 시작=종료=2026-08-03처럼 날짜만 넣으면 그날 하루 전체를 봅니다. 종료에 시간을 넣으면 그 시각은 포함하지 않습니다(예: 10:00 → 09:59:59까지).
    • 필터 결과의 시간 분포와 상위 Event ID, 100건 단위 페이지
    • 행을 클릭하면 상세 패널이 열림: 레퍼런스 설명, EventData(레퍼런스 주요 필드는 주황색), System 정보, 같은 로그의 전후 이벤트, "같은 Event ID만 보기", "이 시각 ±10분 보기"
    • 필터 상태가 URL에 남으므로 주소를 공유하면 같은 화면을 볼 수 있음
  • RDP 세션: LocalSessionManager 이벤트로 세션 흐름을 재구성 (21 로그온 → 22 셸 시작 → 24 끊김 / 25 재연결 → 23 로그오프). 원격 IP는 주황색으로 표시

Event ID 레퍼런스 (/reference)

  • 앱 안의 화면으로 열리며, 분석 대상을 고르지 않아도 볼 수 있습니다.
  • 검색: Event ID, 이름, 설명, 필드명, MITRE 기법, 로그 파일명을 대상으로 하며 공백으로 나누면 모두 포함하는 항목만 찾습니다(AND). Event ID와 정확히 같은 항목이 맨 앞에 옵니다. / 키로 검색칸 이동, ESC로 초기화
  • 필터: 주제(로그온, RDP, PowerShell 등), 등급, 왼쪽 로그 파일 목록(파일 이름 필터 지원, 현재 조건의 건수 표시)
  • 연관 Event ID를 누르면 그 ID로 검색하고 해당 항목을 펼칩니다.
  • 필터 상태가 URL에 남습니다. 예: /reference?q=4624, /reference?file=Security.evtx&topic=logon

탐지 규칙

일반 탐지 규칙은 analysis.py의 RULES에 정의되어 있습니다. Sysmon 채널은 refdata/sysmon.py에서 sev="warn"으로 등록된 Event ID를 탐지 결과에 자동으로 표시합니다. Sysmon 주의 이벤트는 조사 대상 요약이며, 기록 자체가 침해를 뜻하지 않습니다.

등급 규칙 Event ID
침해주의 보안 감사 로그 삭제 / 이벤트 로그 삭제 Security 1102 / System 104
침해주의 감사 정책 변경 4719
침해주의 새 서비스 설치 System 7045, Security 4697
침해주의 사용자 계정 생성 / 보안 그룹 멤버 추가 4720 / 4728·4732·4756
침해주의 WMI 영구 이벤트 구독 등록 5861
침해주의 의심 PowerShell 스크립트 블록(-enc, IEX, DownloadString 등) 4104
주의 로그온 실패 다발(같은 IP·계정 5회 이상) 4625
주의 RDP 로그온(Type 10) / 명시적 자격증명 사용 4624 / 4648
주의 원격 RDP 세션 로그온·재연결 / RDP 인증 성공 LSM 21·25 / RCM 1149
주의 다른 서버로 RDP 접속(발신) RDPClient 1024·1102
주의 WinRM 원격 세션 생성(발신) / 원격 셸 요청 수신 WinRM 6 / 91
주의 비표준 경로의 WMI 프로바이더 로드 5857
주의 Sysmon 레퍼런스의 주의 이벤트 Sysmon 1·2·3·6·8·255 (레퍼런스 등급에 따라 변경)
정보 RDP 리스너 연결 수신, WMI 작업 오류, WinRM 작업 요약 261, 5858, 145

일반 규칙을 추가하려면 RULES에 항목을 하나 더 넣습니다. Sysmon 주의 등급은 refdata/sysmon.py의 sev 값을 수정합니다.

{"key": "my_rule", "sev": "warn", "title": "규칙 이름", "eids": [4624], "channels": SECURITY,
 "where": "json_extract(e.data, '$.LogonType') = 3",   # 선택: 추가 SQL 조건
 "group": ["IpAddress", "TargetUserName"],              # 묶을 EventData 필드 (비우면 최근 20건 샘플)
 "labels": ["출발지 IP", "계정"], "min_count": 1,
 "mitre": "T1021", "desc": "설명"},

레퍼런스 확장

  • Event ID 설명은 refdata/ 폴더의 Python 모듈에 있습니다. 수정 후 서버를 재시작하면 반영됩니다.
  • 새 로그 파일(채널)은 기존 카테고리 모듈의 ITEMS에 추가하거나, 새 모듈 파일을 만들면 자동으로 읽습니다.
# refdata/taskscheduler.py
CATEGORY = "작업 스케줄러"
FILE = "Microsoft-Windows-TaskScheduler/Operational.evtx"      # 화면 표시용
SOURCE = "Microsoft-Windows-TaskScheduler%4Operational.evtx"   # 실제 파일명. 채널 매칭에 사용
TAGS = ["system"]                                              # reference.TOPICS의 키
ORDER = 130                                                    # 표시 순서

ITEMS = [
    {
        "id": "106", "name": "예약 작업 등록", "en": "Task registered", "sev": "warn", "tags": ["system"],
        "desc": "새 예약 작업 등록. <b>TaskName</b>과 등록 사용자로 지속성 여부 확인.",   # HTML(<b>, <br>) 사용 가능
        "fields": ["TaskName", "UserContext"],
        "rel": ["140", "200"],
        "attack": "T1053.005 예약 작업",
    },
]
  • 한 카테고리 안에서 파일이 다른 항목은 "source": "...evtx"를 따로 지정합니다(예: rdp.py의 1149, 1024).
  • 주제 필터를 늘리려면 reference.py의 TOPICS에 (키, 라벨)을 추가합니다.
  • 레퍼런스에 없는 ID는 화면에 "미등록"으로 표시됩니다(예: LocalSessionManager 32, 34, 54).

API

경로 설명
GET /api/events 선택한 대상의 이벤트 목록(대상 미선택 시 409). 파라미터: file, channel, eid, level(error/warn/info), q, start, end(KST, YYYY-MM-DDTHH:MM), page, size(10~500), order(asc/desc). 시간 분포(histogram)와 상위 ID(top)를 함께 반환
GET /api/events/<id> 이벤트 상세: EventData, System, 레퍼런스, 전후 이벤트
GET /api/reference 레퍼런스 검색. 파라미터: q, topic, sev(crit/warn/info), file(로그 파일명). 로그 파일별 건수(files)를 함께 반환
POST /targets/select 분석 대상 선택(ids 여러 개)
POST /targets/scan 전체 스캔 경로 재스캔
POST /targets/<id>/scan 대상 하나만 재스캔
POST /targets/add 외부 스캔 경로 등록(path)
POST /targets/<id>/exclude 기본 경로 대상 등록 해제(전체 재스캔에서 제외)
POST /targets/roots/remove 외부 스캔 경로 등록 해제(root)
POST /targets/upload Target_eventlog/<name>/에 .evtx 업로드(name, files) 후 파싱

폰트 (오프라인)

외부 CDN 없이 static/fonts의 파일만 사용하므로 인터넷이 없는 분석 PC에서도 같은 화면으로 보입니다.

파일 용도 출처
NotoSans-latin.woff2, NotoSans-latin-ext.woff2 영문 Fontsource (Noto Sans, variable)
NotoSansKR-korean.woff2 한글 Google Fonts NotoSansKR[wght].ttf에서 한글·영문·기호만 추려 변환
NotoSansMono-latin.woff2 Event ID, 시간, IP 등 고정폭 Fontsource (Noto Sans Mono, variable)
  • Noto Sans에는 한글 글자가 없으므로 같은 문장 안에서도 한글은 자동으로 Noto Sans KR로 표시됩니다.
  • 한글 폰트는 한자를 제외해 용량을 줄였습니다(10MB → 1.3MB). 다시 만들 때는 아래 명령을 사용합니다.
pip install fonttools brotli
pyftsubset "NotoSansKR[wght].ttf" --flavor=woff2 --layout-features='*' --output-file=static/fonts/NotoSansKR-korean.woff2 `
  --unicodes="U+0020-007E,U+00A0-00FF,U+1100-11FF,U+2000-206F,U+2190-21FF,U+2460-24FF,U+25A0-25FF,U+2600-27BF,U+3000-303F,U+3131-318F,U+3200-321E,U+AC00-D7A3,U+FF01-FF5E"
  • 라이선스: SIL Open Font License 1.1 (static/fonts/OFL.txt)

제한 사항

  • Flask 개발 서버로 동작합니다. 여러 명이 함께 쓰려면 waitress 등 WSGI 서버로 실행하는 것이 좋습니다.
  • 인증 기능이 없으므로 --host 0.0.0.0으로 열 때는 접근 가능한 네트워크를 제한하세요.

About

Event Log Analysis Tool

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages