Communitygithub.com

IOC Defanging 및 공유 파이프라인 구축

원시 IOC(URL, IP, 도메인 등)를 여러 소스에서 수집하여 정규화 및 중복 제거하고, 안전한 인간 소비를 위해 디펜징한 후 STIX 2.1 형식으로 변환하여 TAXII 서버, MISP 인스턴스 및 이메일 보고서를 통해 배포하는 자동화된 파이프라인을 구축합니다.

IOC Defanging 및 공유 파이프라인 구축란 무엇인가요?

이 스킬은 보안 분석가와 Threat Intelligence 팀이 악성 IOC(URL, IP, 도메인, 이메일, 파일 해시)를 안전하게 처리하고 공유할 수 있는 완전한 파이프라인을 구축하는 방법을 제공합니다. `defang`, `ioc-fanger`, `stix2` 라이브러리를 활용하여 원시 텍스트에서 IOC를 추출하고, 프로토콜과 도메인 구성 요소를 비활성화하는 디펜징(예: `http://` → `hxxp://`, `.` → `[.]`)을 적용하며, 중복 제거 및 화이트리스트 기반 필터링을 수행합니다. 이후 STIX 2.1 Indicator 패턴으로 변환하여 MISP 이벤트 생성 및 TAXII 2.1 컬렉션 게시를 통해 자동 배포합니다. 이 파이프라인은 보고서, 슬랙 채널, 이메일 등에서 실수로 악성 인프라에 연결되는 것을 방지하면서도 가독성을 유지하는 데 필수적입니다. 주요 지원 플랫폼은 Python 3.9+ 기반으로 MISP 및 TAXII 서버와 연동됩니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main/skills/building-ioc-defanging-and-sharing-pipeline

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

IOC Defanging 및 공유 파이프라인 구축은(는) 무엇을 하나요?

개요

IOC 디펜징은 잠재적으로 악성인 지표(URL, IP 주소, 도메인, 이메일 주소)를 변환하여 실수로 클릭하거나 실행되는 것을 방지하면서도 분석 및 공유를 위한 가독성을 유지합니다. 이 스킬은 여러 소스에서 원시 IOC를 수집하고, 정규화 및 중복 제거한 후 안전한 인간 소비를 위해 디펜징하고, 기계 소비를 위해 STIX 2.1 형식으로 변환한 다음 TAXII 서버, MISP 인스턴스 및 이메일 보고서를 통해 배포하는 자동화된 파이프라인을 구축하는 방법을 다룹니다.

사용 시기

  • 사용자 환경에서 IOC 디펜징 및 공유 파이프라인을 구축하거나 구성할 때
  • 규정 준수 요구 사항에 맞춰 보안 제어를 수립할 때
  • 이 도메인의 보안 아키텍처를 구축하거나 개선할 때
  • 이 구현이 필요한 보안 평가를 수행할 때

사전 요구 사항

  • Python 3.9+ 및 defang, ioc-fanger, stix2, requests, validators 라이브러리
  • 자동 공유를 위한 MISP 인스턴스 또는 TAXII 서버
  • IOC 유형 이해: IPv4/IPv6, 도메인, URL, 이메일 주소, 파일 해시
  • STIX 2.1 Indicator 패턴 및 TLP 마킹 정의에 대한 친숙함
  • IOC 수집을 위한 위협 인텔리전스 피드 접근 권한

핵심 개념

IOC 디펜징 표준

디펜징은 실행을 방지하기 위해 활성 프로토콜 및 도메인 구성 요소를 대체합니다: http:// → hxxp://, https:// → hxxps://, 도메인/IP의 점은 [.], 이메일의 @는 [@]가 됩니다. 이는 보고서, 이메일, Slack 채널 및 Paste 사이트에서 자동 링크가 악성 인프라에 대한 네트워크 연결을 트리거할 수 있는 경우에 공유할 때 중요합니다.

IOC 정규화

다른 소스의 원시 IOC는 일관되지 않은 형식으로 제공됩니다. 정규화는 소문자 변환, 후행 슬래시 및 공백 제거, URL에서 도메인 추출, URL 인코딩 해결, 형식 정확성 검증 및 소스 간 중복 제거를 포함합니다.

STIX 2.1 Indicator 패턴

STIX 패턴은 IOC를 표준화된 형식으로 표현합니다: [ipv4-addr:value = '203.0.113.1'], [domain-name:value = 'malicious.example.com'], [url:value = 'http://evil.com/payload'], [file:hashes.'SHA-256' = 'abc123...']. 각 지표는 valid_from, indicator_types, confidence 및 선택적 TLP 마킹을 포함합니다.

워크플로우

단계 1: IOC 추출 및 정규화 구축

import re
import hashlib
from urllib.parse import urlparse, unquote
from datetime import datetime

class IOCExtractor:
    """텍스트에서 IOC를 추출하고 정규화합니다."""

    PATTERNS = {
        "ipv4": r'\b(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\b',
        "domain": r'\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\b',
        "url": r'https?://[^\s<>"{}|\\^`\[\]]+',
        "email": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
        "md5": r'\b[a-fA-F0-9]{32}\b',
        "sha1": r'\b[a-fA-F0-9]{40}\b',
        "sha256": r'\b[a-fA-F0-9]{64}\b',
    }

    WHITELIST_DOMAINS = {
        "google.com", "microsoft.com", "amazon.com", "github.com",
        "cloudflare.com", "akamai.com", "example.com",
    }

    def extract_from_text(self, text):
        """자유 텍스트에서 모든 IOC 유형을 추출합니다."""
        # 먼저 이미 디펜징된 지표를 리펜징합니다
        text = self._refang(text)
        iocs = {"ipv4": set(), "domain": set(), "url": set(),
                "email": set(), "md5": set(), "sha1": set(), "sha256": set()}

        for ioc_type, pattern in self.PATTERNS.items():
            matches = re.findall(pattern, text)
            for match in matches:
                normalized = self._normalize(match, ioc_type)
                if normalized and not self._is_whitelisted(normalized, ioc_type):
                    iocs[ioc_type].add(normalized)

        # URL의 일부인 도메인 제거
        url_domains = set()
        for url in iocs["url"]:
            parsed = urlparse(url)
            url_domains.add(parsed.netloc)
        iocs["domain"] -= url_domains

        total = sum(len(v) for v in iocs.values())
        print(f"[+] 텍스트에서 {total}개의 고유 IOC 추출됨")
        return {k: sorted(v) for k, v in iocs.items()}

    def _refang(self, text):
        """디펜징된 지표를 활성 형태로 변환합니다."""
        text = text.replace("hxxp://", "http://").replace("hxxps://", "https://")
        text = text.replace("[.]", ".").replace("[@]", "@")
        text = text.replace("[://]", "://").replace("(.)", ".")
        return text

    def _normalize(self, value, ioc_type):
        """IOC 값을 정규화합니다."""
        value = value.strip().lower()
        if ioc_type == "url":
            value = unquote(value).rstrip("/")
        elif ioc_type == "domain":
            value = value.rstrip(".")
        return value

    def _is_whitelisted(self, value, ioc_type):
        """IOC가 화이트리스트에 있는지 확인합니다."""
        if ioc_type == "domain":
            return value in self.WHITELIST_DOMAINS
        if ioc_type == "url":
            parsed = urlparse(value)
            return parsed.netloc in self.WHITELIST_DOMAINS
        return False

extractor = IOCExtractor()
sample_text = """
악성코드 C2: hxxps://evil-domain[.]com/beacon
192.168.1.100에서 페이로드 드롭 및 10[.]0[.]0[.]1 접촉
SHA256: 275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
attacker[@]phishing-domain[.]com에서 피싱 이메일
"""
iocs = extractor.extract_from_text(sample_text)

단계 2: 디펜징 엔진

class IOCDefanger:
    """보고서 및 커뮤니케이션에서 안전하게 공유하기 위해 IOC를 디펜징합니다."""

    def defang_url(self, url):
        return url.replace("http://", "hxxp://").replace("https://", "hxxps://").replace(".", "[.]")

    def defang_domain(self, domain):
        return domain.replace(".", "[.]")

    def defang_ip(self, ip):
        return ip.replace(".", "[.]")

    def defang_email(self, email):
        return email.replace("@", "[@]").replace(".", "[.]")

    def defang_all(self, iocs):
        """딕셔너리의 모든 IOC를 디펜징합니다."""
        defanged = {}
        for ioc_type, values in iocs.items():
            if ioc_type == "url":
                defanged[ioc_type] = [self.defang_url(v) for v in values]
            elif ioc_type == "domain":
                defanged[ioc_type] = [self.defang_domain(v) for v in values]
            elif ioc_type == "ipv4":
                defanged[ioc_type] = [self.defang_ip(v) for v in values]
            elif ioc_type == "email":
                defanged[ioc_type] = [self.defang_email(v) for v in values]
            else:
                defanged[ioc_type] = values  # 해시는 디펜징 불필요
        return defanged

    def generate_sharing_report(self, iocs, defanged, report_name="IOC 보고서"):
        """사람이 읽을 수 있는 디펜징된 IOC 보고서를 생성합니다."""
        report = f"# {report_name}\n"
        report += f"생성일: {datetime.now().isoformat()}\n\n"

        for ioc_type in ["url", "domain", "ipv4", "email", "sha256", "sha1", "md5"]:
            values = defanged.get(ioc_type, [])
            if values:
                report += f"## {ioc_type.upper()} ({len(values)}개)\n"
                for v in values:
                    report += f"- `{v}`\n"
                report += "\n"

        return report

defanger = IOCDefanger()
defanged = defanger.defang_all(iocs)
report = defanger.generate_sharing_report(iocs, defanged, "악성코드 캠페인 IOC")
print(report)

단계 3: STIX 2.1 형식으로 변환

from stix2 import Indicator, Bundle, TLP_WHITE, TLP_GREEN, TLP_AMBER
from datetime import datetime

class STIXConverter:
    """원시 IOC를 STIX 2.1 Indicator 객체로 변환합니다."""

    TLP_MAP = {"white": TLP_WHITE, "green": TLP_GREEN, "amber": TLP_AMBER}

    def iocs_to_stix(self, iocs, tlp="green", confidence=75):
        """IOC 딕셔너리를 STIX 2.1 번들로 변환합니다."""
        stix_objects = []
        marking = self.TLP_MAP.get(tlp, TLP_GREEN)

        for ip in iocs.get("ipv4", []):
            stix_objects.append(Indicator(
                name=f"악성 IP: {ip}",
                pattern=f"[ipv4-addr:value = '{ip}']",
                pattern_type="stix",
                valid_from=datetime.now(),
                indicator_types=["malicious-activity"],
                confidence=confidence,
                object_marking_refs=[marking],
            ))

        for domain in iocs.get("domain", []):
            stix_objects.append(Indicator(
                name=f"악성 도메인: {domain}",
                pattern=f"[domain-name:value = '{domain}']",
                pattern_type="stix",
                valid_from=datetime.now(),
                indicator_types=["malicious-activity"],
                confidence=confidence,
                object_marking_refs=[marking],
            ))

        for url in iocs.get("url", []):
            escaped = url.replace("'", "\\'")
            stix_objects.append(Indicator(
                name=f"악성 URL: {url[:60]}",
                pattern=f"[url:value = '{escaped}']",
                pattern_type="stix",
                valid_from=datetime.now(),
                indicator_types=["malicious-activity"],
                confidence=confidence,
                object_marking_refs=[marking],
            ))

        for sha256 in iocs.get("sha256", []):
            stix_objects.append(Indicator(
                name=f"악성 파일 해시: {sha256[:16]}...",
                pattern=f"[file:hashes.'SHA-256' = '{sha256}']",
                pattern_type="stix",
                valid_from=datetime.now(),
                indicator_types=["malicious-activity"],
                confidence=confidence,
                object_marking_refs=[marking],
            ))

        bundle = Bundle(objects=stix_objects)
        print(f"[+] {len(stix_objects)}개의 지표로 STIX 번들 생성됨")
        return bundle

converter = STIXConverter()
stix_bundle = converter.iocs_to_stix(iocs, tlp="amber", confidence=80)
with open("iocs_stix_bundle.json", "w") as f:
    f.write(stix_bundle.serialize(pretty=True))

단계 4: MISP 및 TAXII를 통해 배포

import requests
import json

class IOCDistributor:
    """다양한 채널을 통해 IOC를 배포합니다."""

    def push_to_misp(self, iocs, misp_url, misp_key, event_info):
        """IOC를 MISP에 새 이벤트로 푸시합니다."""
        headers = {
            "Authorization": misp_key,
            "Content-Type": "application/json",
            "Accept": "application/json",
        }
        event = {
            "Event": {
                "info": event_info,
                "distribution": "1",  # 이 커뮤니티만
                "threat_level_id": "2",  # 중간
                "analysis": "2",  # 완료됨
                "Attribute": [],
            }
        }

        type_mapping = {
            "ipv4": "ip-dst",
            "domain": "domain",
            "url": "url",
            "email": "email-src",
            "md5": "md5",
            "sha1": "sha1",
            "sha256": "sha256",
        }

        for ioc_type, values in iocs.items():
            misp_type = type_mapping.get(ioc_type)
            if misp_type:
                for value in values:
                    event["Event"]["Attribute"].append({
                        "type": misp_type,
                        "value": value,
                        "category": "Network activity" if ioc_type in ("ipv4", "domain", "url") else "Payload delivery",
                        "to_ids": True,
                    })

        resp = requests.post(
            f"{misp_url}/events",
            headers=headers,
            json=event,
            verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true",  # 실습 환경에서 자체 서명 인증서 사용 시 SKIP_TLS_VERIFY=true 설정
        )
        if resp.status_code == 200:
            event_id = resp.json().get("Event", {}).get("id", "")
            print(f"[+] MISP 이벤트 생성됨: {event_id}")
            return event_id
        else:
            print(f"[-] MISP 오류: {resp.status_code} - {resp.text[:200]}")
            return None

    def push_to_taxii(self, stix_bundle, taxii_url, collection_id, username, password):
        """STIX 번들을 TAXII 2.1 컬렉션으로 푸시합니다."""
        from taxii2client.v21 import Collection
        collection = Collection(
            f"{taxii_url}/collections/{collection_id}/",
            user=username, password=password,
        )
        response = collection.add_objects(stix_bundle.serialize())
        print(f"[+] TAXII: 번들 게시됨, 상태: {response.status}")
        return response

distributor = IOCDistributor()
distributor.push_to_misp(
    iocs,
    misp_url="https://misp.organization.com",
    misp_key="YOUR_MISP_API_KEY",
    event_info="악성코드 캠페인 IOC - 2025",
)

검증 기준

  • IOC가 리펜징 지원과 함께 자유 텍스트에서 올바르게 추출됨
  • 디펜징이 안전하고 클릭할 수 없는 지표를 생성함
  • STIX 2.1 번들에 유효한 Indicator 패턴이 포함됨
  • IOC가 MISP 및 TAXII로 성공적으로 배포됨
  • 중복 제거로 중복 지표 방지
  • 화이트리스트가 알려진 양호한 도메인에 대한 오탐 방지

참고 자료

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

abusing-dpapi-for-credential-access

Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys) online or offline using SharpDPAPI, SharpChrome, Mimikatz, or Impacket

abusing-shadow-credentials-for-privesc

Take over Active Directory accounts by writing attacker-controlled public keys to msDS-KeyCredentialLink (Shadow Credentials) with pyWhisker, Whisker, or Certipy, then authenticate via PKINIT to recover the target

achieving-cmmc-level-2-compliance

>-

acquiring-disk-image-with-dd-and-dcfldd

Create forensically sound bit-for-bit disk images with dd or dcfldd on a Linux forensic workstation, preserving evidence integrity through hash verification (MD5/SHA) during acquisition. Use when imaging a suspect drive, USB device, or memory card for investigation, preserving volatile disk evidence during incident response, or producing a verified copy for legal or law-enforcement proceedings before any destructive analysis.

analyzing-active-directory-acl-abuse

Detect dangerous ACL misconfigurations in Active Directory using ldap3

analyzing-android-malware-with-apktool

Perform static analysis of Android APK malware using apktool for resource decompilation, jadx for Java source recovery, and androguard for manifest inspection, dangerous permission-combination detection, and identification of obfuscated code, dynamic code loading, and reflection-based API calls. Use to statically triage a suspicious APK without executing it or to build mobile malware detection rules.

analyzing-api-gateway-access-logs

Parses API Gateway access logs (AWS API Gateway, Kong, Nginx) to detect

analyzing-apt-group-with-mitre-navigator

Query ATT&CK data with attackcti, mitreattack-python, and stix2, then build MITRE ATT&CK Navigator layers and multi-layer heatmap overlays mapping one or more APT groups

analyzing-azure-activity-logs-for-threats

Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query

analyzing-bootkit-and-rootkit-samples

Analyzes bootkit and advanced rootkit malware infecting the Master

analyzing-browser-forensics-with-hindsight

Parse Chromium-based browser databases with Hindsight to extract and correlate browsing history, downloads, cookies, cached content, autofill data, saved passwords, and extensions from Chrome, Edge, Brave, Opera, and Vivaldi into a unified timeline (XLSX, JSON, or SQLite output). Use during incident response, insider-threat investigations, or criminal cases when you need to reconstruct a user

analyzing-campaign-attribution-evidence

Systematically evaluate cyber-campaign evidence to attribute an operation to a threat actor, using the Diamond Model and Analysis of Competing Hypotheses (ACH) to weigh infrastructure overlaps, TTP consistency, malware code similarity, and timing/language artifacts into confidence-weighted attribution assessments. Use when an incident investigation needs a defensible attribution confidence level.

analyzing-certificate-transparency-for-phishing

Monitor Certificate Transparency logs using crt.sh and Certstream to

analyzing-cloud-storage-access-patterns

Detect abnormal access in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics for after-hours bulk downloads, new-IP access, and API-call spikes (e.g. GetObject) via statistical baselines and time-series anomaly detection. Use when investigating suspected cloud data exfiltration or building related detection rules.

analyzing-cobalt-strike-beacon-configuration

Extract and analyze Cobalt Strike beacon configuration from PE files

analyzing-cobaltstrike-malleable-c2-profiles

Parse and analyze Cobalt Strike Malleable C2 profiles with dissect.cobaltstrike (profiles and beacon-payload configs) and pyMalleableC2 (AST parsing) to extract HTTP/DNS transforms, URIs, headers, sleep/jitter, and injection behavior, then generate network detection signatures. Use when reverse-engineering a captured malleable profile or building detections against Cobalt Strike Beacon traffic.

analyzing-command-and-control-communication

Analyzes malware C2 communication over HTTP, HTTPS, DNS, and custom

analyzing-cyber-kill-chain

Analyzes intrusion activity against the Lockheed Martin Cyber Kill Chain

analyzing-disk-image-with-autopsy

Perform comprehensive forensic analysis of raw (dd), E01, or AFF disk images with Autopsy and The Sleuth Kit, recovering deleted files, examining metadata and embedded artifacts, keyword searching, and building investigation timelines with visual reports. Use for structured analysis of a forensic disk image or when stakeholders need visual reports from evidence.

analyzing-dns-logs-for-exfiltration

Analyzes DNS query logs to detect data exfiltration via DNS tunneling,

관련 스킬