Communitygithub.com

IOCの無害化と共有パイプラインの構築

攻撃指標(IOC)を自動抽出・無害化・正規化し、STIX 2.1形式に変換してTAXIIサーバやMISPインスタンス経由で共有するパイプラインを構築します。

IOCの無害化と共有パイプラインの構築 とは?

このスキルは、セキュリティアナリストやSOC担当者向けに、悪意あるURL、IPアドレス、ドメイン、メールアドレス、ファイルハッシュなどのIOCを複数のソースから自動収集し、無害化(defanging)してヒトが安全に共有できるレポートを生成するパイプラインの構築方法を提供します。 まず、正規表現を使った抽出エンジンで生テキストからIOCを抽出し、ホワイトリストで誤検知を除外します。次に「hxxp://」や「[.]」などの標準的な無害化ルールを適用し、クリック不能な安全な形式に変換します。さらに、STIX 2.1形式のインジケータオブジェクトに変換して機械可読性を高め、最後にMISPイベントやTAXIIコレクションにプッシュすることで、組織内外との脅威情報共有を自動化します。 このスキルはPython 3.9以上で動作し、defang、ioc-fanger、stix2、requests、validatorsなどのライブラリを必要とします。CISAやMITRE ATT&CKのフレームワークとも互換性があり、レポート、Slack、メール、ペーストサイトなど、自動リンクが危険なあらゆるチャネルでの安全なIOC配布に役立ちます。

対応~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無害化・共有パイプラインの構築

概要

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インジケータパターンとTLPマーキング定義の知識
  • IOC取り込みのための脅威インテリジェンスフィードへのアクセス

主要な概念

IOC無害化の標準

無害化は、プロトコルおよびドメインのアクティブな構成要素を置き換えて実行を防ぎます。http:// は hxxp:// に、https:// は hxxps:// に、ドメイン/IP内のドットは [.] に、メール内の @ は [@] に置き換えられます。これは、自動リンクによって悪意あるインフラへのネットワーク接続がトリガーされる可能性がある、レポート、メール、Slackチャンネル、ペーストサイトでのIOC共有に不可欠です。

IOC正規化

異なるソースからの生のIOCは、一貫性のない形式で提供されます。正規化には、小文字への変換、末尾のスラッシュと空白の削除、URLからのドメイン抽出、URLエンコーディングの解決、形式の正確性の検証、ソース間の重複排除が含まれます。

STIX 2.1インジケータパターン

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
攻撃者[@]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インジケータオブジェクトに変換する。"""

    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": "ネットワークアクティビティ" if ioc_type in ("ipv4", "domain", "url") else "ペイロード配信",
                        "to_ids": True,
                    })

        # ラボ環境で自己署名証明書を使用する場合、SKIP_TLS_VERIFY=true を設定
        resp = requests.post(
            f"{misp_url}/events",
            headers=headers,
            json=event,
            verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "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が正しく抽出され、復元(refang)機能が動作すること
  • 無害化により、安全でクリックできないインジケータが生成されること
  • STIX 2.1バンドルに有効なインジケータパターンが含まれていること
  • 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,

関連スキル