EDR
Endpoint Detection and Response (엔드포인트 탐지 및 대응)
엔드포인트(PC, 서버, 모바일 기기)에서 발생하는 보안 위협을 실시간으로 탐지하고, 조사하며, 자동 또는 수동으로 대응하는 보안 솔루션입니다. 시그니처 기반 안티바이러스의 한계를 넘어 행위 기반 분석으로 제로데이 공격과 APT를 방어합니다.
Endpoint Detection and Response (엔드포인트 탐지 및 대응)
엔드포인트(PC, 서버, 모바일 기기)에서 발생하는 보안 위협을 실시간으로 탐지하고, 조사하며, 자동 또는 수동으로 대응하는 보안 솔루션입니다. 시그니처 기반 안티바이러스의 한계를 넘어 행위 기반 분석으로 제로데이 공격과 APT를 방어합니다.
EDR(Endpoint Detection and Response)은 엔드포인트에서 발생하는 모든 활동을 지속적으로 모니터링하고 기록하여 위협을 탐지하고 대응하는 보안 솔루션입니다. 기존 안티바이러스(AV)가 알려진 맬웨어 시그니처를 탐지하는 반면, EDR은 프로세스 실행, 파일 변경, 네트워크 연결, 레지스트리 수정 등 시스템 행위를 분석하여 비정상적인 패턴을 식별합니다. 이는 "알려지지 않은 위협"에 대응할 수 있게 해줍니다.
EDR의 등장은 사이버 위협의 진화와 맞물려 있습니다. 2013년 Gartner의 Anton Chuvakin이 처음 제안한 이 개념은 APT(Advanced Persistent Threat) 공격의 증가에 대응하기 위해 탄생했습니다. 공격자들이 정상 시스템 도구(PowerShell, WMI)를 악용하는 Living-off-the-Land 기법과 파일리스 맬웨어를 사용하면서 시그니처 기반 탐지의 한계가 명확해졌고, 행위 기반 탐지의 필요성이 대두되었습니다.
EDR의 핵심 기능은 크게 네 가지입니다. 첫째, 텔레메트리 수집(Telemetry Collection)은 엔드포인트의 모든 활동 데이터를 수집합니다. 둘째, 위협 탐지(Threat Detection)는 머신러닝과 행위 분석으로 이상 행위를 식별합니다. 셋째, 조사(Investigation)는 타임라인 분석과 프로세스 트리 시각화로 공격 경로를 추적합니다. 넷째, 대응(Response)은 프로세스 종료, 파일 격리, 네트워크 차단 등 자동/수동 조치를 실행합니다.
현대 EDR은 EPP(Endpoint Protection Platform)와 통합되어 예방-탐지-대응을 단일 에이전트로 제공하며, XDR로 확장되어 네트워크, 클라우드, 이메일 등 전체 공격 표면을 통합 모니터링합니다. MITRE ATT&CK 프레임워크 매핑을 통해 공격자의 전술, 기법, 절차(TTP)를 식별하고, 위협 헌팅(Threat Hunting)을 통해 능동적으로 잠복 위협을 탐색하는 것이 고급 활용법입니다.
# EDR 텔레메트리 수집 및 분석 예제 - Python
# 엔드포인트에서 프로세스, 파일, 네트워크 활동을 수집하고 이상 탐지
import psutil
import os
import hashlib
import json
from datetime import datetime
from typing import Dict, List, Optional
from collections import defaultdict
import socket
class EDRTelemetryCollector:
"""EDR 텔레메트리 수집기 - 엔드포인트 활동 모니터링"""
def __init__(self):
self.baseline_processes = set()
self.suspicious_patterns = []
self.telemetry_buffer = []
def collect_process_telemetry(self) -> List[Dict]:
"""실행 중인 프로세스 정보 수집"""
processes = []
for proc in psutil.process_iter(['pid', 'name', 'exe', 'cmdline',
'username', 'create_time', 'ppid']):
try:
pinfo = proc.info
process_data = {
"timestamp": datetime.utcnow().isoformat(),
"event_type": "process_create",
"pid": pinfo['pid'],
"name": pinfo['name'],
"executable": pinfo['exe'],
"cmdline": ' '.join(pinfo['cmdline'] or []),
"username": pinfo['username'],
"parent_pid": pinfo['ppid'],
"parent_name": self._get_parent_name(pinfo['ppid']),
"file_hash": self._calculate_hash(pinfo['exe']),
"create_time": datetime.fromtimestamp(
pinfo['create_time']
).isoformat() if pinfo['create_time'] else None
}
# 의심스러운 프로세스 체인 탐지
process_data["suspicion_score"] = self._analyze_process_chain(
process_data
)
processes.append(process_data)
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
return processes
def _get_parent_name(self, ppid: int) -> Optional[str]:
"""부모 프로세스 이름 조회"""
try:
return psutil.Process(ppid).name()
except (psutil.NoSuchProcess, psutil.AccessDenied):
return None
def _calculate_hash(self, filepath: str) -> Optional[str]:
"""파일 SHA256 해시 계산"""
if not filepath or not os.path.exists(filepath):
return None
try:
with open(filepath, 'rb') as f:
return hashlib.sha256(f.read()).hexdigest()
except (IOError, PermissionError):
return None
def _analyze_process_chain(self, process: Dict) -> int:
"""프로세스 체인 분석 - 의심스러운 패턴 탐지"""
score = 0
# 1. 의심스러운 부모-자식 관계 탐지
suspicious_chains = [
("excel.exe", "powershell.exe"), # 매크로 실행
("outlook.exe", "powershell.exe"), # 피싱 첨부파일
("word.exe", "cmd.exe"), # 매크로 실행
("winword.exe", "certutil.exe"), # 문서 통한 다운로드
("explorer.exe", "mshta.exe"), # HTA 실행
]
parent = (process.get("parent_name") or "").lower()
child = (process.get("name") or "").lower()
if (parent, child) in suspicious_chains:
score += 80
# 2. 의심스러운 명령줄 패턴
cmdline = (process.get("cmdline") or "").lower()
suspicious_cmdline_patterns = [
"-encodedcommand", # PowerShell 인코딩된 명령
"downloadstring", # 원격 코드 다운로드
"-nop -w hidden", # 숨겨진 PowerShell
"invoke-expression", # 동적 코드 실행
"certutil -urlcache", # Certutil 다운로더
"bitsadmin /transfer", # BITS 다운로더
"regsvr32 /s /n /u", # 스크립트 실행 우회
]
for pattern in suspicious_cmdline_patterns:
if pattern in cmdline:
score += 60
# 3. 비정상 경로에서 실행
exe_path = (process.get("executable") or "").lower()
normal_paths = [
"c:\\windows", "c:\\program files",
"/usr/bin", "/usr/local/bin"
]
if exe_path and not any(exe_path.startswith(p) for p in normal_paths):
if "temp" in exe_path or "appdata" in exe_path:
score += 40
return min(score, 100) # 최대 100점
def collect_network_connections(self) -> List[Dict]:
"""네트워크 연결 정보 수집"""
connections = []
for conn in psutil.net_connections(kind='inet'):
try:
connection_data = {
"timestamp": datetime.utcnow().isoformat(),
"event_type": "network_connection",
"local_address": conn.laddr.ip if conn.laddr else None,
"local_port": conn.laddr.port if conn.laddr else None,
"remote_address": conn.raddr.ip if conn.raddr else None,
"remote_port": conn.raddr.port if conn.raddr else None,
"status": conn.status,
"pid": conn.pid,
"process_name": self._get_process_name(conn.pid)
}
# 의심스러운 연결 분석
connection_data["threat_indicators"] = self._analyze_connection(
connection_data
)
connections.append(connection_data)
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
return connections
def _get_process_name(self, pid: int) -> Optional[str]:
"""PID로 프로세스 이름 조회"""
try:
return psutil.Process(pid).name() if pid else None
except (psutil.NoSuchProcess, psutil.AccessDenied):
return None
def _analyze_connection(self, conn: Dict) -> List[str]:
"""네트워크 연결 위협 지표 분석"""
indicators = []
remote_port = conn.get("remote_port")
remote_addr = conn.get("remote_address")
# C2 통신에 자주 사용되는 포트
suspicious_ports = [4444, 5555, 8080, 8443, 1337, 31337]
if remote_port in suspicious_ports:
indicators.append(f"suspicious_port_{remote_port}")
# 비표준 포트로 HTTPS 연결
if remote_port and remote_port not in [80, 443, 8080, 8443]:
process = conn.get("process_name", "").lower()
if process in ["chrome.exe", "firefox.exe", "edge.exe"]:
indicators.append("browser_unusual_port")
return indicators
def collect_file_events(self, path: str) -> List[Dict]:
"""파일 시스템 이벤트 수집 (감시 대상 경로)"""
# 실제로는 watchdog 등 파일 시스템 감시 라이브러리 사용
events = []
monitored_extensions = ['.exe', '.dll', '.ps1', '.vbs', '.bat', '.js']
for root, dirs, files in os.walk(path):
for file in files:
if any(file.lower().endswith(ext) for ext in monitored_extensions):
filepath = os.path.join(root, file)
try:
stat = os.stat(filepath)
events.append({
"timestamp": datetime.utcnow().isoformat(),
"event_type": "file_create",
"path": filepath,
"size": stat.st_size,
"hash": self._calculate_hash(filepath),
"modified_time": datetime.fromtimestamp(
stat.st_mtime
).isoformat()
})
except (IOError, PermissionError):
continue
return events
def generate_alert(self, event: Dict, severity: str) -> Dict:
"""보안 알림 생성"""
return {
"alert_id": f"EDR-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
"timestamp": datetime.utcnow().isoformat(),
"severity": severity,
"hostname": socket.gethostname(),
"event": event,
"mitre_techniques": self._map_to_mitre(event),
"recommended_actions": self._get_recommendations(event, severity)
}
def _map_to_mitre(self, event: Dict) -> List[str]:
"""MITRE ATT&CK 기법 매핑"""
techniques = []
cmdline = (event.get("cmdline") or "").lower()
if "powershell" in cmdline and "encodedcommand" in cmdline:
techniques.append("T1059.001 - PowerShell")
if "certutil" in cmdline:
techniques.append("T1105 - Ingress Tool Transfer")
if "regsvr32" in cmdline:
techniques.append("T1218.010 - Regsvr32")
return techniques
def _get_recommendations(self, event: Dict, severity: str) -> List[str]:
"""대응 권장사항 생성"""
recommendations = []
if severity == "critical":
recommendations.append("즉시 해당 프로세스 종료")
recommendations.append("엔드포인트 네트워크 격리")
recommendations.append("메모리 덤프 수집")
if severity in ["critical", "high"]:
recommendations.append("관련 파일 격리 및 해시 차단 목록 추가")
recommendations.append("보안팀 에스컬레이션")
return recommendations
# 사용 예시
if __name__ == "__main__":
collector = EDRTelemetryCollector()
# 프로세스 텔레메트리 수집
processes = collector.collect_process_telemetry()
print("=== EDR 텔레메트리 분석 결과 ===")
for proc in processes:
if proc["suspicion_score"] > 50:
print(f"\n[경고] 의심스러운 프로세스 탐지!")
print(f" 프로세스: {proc['name']} (PID: {proc['pid']})")
print(f" 명령줄: {proc['cmdline'][:100]}...")
print(f" 부모: {proc['parent_name']}")
print(f" 의심 점수: {proc['suspicion_score']}/100")
# 알림 생성
alert = collector.generate_alert(proc, "high")
print(f" MITRE: {alert['mitre_techniques']}")
# EDR 위협 탐지 규칙 (Sigma 형식 기반) - YAML
# 행위 기반 탐지 규칙 정의
# 규칙 1: PowerShell 인코딩된 명령 실행 탐지
- rule:
name: "Encoded PowerShell Command Execution"
id: "EDR-001"
severity: high
mitre_attack:
- technique: T1059.001
tactic: Execution
- technique: T1027
tactic: Defense Evasion
description: |
Base64 인코딩된 PowerShell 명령 실행을 탐지합니다.
공격자가 명령을 난독화하여 탐지를 회피하려는 시도입니다.
detection:
process:
name:
- powershell.exe
- pwsh.exe
cmdline|contains|any:
- "-encodedcommand"
- "-enc "
- "-e "
- "frombase64string"
filter:
# 정상 관리 도구 제외
parent_process:
- sccm.exe
- intune*.exe
response:
- action: alert
priority: high
- action: collect_memory_dump
- action: block_network
condition: "severity >= high AND parent != sccm.exe"
# 규칙 2: LSASS 메모리 접근 탐지 (자격증명 덤프)
- rule:
name: "LSASS Memory Access - Credential Dumping"
id: "EDR-002"
severity: critical
mitre_attack:
- technique: T1003.001
tactic: Credential Access
description: |
LSASS 프로세스 메모리에 대한 접근을 탐지합니다.
자격증명 탈취를 위한 Mimikatz 등의 도구 사용 가능성.
detection:
process_access:
target_process: lsass.exe
access_mask|any:
- "0x1010" # PROCESS_VM_READ
- "0x1038" # PROCESS_VM_READ + PROCESS_QUERY_INFO
- "0x1fffff" # PROCESS_ALL_ACCESS
exclude:
source_process:
- csrss.exe
- wininit.exe
- smss.exe
- mrt.exe # Windows Malicious Software Removal Tool
response:
- action: alert
priority: critical
- action: terminate_process
condition: "source_process NOT IN whitelist"
- action: isolate_endpoint
condition: "confidence >= 0.9"
- action: collect_forensic_data
items:
- memory_dump
- process_tree
- network_connections
# 규칙 3: 랜섬웨어 행위 탐지
- rule:
name: "Ransomware Behavior Detection"
id: "EDR-003"
severity: critical
mitre_attack:
- technique: T1486
tactic: Impact
description: |
대량 파일 암호화, 섀도 복사본 삭제 등 랜섬웨어 특성 행위 탐지.
detection:
# 짧은 시간 내 대량 파일 수정
file_modification:
count: ">= 100"
time_window: "60s"
extensions|any:
- ".encrypted"
- ".locked"
- ".crypto"
- ".crypt"
# 또는 섀도 복사본 삭제
process:
cmdline|contains|any:
- "vssadmin delete shadows"
- "wmic shadowcopy delete"
- "bcdedit /set {default} recoveryenabled no"
response:
- action: alert
priority: critical
- action: terminate_process
immediate: true
- action: isolate_endpoint
immediate: true
- action: snapshot_filesystem
reason: "ransomware_evidence"
# 규칙 4: 지속성 메커니즘 설치 탐지
- rule:
name: "Persistence Mechanism Installation"
id: "EDR-004"
severity: high
mitre_attack:
- technique: T1547.001
tactic: Persistence
description: |
레지스트리 Run 키, 서비스, 스케줄 작업 등 지속성 메커니즘 설치 탐지.
detection:
registry_modification:
path|contains|any:
- "\\CurrentVersion\\Run"
- "\\CurrentVersion\\RunOnce"
- "\\CurrentVersion\\Policies\\Explorer\\Run"
value_data|contains:
- ".exe"
- ".dll"
- ".vbs"
- ".ps1"
# 또는 새 서비스 생성
process:
name: sc.exe
cmdline|contains: "create"
# 또는 스케줄 작업 생성
process:
name: schtasks.exe
cmdline|contains: "/create"
filter:
# 정상 설치 프로그램 제외
process:
signature:
- "Microsoft Corporation"
- "Adobe Inc."
is_signed: true
response:
- action: alert
priority: high
- action: collect_registry_snapshot
- action: query_user
message: "새로운 시작 프로그램이 등록되었습니다. 승인하시겠습니까?"
# 규칙 5: Living-off-the-Land 바이너리 (LOLBins) 악용
- rule:
name: "LOLBins Abuse Detection"
id: "EDR-005"
severity: medium
mitre_attack:
- technique: T1218
tactic: Defense Evasion
description: |
정상 Windows 바이너리를 악용한 코드 실행 탐지.
MITRE ATT&CK Sub-techniques 기반.
detection:
process|any:
# Certutil 다운로더
- name: certutil.exe
cmdline|contains|any:
- "-urlcache"
- "-verifyctl"
# Mshta 스크립트 실행
- name: mshta.exe
cmdline|contains|any:
- "javascript:"
- "vbscript:"
- "http://"
- "https://"
# Regsvr32 스크립트 실행
- name: regsvr32.exe
cmdline|contains:
- "/s /n /u"
- "scrobj.dll"
# WMIC 원격 실행
- name: wmic.exe
cmdline|contains:
- "process call create"
response:
- action: alert
priority: medium
- action: log_enhanced
include:
- parent_process_tree
- network_connections
- file_writes
# 전역 설정
global_settings:
telemetry:
collection_interval: 1s
buffer_size: 10000
compression: true
response:
auto_isolate_threshold: critical
require_approval_for:
- terminate_process
- isolate_endpoint
integration:
siem_endpoint: "https://siem.company.com/api/events"
soar_endpoint: "https://soar.company.com/api/incidents"
threat_intel_feeds:
- "https://threatintel.company.com/iocs"
# EDR 자동 대응 스크립트 - PowerShell
# 위협 탐지 시 자동화된 대응 조치 실행
param(
[Parameter(Mandatory=$true)]
[string]$AlertId,
[Parameter(Mandatory=$true)]
[string]$ThreatType,
[Parameter(Mandatory=$true)]
[int]$ProcessId,
[string]$Severity = "high"
)
# 로깅 설정
$LogPath = "C:\ProgramData\EDR\Logs\response_$(Get-Date -Format 'yyyyMMdd').log"
function Write-EDRLog {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp [$Level] $Message" | Add-Content -Path $LogPath
Write-Host "[$Level] $Message" -ForegroundColor $(
switch($Level) {
"ERROR" { "Red" }
"WARN" { "Yellow" }
"INFO" { "Green" }
default { "White" }
}
)
}
# 프로세스 정보 수집
function Get-ProcessDetails {
param([int]$Pid)
try {
$process = Get-Process -Id $Pid -ErrorAction Stop
$wmiProcess = Get-WmiObject Win32_Process -Filter "ProcessId=$Pid"
return @{
Name = $process.Name
Path = $process.Path
CommandLine = $wmiProcess.CommandLine
ParentId = $wmiProcess.ParentProcessId
StartTime = $process.StartTime
User = $wmiProcess.GetOwner().User
Hash = (Get-FileHash -Path $process.Path -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
}
} catch {
Write-EDRLog "프로세스 정보 수집 실패: $_" "ERROR"
return $null
}
}
# 프로세스 종료
function Stop-MaliciousProcess {
param([int]$Pid, [string]$Reason)
Write-EDRLog "프로세스 종료 시도: PID $Pid (사유: $Reason)"
try {
# 프로세스 정보 먼저 수집 (포렌식)
$details = Get-ProcessDetails -Pid $Pid
# 프로세스 종료
Stop-Process -Id $Pid -Force -ErrorAction Stop
Write-EDRLog "프로세스 종료 완료: $($details.Name) (PID: $Pid)"
return @{
Success = $true
ProcessDetails = $details
}
} catch {
Write-EDRLog "프로세스 종료 실패: $_" "ERROR"
return @{ Success = $false; Error = $_.Exception.Message }
}
}
# 파일 격리
function Invoke-FileQuarantine {
param([string]$FilePath)
$quarantinePath = "C:\ProgramData\EDR\Quarantine"
if (-not (Test-Path $quarantinePath)) {
New-Item -ItemType Directory -Path $quarantinePath -Force | Out-Null
}
try {
$hash = (Get-FileHash -Path $FilePath -Algorithm SHA256).Hash
$quarantineName = "$hash.quarantine"
$quarantineFile = Join-Path $quarantinePath $quarantineName
# 파일 해시 및 메타데이터 저장
$metadata = @{
OriginalPath = $FilePath
QuarantineTime = Get-Date -Format "o"
Hash = $hash
Size = (Get-Item $FilePath).Length
}
$metadata | ConvertTo-Json | Out-File "$quarantineFile.meta"
# 파일 이동 및 암호화 (간단한 XOR)
$bytes = [System.IO.File]::ReadAllBytes($FilePath)
$key = 0xAA
$encrypted = $bytes | ForEach-Object { $_ -bxor $key }
[System.IO.File]::WriteAllBytes($quarantineFile, $encrypted)
# 원본 파일 삭제
Remove-Item -Path $FilePath -Force
Write-EDRLog "파일 격리 완료: $FilePath -> $quarantineFile"
return @{
Success = $true
QuarantinePath = $quarantineFile
Hash = $hash
}
} catch {
Write-EDRLog "파일 격리 실패: $_" "ERROR"
return @{ Success = $false; Error = $_.Exception.Message }
}
}
# 네트워크 격리
function Invoke-NetworkIsolation {
param([switch]$Enable)
$ruleName = "EDR_Network_Isolation"
try {
if ($Enable) {
Write-EDRLog "네트워크 격리 활성화"
# 모든 아웃바운드 연결 차단 (EDR 서버 제외)
$edrServer = "edr-server.company.com"
$edrServerIP = [System.Net.Dns]::GetHostAddresses($edrServer)[0].IPAddressToString
# 기존 규칙 제거
Remove-NetFirewallRule -DisplayName "$ruleName*" -ErrorAction SilentlyContinue
# EDR 서버 제외하고 모든 아웃바운드 차단
New-NetFirewallRule -DisplayName "${ruleName}_Block_All" `
-Direction Outbound -Action Block -Enabled True
New-NetFirewallRule -DisplayName "${ruleName}_Allow_EDR" `
-Direction Outbound -Action Allow -RemoteAddress $edrServerIP `
-Enabled True
# DNS 허용 (EDR 통신용)
New-NetFirewallRule -DisplayName "${ruleName}_Allow_DNS" `
-Direction Outbound -Action Allow -RemotePort 53 `
-Protocol UDP -Enabled True
Write-EDRLog "네트워크 격리 완료 (EDR 서버 연결만 허용)"
} else {
Write-EDRLog "네트워크 격리 해제"
Remove-NetFirewallRule -DisplayName "$ruleName*" -ErrorAction SilentlyContinue
Write-EDRLog "네트워크 격리 해제 완료"
}
return @{ Success = $true }
} catch {
Write-EDRLog "네트워크 격리 실패: $_" "ERROR"
return @{ Success = $false; Error = $_.Exception.Message }
}
}
# 포렌식 데이터 수집
function Collect-ForensicData {
param([int]$Pid)
$forensicPath = "C:\ProgramData\EDR\Forensics\$AlertId"
New-Item -ItemType Directory -Path $forensicPath -Force | Out-Null
Write-EDRLog "포렌식 데이터 수집 시작"
try {
# 1. 프로세스 트리
$processTree = @()
$currentPid = $Pid
while ($currentPid -gt 0) {
$proc = Get-WmiObject Win32_Process -Filter "ProcessId=$currentPid"
if ($proc) {
$processTree += @{
PID = $proc.ProcessId
Name = $proc.Name
CommandLine = $proc.CommandLine
ParentPID = $proc.ParentProcessId
}
$currentPid = $proc.ParentProcessId
} else {
break
}
}
$processTree | ConvertTo-Json | Out-File "$forensicPath\process_tree.json"
# 2. 네트워크 연결
Get-NetTCPConnection -OwningProcess $Pid -ErrorAction SilentlyContinue |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State |
ConvertTo-Json | Out-File "$forensicPath\network_connections.json"
# 3. 로드된 모듈/DLL
Get-Process -Id $Pid -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Modules |
Select-Object ModuleName, FileName, Size |
ConvertTo-Json | Out-File "$forensicPath\loaded_modules.json"
# 4. 최근 레지스트리 변경
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
)
$regChanges = foreach ($path in $regPaths) {
if (Test-Path $path) {
Get-ItemProperty -Path $path | Select-Object *
}
}
$regChanges | ConvertTo-Json | Out-File "$forensicPath\registry_persistence.json"
Write-EDRLog "포렌식 데이터 수집 완료: $forensicPath"
return @{
Success = $true
Path = $forensicPath
}
} catch {
Write-EDRLog "포렌식 데이터 수집 실패: $_" "ERROR"
return @{ Success = $false; Error = $_.Exception.Message }
}
}
# 메인 대응 로직
function Invoke-EDRResponse {
Write-EDRLog "=" * 60
Write-EDRLog "EDR 자동 대응 시작"
Write-EDRLog "Alert ID: $AlertId"
Write-EDRLog "위협 유형: $ThreatType"
Write-EDRLog "대상 PID: $ProcessId"
Write-EDRLog "심각도: $Severity"
Write-EDRLog "=" * 60
$response = @{
AlertId = $AlertId
StartTime = Get-Date -Format "o"
Actions = @()
}
# 1. 포렌식 데이터 먼저 수집
Write-EDRLog "1단계: 포렌식 데이터 수집"
$forensicResult = Collect-ForensicData -Pid $ProcessId
$response.Actions += @{ Action = "forensic_collection"; Result = $forensicResult }
# 2. 위협 유형별 대응
switch ($ThreatType) {
"ransomware" {
Write-EDRLog "2단계: 랜섬웨어 대응 - 네트워크 격리 및 프로세스 종료"
$response.Actions += @{
Action = "network_isolation"
Result = Invoke-NetworkIsolation -Enable
}
$response.Actions += @{
Action = "process_termination"
Result = Stop-MaliciousProcess -Pid $ProcessId -Reason "Ransomware detected"
}
}
"credential_theft" {
Write-EDRLog "2단계: 자격증명 탈취 대응 - 프로세스 종료"
$response.Actions += @{
Action = "process_termination"
Result = Stop-MaliciousProcess -Pid $ProcessId -Reason "Credential theft attempt"
}
}
"malware" {
Write-EDRLog "2단계: 맬웨어 대응 - 프로세스 종료 및 파일 격리"
$processDetails = Get-ProcessDetails -Pid $ProcessId
$response.Actions += @{
Action = "process_termination"
Result = Stop-MaliciousProcess -Pid $ProcessId -Reason "Malware detected"
}
if ($processDetails.Path) {
$response.Actions += @{
Action = "file_quarantine"
Result = Invoke-FileQuarantine -FilePath $processDetails.Path
}
}
}
default {
Write-EDRLog "2단계: 일반 대응 - 프로세스 종료"
$response.Actions += @{
Action = "process_termination"
Result = Stop-MaliciousProcess -Pid $ProcessId -Reason "Threat detected: $ThreatType"
}
}
}
# 3. Critical 심각도면 네트워크 격리
if ($Severity -eq "critical" -and $ThreatType -ne "ransomware") {
Write-EDRLog "3단계: Critical 위협 - 네트워크 격리"
$response.Actions += @{
Action = "network_isolation"
Result = Invoke-NetworkIsolation -Enable
}
}
$response.EndTime = Get-Date -Format "o"
$response.Success = ($response.Actions | Where-Object { -not $_.Result.Success }).Count -eq 0
# 대응 결과 저장
$response | ConvertTo-Json -Depth 5 |
Out-File "C:\ProgramData\EDR\Responses\$AlertId.json"
Write-EDRLog "EDR 자동 대응 완료 (성공: $($response.Success))"
return $response
}
# 실행
Invoke-EDRResponse
"EDR에서 LSASS 메모리 접근 알림이 발생했습니다. 프로세스 트리를 보면 Word에서 PowerShell이 실행되고, 이후 Mimikatz로 추정되는 도구가 LSASS를 덤프하려 했습니다. MITRE ATT&CK T1003.001로 분류되며, 해당 엔드포인트는 이미 네트워크 격리 상태입니다. 추가 감염 여부를 확인하기 위해 같은 해시를 가진 파일이 있는 다른 엔드포인트를 스캔하겠습니다."
"기존 안티바이러스로는 파일리스 맬웨어와 Living-off-the-Land 공격을 막기 어렵습니다. EDR을 도입하면 행위 기반 탐지로 PowerShell 악용, 정상 도구를 통한 측면 이동 등을 탐지할 수 있습니다. 단, EDR만으로는 부족하고 SIEM/SOAR와 연동하여 자동화된 대응 체계를 구축해야 SOC 운영 효율이 높아집니다."
"지난 주 EDR 텔레메트리 기반 위협 헌팅 결과입니다. certutil을 이용한 파일 다운로드 패턴 6건, 비정상 시간대 PowerShell 원격 실행 2건을 발견했습니다. 대부분 오탐이었지만, 마케팅팀 PC 1대에서 실제 코발트 스트라이크 비콘 통신 시도를 확인했습니다. 해당 PC는 격리 후 재이미징 진행 중입니다."
모든 탐지에 자동 프로세스 종료나 네트워크 격리를 적용하면 오탐 시 업무 중단이 발생합니다. Critical 위협만 자동 대응하고, 나머지는 분석 후 수동 대응하거나 사용자 확인을 받는 단계적 접근이 필요합니다.
튜닝 없이 기본 룰셋만 사용하면 수천 건의 알림이 쏟아져 중요한 위협을 놓칩니다. 환경에 맞는 베이스라인 수립, 정상 프로세스 화이트리스트, 알림 우선순위 설정이 필수입니다.
APT 공격의 평균 체류 시간(Dwell Time)은 수개월입니다. 텔레메트리를 30일만 보관하면 과거 침해 흔적을 추적할 수 없습니다. 최소 90일, 가능하면 1년 이상 보관하고 핫/콜드 스토리지를 구분하세요.
MITRE ATT&CK 커버리지 확인, 위협 헌팅 정기 수행, SIEM/SOAR 연동으로 자동화, 포렌식 데이터 자동 수집 설정, 정기적인 탐지 규칙 튜닝을 구현하세요. EDR은 도입보다 운영 역량이 핵심입니다. SOC 팀의 분석 능력 향상에 투자하세요.