MITRE ATT&CK Navigator를 사용한 APT 그룹 분석
개요
MITRE ATT&CK Navigator는 ATT&CK 매트릭스에 주석을 달고 탐색할 수 있는 웹 기반 도구로, 분석가가 위협 행위자의 기법 적용 범위를 시각화하고, 여러 APT 그룹을 비교하며, 탐지 격차를 식별하고, 위협 기반 방어 전략을 수립할 수 있도록 지원합니다. 이 스킬은 ATT&CK 데이터를 프로그래밍 방식으로 쿼리하고, APT 그룹 TTP를 Navigator 레이어에 매핑하며, 격차 분석을 위한 다중 레이어 오버레이를 생성하고, 탐지 엔지니어링 팀을 위한 실행 가능한 인텔리전스 보고서를 생성하는 방법을 다룹니다.
사용 시기
- MITRE Navigator를 사용한 APT 그룹 분석이 필요한 보안 사고를 조사할 때
- 이 도메인에 대한 탐지 규칙 또는 위협 헌팅 쿼리를 구축할 때
- SOC 분석가가 이 분석 유형에 대한 구조화된 절차가 필요할 때
- 관련 공격 기법에 대한 보안 모니터링 커버리지를 검증할 때
사전 요구 사항
- Python 3.9+ 및
attackcti,mitreattack-python,stix2,requests라이브러리 - ATT&CK Navigator (https://mitre-attack.github.io/attack-navigator/) 또는 로컬 배포
- ATT&CK Enterprise 매트릭스 이해: 14가지 전술, 200개 이상의 기법, 하위 기법
- 위협 인텔리전스 보고서 또는 위협 행위자 데이터를 위한 MISP/OpenCTI 접근 권한
- STIX 2.1 Intrusion Set 및 Attack Pattern 개체에 대한 이해
주요 개념
ATT&CK Navigator 레이어
Navigator 레이어는 ATT&CK 기법에 점수, 색상, 주석 및 메타데이터를 추가하는 JSON 파일입니다. 각 레이어는 단일 APT 그룹의 기법 사용, 탐지 기능 맵 또는 결합된 오버레이를 나타낼 수 있습니다. 레이어 버전 4.5는 enterprise-attack, mobile-attack 및 ics-attack 도메인을 지원하며, 플랫폼(Windows, Linux, macOS, Cloud, Azure AD, Office 365, SaaS)별 필터링이 가능합니다.
ATT&CK의 APT 그룹 프로필
ATT&CK은 140개 이상의 위협 그룹을 문서화된 기법 사용과 함께 카탈로그화합니다. 각 그룹 프로필에는 별칭, 대상 부문, 관련 캠페인, 사용된 소프트웨어 및 절차 수준 세부 정보가 포함된 기법 매핑이 포함됩니다. 그룹은 G-코드(예: APT29의 경우 G0016, APT28의 경우 G0007, Lazarus Group의 경우 G0032)로 식별됩니다.
다중 레이어 분석
Navigator는 여러 레이어를 동시에 로드하여 분석가가 탐지 커버리지에 대해 위협 행위자 TTP를 오버레이하여 격차를 식별하고, 우선 순위를 지정할 가치가 있는 공통 기법을 찾기 위해 여러 APT 그룹을 비교하며, 시간에 따른 기법 커버리지 변경 사항을 추적할 수 있도록 합니다.
워크플로우
1단계: APT 그룹에 대한 ATT&CK 데이터 쿼리
from attackcti import attack_client
import json
lift = attack_client()
# 모든 위협 그룹 가져오기
groups = lift.get_groups()
print(f"전체 ATT&CK 그룹 수: {len(groups)}")
# APT29(Cozy Bear / Midnight Blizzard) 찾기
apt29 = next((g for g in groups if g.get('name') == 'APT29'), None)
if apt29:
print(f"그룹: {apt29['name']}")
print(f"별칭: {apt29.get('aliases', [])}")
print(f"설명: {apt29.get('description', '')[:300]}")
# APT29(G0016)가 사용하는 기법 가져오기
techniques = lift.get_techniques_used_by_group("G0016")
print(f"APT29가 사용하는 기법 수: {len(techniques)}")
technique_map = {}
for tech in techniques:
tech_id = ""
for ref in tech.get("external_references", []):
if ref.get("source_name") == "mitre-attack":
tech_id = ref.get("external_id", "")
break
if tech_id:
tactics = [p.get("phase_name", "") for p in tech.get("kill_chain_phases", [])]
technique_map[tech_id] = {
"name": tech.get("name", ""),
"tactics": tactics,
"description": tech.get("description", "")[:500],
"platforms": tech.get("x_mitre_platforms", []),
"data_sources": tech.get("x_mitre_data_sources", []),
}
2단계: Navigator 레이어 JSON 생성
def create_navigator_layer(group_name, technique_map, color="#ff6666"):
techniques_list = []
for tech_id, info in technique_map.items():
for tactic in info["tactics"]:
techniques_list.append({
"techniqueID": tech_id,
"tactic": tactic,
"color": color,
"comment": info["name"],
"enabled": True,
"score": 100,
"metadata": [
{"name": "group", "value": group_name},
{"name": "platforms", "value": ", ".join(info["platforms"])},
],
})
layer = {
"name": f"{group_name} TTP 적용 범위",
"versions": {"attack": "16.1", "navigator": "5.1.0", "layer": "4.5"},
"domain": "enterprise-attack",
"description": f"{group_name}에 귀속된 기법",
"filters": {
"platforms": ["Linux", "macOS", "Windows", "Cloud",
"Azure AD", "Office 365", "SaaS", "Google Workspace"]
},
"sorting": 0,
"layout": {
"layout": "side", "aggregateFunction": "average",
"showID": True, "showName": True,
"showAggregateScores": False, "countUnscored": False,
},
"hideDisabled": False,
"techniques": techniques_list,
"gradient": {"colors": ["#ffffff", color], "minValue": 0, "maxValue": 100},
"legendItems": [
{"label": f"{group_name}가 사용", "color": color},
{"label": "관찰되지 않음", "color": "#ffffff"},
],
"showTacticRowBackground": True,
"tacticRowBackground": "#dddddd",
"selectTechniquesAcrossTactics": True,
"selectSubtechniquesWithParent": False,
"selectVisibleTechniques": False,
}
return layer
layer = create_navigator_layer("APT29", technique_map)
with open("apt29_layer.json", "w") as f:
json.dump(layer, f, indent=2)
print("[+] 레이어 저장됨: apt29_layer.json")
3단계: 여러 APT 그룹 비교
groups_to_compare = {"G0016": "APT29", "G0007": "APT28", "G0032": "Lazarus Group"}
group_techniques = {}
for gid, gname in groups_to_compare.items():
techs = lift.get_techniques_used_by_group(gid)
tech_ids = set()
for t in techs:
for ref in t.get("external_references", []):
if ref.get("source_name") == "mitre-attack":
tech_ids.add(ref.get("external_id", ""))
group_techniques[gname] = tech_ids
common_to_all = set.intersection(*group_techniques.values())
print(f"모든 그룹에 공통된 기법 수: {len(common_to_all)}")
for tid in sorted(common_to_all):
print(f" {tid}")
for gname, techs in group_techniques.items():
others = set.union(*[t for n, t in group_techniques.items() if n != gname])
unique = techs - others
print(f"\n{gname}에 고유한 기법: {len(unique)}개")
4단계: 레이어 오버레이를 통한 탐지 격차 분석
# 현재 탐지 기능 정의
detected_techniques = {
"T1059", "T1059.001", "T1071", "T1071.001", "T1566", "T1566.001",
"T1547", "T1547.001", "T1053", "T1053.005", "T1078", "T1027",
}
actor_techniques = set(technique_map.keys())
covered = actor_techniques.intersection(detected_techniques)
gaps = actor_techniques - detected_techniques
print(f"=== APT29 탐지 격차 분석 ===")
print(f"위협 행위자 기법 수: {len(actor_techniques)}")
print(f"탐지됨: {len(covered)} ({len(covered)/len(actor_techniques)*100:.0f}%)")
print(f"격차: {len(gaps)} ({len(gaps)/len(actor_techniques)*100:.0f}%)")
# 격차 레이어 생성 (빨간색 = 미탐지, 녹색 = 탐지)
gap_techniques = []
for tech_id in actor_techniques:
info = technique_map.get(tech_id, {})
for tactic in info.get("tactics", [""]):
color = "#66ff66" if tech_id in detected_techniques else "#ff3333"
gap_techniques.append({
"techniqueID": tech_id,
"tactic": tactic,
"color": color,
"comment": f"{'탐지됨' if tech_id in detected_techniques else '격차'}: {info.get('name', '')}",
"enabled": True,
"score": 100 if tech_id in detected_techniques else 0,
})
gap_layer = {
"name": "APT29 탐지 격차 분석",
"versions": {"attack": "16.1", "navigator": "5.1.0", "layer": "4.5"},
"domain": "enterprise-attack",
"description": "녹색 = 탐지됨, 빨간색 = 격차",
"techniques": gap_techniques,
"gradient": {"colors": ["#ff3333", "#66ff66"], "minValue": 0, "maxValue": 100},
"legendItems": [
{"label": "탐지됨", "color": "#66ff66"},
{"label": "탐지 격차", "color": "#ff3333"},
],
}
with open("apt29_gap_layer.json", "w") as f:
json.dump(gap_layer, f, indent=2)
5단계: 전술별 분석
from collections import defaultdict
tactic_breakdown = defaultdict(list)
for tech_id, info in technique_map.items():
for tactic in info["tactics"]:
tactic_breakdown[tactic].append({"id": tech_id, "name": info["name"]})
tactic_order = [
"reconnaissance", "resource-development", "initial-access",
"execution", "persistence", "privilege-escalation",
"defense-evasion", "credential-access", "discovery",
"lateral-movement", "collection", "command-and-control",
"exfiltration", "impact",
]
print("\n=== APT29 전술별 분석 ===")
for tactic in tactic_order:
techs = tactic_breakdown.get(tactic, [])
if techs:
print(f"\n{tactic.upper()} ({len(techs)}개 기법):")
for t in techs:
print(f" {t['id']}: {t['name']}")
검증 기준
- TAXII 서버를 통해 ATT&CK 데이터가 성공적으로 쿼리됨
- APT 그룹이 절차 예제를 포함한 모든 문서화된 기법에 매핑됨
- Navigator 레이어 JSON이 유효하며 ATT&CK Navigator에서 올바르게 렌더링됨
- 다중 레이어 오버레이가 위협 행위자 대 탐지 적용 범위를 보여줌
- 탐지 격차 분석이 모니터링되지 않은 기법을 식별하고 데이터 소스 권장 사항을 제공함
- 그룹 간 비교가 공유 및 고유 TTP를 보여줌
- 출력이 탐지 엔지니어링 우선 순위 지정에 실행 가능함