Agent skill

extracting-iocs-from-samples

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/extracting-iocs-from-samples

SKILL.md


name: extracting-iocs-from-samples description: >- Systematically extract Indicators of Compromise (IOCs) from malware samples including hashes, domains, IPs, URLs, mutexes, file paths, and registry keys. domain: cybersecurity subdomain: malware-analysis tags:

  • ioc-extraction
  • threat-intelligence
  • stix
  • openioc
  • malware-triage
  • hash-analysis
  • network-indicators version: "1.0" author: defconxt license: AGPL-3.0 metadata: mitre-attack: ["T1587.001"]

Extracting IOCs from Samples

Overview

Indicators of Compromise (IOCs) are forensic artifacts — file hashes, IP addresses, domain names, URLs, mutexes, registry keys, and behavioral patterns — that identify malicious activity. Systematic IOC extraction from malware samples enables threat detection, hunting, blocking, and intelligence sharing. IOCs feed into SIEMs, EDR platforms, firewalls, and threat intelligence platforms (TIPs). Output formats include STIX 2.1, OpenIOC, CSV, and JSON for automated ingestion.

Prerequisites

  • Python 3.10+ with iocextract, pefile, yara-python, stix2
  • strings, floss for string extraction
  • VirusTotal API key for enrichment
  • MISP or OpenCTI for IOC sharing
  • Isolated analysis environment

Key Concepts

IOC Types and Confidence

IOC Type Example Confidence Shelf Life
File hash (SHA256) a1b2c3... Very High Permanent (for that sample)
File hash (MD5) d4e5f6... High Permanent (collision risk)
Domain evil.example.com Medium-High Weeks-months
IP address 192.168.1.100 Medium Days-weeks (dynamic IPs)
URL http://evil.com/payload High Days-weeks
Mutex Global\\MyMutex High Months (sample-specific)
Registry key HKLM\...\Run\malware Medium Varies
User-Agent Mozilla/5.0 Custom/1.0 Medium Months
YARA rule Behavioral pattern Very High Months-years
JARM hash TLS fingerprint High Months

IOC Pyramid of Pain

From easiest (bottom) to hardest (top) for attackers to change:

  1. Hash values — trivial to change (recompile)
  2. IP addresses — easy to change (new infrastructure)
  3. Domain names — moderate effort (new registration)
  4. Network artifacts — significant effort (new C2 profile)
  5. Host artifacts — significant effort (new TTPs)
  6. Tools — hard (develop new tooling)
  7. TTPs — very hard (change methodology)

Workflow

Step 1: Hash Generation

bash
# Multiple hash types
md5sum sample.exe
sha1sum sample.exe
sha256sum sample.exe
ssdeep sample.exe        # Fuzzy hash for similarity
tlsh sample.exe          # Trend Micro Locality Sensitive Hash
imphash sample.exe       # Import hash (pefile)

# Python hash generation
python3 -c "
import hashlib, pefile
data = open('sample.exe', 'rb').read()
print(f'MD5:    {hashlib.md5(data).hexdigest()}')
print(f'SHA1:   {hashlib.sha1(data).hexdigest()}')
print(f'SHA256: {hashlib.sha256(data).hexdigest()}')
pe = pefile.PE('sample.exe')
print(f'Imphash: {pe.get_imphash()}')
"

Step 2: String-Based IOC Extraction

bash
# Raw strings
strings -a -n 6 sample.exe > strings.txt

# FLOSS for obfuscated strings
floss sample.exe -j > floss_output.json

# Extract with regex patterns
grep -oP 'https?://[^\s"'"'"']+' strings.txt              # URLs
grep -oP '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' strings.txt  # IPs
grep -oP '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}' strings.txt      # Domains
grep -oP 'HKLM\\[^\s"]+|HKCU\\[^\s"]+' strings.txt        # Registry

Step 3: Automated IOC Extraction

python
#!/usr/bin/env python3
"""Comprehensive IOC extraction from malware samples."""
import hashlib
import re
import json
from pathlib import Path
from ipaddress import ip_address

# IOC regex patterns
IOC_PATTERNS = {
    "ipv4": r'\b(?:(?:25[0-5]|2[0-4]\d|1?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|1?\d\d?)\b',
    "ipv6": r'\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b',
    "url": r'https?://[^\s<>"\'{}|\\^`\[\]]+',
    "domain": r'\b[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,6}\b',
    "email": r'\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-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',
    "registry": r'(?:HKLM|HKCU|HKCR|HKU|HKCC)\\[^\s"\']+',
    "filepath_win": r'[A-Z]:\\(?:[^\s<>"\'|]+\\)*[^\s<>"\'|]+',
    "filepath_unix": r'(?:/(?:tmp|var|etc|usr|opt|home|root)/[^\s"\']+)',
    "mutex": r'(?:Global\\|Local\\)[^\s"\']+',
    "useragent": r'Mozilla/5\.0[^\r\n"\']{10,}',
    "bitcoin": r'\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b',
}

# False positive filters
FP_DOMAINS = {'example.com', 'microsoft.com', 'google.com', 'localhost',
              'schema.org', 'w3.org', 'xmlsoap.org'}
FP_IPS = {'0.0.0.0', '127.0.0.1', '255.255.255.255'}

def extract_iocs(text: str) -> dict:
    """Extract all IOC types from text."""
    iocs = {}
    for ioc_type, pattern in IOC_PATTERNS.items():
        matches = set(re.findall(pattern, text))
        # Filter false positives
        if ioc_type == "domain":
            matches -= FP_DOMAINS
            matches = {d for d in matches if '.' in d and not d.endswith('.exe')}
        elif ioc_type == "ipv4":
            matches -= FP_IPS
            matches = {ip for ip in matches if is_routable(ip)}
        iocs[ioc_type] = sorted(matches)
    return {k: v for k, v in iocs.items() if v}

def is_routable(ip_str: str) -> bool:
    """Check if IP is routable (not private/reserved)."""
    try:
        ip = ip_address(ip_str)
        return not (ip.is_private or ip.is_reserved or ip.is_loopback)
    except ValueError:
        return False

def extract_from_file(filepath: str) -> dict:
    """Full IOC extraction pipeline for a file."""
    path = Path(filepath)
    data = path.read_bytes()
    text = data.decode('utf-8', errors='replace')

    # File hashes
    file_iocs = {
        "file_hashes": {
            "md5": hashlib.md5(data).hexdigest(),
            "sha1": hashlib.sha1(data).hexdigest(),
            "sha256": hashlib.sha256(data).hexdigest(),
        },
        "file_size": len(data),
        "file_name": path.name,
    }

    # String-based IOCs
    string_iocs = extract_iocs(text)
    file_iocs["network_iocs"] = {
        "urls": string_iocs.get("url", []),
        "domains": string_iocs.get("domain", []),
        "ips": string_iocs.get("ipv4", []),
        "emails": string_iocs.get("email", []),
    }
    file_iocs["host_iocs"] = {
        "registry_keys": string_iocs.get("registry", []),
        "file_paths": string_iocs.get("filepath_win", []) + string_iocs.get("filepath_unix", []),
        "mutexes": string_iocs.get("mutex", []),
    }
    file_iocs["crypto"] = {
        "bitcoin_addresses": string_iocs.get("bitcoin", []),
    }

    return file_iocs

Step 4: VirusTotal Enrichment

python
import requests

def enrich_hash_vt(sha256: str, api_key: str) -> dict:
    """Enrich file hash via VirusTotal API."""
    url = f"https://www.virustotal.com/api/v3/files/{sha256}"
    headers = {"x-apikey": api_key}
    resp = requests.get(url, headers=headers, timeout=30)
    if resp.status_code == 200:
        data = resp.json()["data"]["attributes"]
        return {
            "detection_ratio": f"{data['last_analysis_stats']['malicious']}/{sum(data['last_analysis_stats'].values())}",
            "first_seen": data.get("first_submission_date"),
            "names": data.get("names", [])[:5],
            "tags": data.get("tags", []),
            "type": data.get("type_description", ""),
        }
    return {"error": f"VT API returned {resp.status_code}"}

Step 5: Export as STIX 2.1

python
from stix2 import Indicator, Bundle, Malware, Relationship
from datetime import datetime

def export_stix(iocs: dict) -> str:
    """Export IOCs as STIX 2.1 bundle."""
    objects = []

    # File hash indicator
    sha256 = iocs["file_hashes"]["sha256"]
    file_indicator = Indicator(
        name=f"Malware hash: {iocs['file_name']}",
        pattern=f"[file:hashes.'SHA-256' = '{sha256}']",
        pattern_type="stix",
        valid_from=datetime.utcnow(),
    )
    objects.append(file_indicator)

    # Network indicators
    for domain in iocs.get("network_iocs", {}).get("domains", []):
        ind = Indicator(
            name=f"Malicious domain: {domain}",
            pattern=f"[domain-name:value = '{domain}']",
            pattern_type="stix",
            valid_from=datetime.utcnow(),
        )
        objects.append(ind)

    for ip in iocs.get("network_iocs", {}).get("ips", []):
        ind = Indicator(
            name=f"Malicious IP: {ip}",
            pattern=f"[ipv4-addr:value = '{ip}']",
            pattern_type="stix",
            valid_from=datetime.utcnow(),
        )
        objects.append(ind)

    bundle = Bundle(objects=objects)
    return bundle.serialize(pretty=True)

Detection

yaml
title: Extracting Iocs From Samples Detection
id: ea5ef027-7667-4f5c-9eb4-e2bd476e0913
status: experimental
description: Detects suspicious activity related to extracting iocs from samples techniques in malware analysis context
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    CommandLine: "*extracting*iocs*"
  condition: selection
level: high
tags:
  - attack.t1587.001
  - attack.execution
falsepositives:
  - Sandbox detonation environments analyzing submitted samples

Verification

  • All hash types generated (MD5, SHA1, SHA256, imphash, ssdeep)
  • Network IOCs extracted (domains, IPs, URLs)
  • Host IOCs extracted (registry, files, mutexes)
  • False positives filtered (private IPs, common domains)
  • IOCs enriched via VT/threat intel
  • Output exported in standard format (STIX/CSV/JSON)
  • IOCs shared with detection team and TIP

References

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results