reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
name: extracting-iocs-from-analysis-output
|
||||
description: 'Extracts indicators of compromise from raw analysis artifacts: parsing strings
|
||||
dumps, sandbox reports, PCAP summaries, and logs for URLs, domains, IPs, hashes, mutexes, and
|
||||
file paths, then deduplicating and typing them. Activates for requests to extract IOCs from
|
||||
analysis output, pull indicators from a report, or harvest atomic indicators.'
|
||||
domain: cybersecurity
|
||||
subdomain: lab-foundations
|
||||
tags:
|
||||
- lab-foundations
|
||||
- ioc
|
||||
- extraction
|
||||
- parsing
|
||||
- automation
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1071
|
||||
- T1583
|
||||
d3fend:
|
||||
- D3-DA
|
||||
references:
|
||||
- 'MITRE ATT&CK — https://attack.mitre.org/'
|
||||
- 'IOC types and indicator extraction patterns — https://github.com/InQuest/python-iocextract'
|
||||
---
|
||||
|
||||
# Extracting IOCs from Analysis Output
|
||||
|
||||
## When to Use
|
||||
|
||||
- You have raw analysis output (strings, sandbox JSON, PCAP notes, logs) and need the atomic
|
||||
indicators out of it.
|
||||
- You want a deduplicated, typed indicator set to feed enrichment, defanging, or detection.
|
||||
- You are building the indicator section of a report from analysis artifacts.
|
||||
|
||||
**Do not use** naive substring matching that produces noise — validate indicator shapes and
|
||||
filter obvious false positives (version strings that look like IPs, library domains).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The analysis artifacts as text/JSON; the defanging skill for safe output.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Gather the artifacts
|
||||
|
||||
Collect strings output, sandbox report fields, network summaries, and relevant log excerpts into
|
||||
text the extractor can scan.
|
||||
|
||||
### Step 2: Extract by pattern
|
||||
|
||||
Pull URLs, domains, IPv4 addresses, email addresses, and hashes (MD5/SHA-1/SHA-256) with
|
||||
validated patterns; also capture host artifacts (mutexes, registry keys, file paths) where the
|
||||
format allows.
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py extract analysis.txt
|
||||
```
|
||||
|
||||
### Step 3: Filter and deduplicate
|
||||
|
||||
Drop benign noise (Microsoft/CDN domains, localhost, RFC1918 where irrelevant) and deduplicate;
|
||||
keep a record of what was filtered and why.
|
||||
|
||||
### Step 4: Type and hand off
|
||||
|
||||
Tag each indicator with its type and pass the set to enrichment/defanging for reporting.
|
||||
|
||||
## Validation
|
||||
|
||||
- Extracted indicators match valid shapes (no malformed IPs/hashes).
|
||||
- The set is deduplicated and obvious benign noise is filtered with a rationale.
|
||||
- Each indicator is typed and ready for enrichment/defanging.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Capturing version numbers as IPs or library hostnames as C2.
|
||||
- Missing indicators split across lines or encoded (base64) in the artifacts.
|
||||
- Not recording what was filtered, losing analyst auditability.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the IOC extractor.
|
||||
- MITRE ATT&CK and indicator-extraction patterns (linked in frontmatter).
|
||||
@@ -0,0 +1,34 @@
|
||||
# API Reference: Extracting IOCs from Analysis Output
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` scans analysis text for URLs, domains, IPv4 addresses, emails, and hashes
|
||||
using validated patterns, filters benign noise (RFC1918 IPs, well-known service domains), and
|
||||
deduplicates. It reads a local text file only and performs no network activity.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | regex, JSON, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `extract(text)`
|
||||
Returns typed, deduplicated indicators and counts, with benign filtering applied.
|
||||
|
||||
### `_is_private(ip)` / `_benign_domain(d)`
|
||||
Noise filters for private address space and common service domains.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py extract analysis.txt
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- MITRE ATT&CK — https://attack.mitre.org/
|
||||
- Indicator-extraction patterns (validated IPv4 octets, hash lengths) —
|
||||
https://github.com/InQuest/python-iocextract
|
||||
- RFC1918 private address ranges for noise filtering — IETF RFC 1918
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract and type indicators of compromise from raw analysis text.
|
||||
|
||||
Scans text for URLs, domains, IPv4 addresses, emails, and hashes using validated patterns,
|
||||
filters common benign noise (RFC1918, localhost, well-known service domains), and deduplicates.
|
||||
It reads a local text file only and performs no network activity.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
RE_URL = re.compile(r"\bhttps?://[^\s\"'<>)\]]+", re.I)
|
||||
RE_IPV4 = re.compile(r"\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b")
|
||||
RE_EMAIL = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b")
|
||||
RE_DOMAIN = re.compile(r"\b(?:[a-z0-9-]+\.)+[a-z]{2,}\b", re.I)
|
||||
RE_MD5 = re.compile(r"\b[a-f0-9]{32}\b", re.I)
|
||||
RE_SHA1 = re.compile(r"\b[a-f0-9]{40}\b", re.I)
|
||||
RE_SHA256 = re.compile(r"\b[a-f0-9]{64}\b", re.I)
|
||||
|
||||
BENIGN_DOMAINS = {"microsoft.com", "windows.com", "msftncsi.com", "schemas.xmlsoap.org",
|
||||
"w3.org", "verisign.com", "digicert.com", "localhost"}
|
||||
|
||||
|
||||
def _is_private(ip: str) -> bool:
|
||||
parts = [int(x) for x in ip.split(".")]
|
||||
return (parts[0] == 10 or (parts[0] == 172 and 16 <= parts[1] <= 31)
|
||||
or (parts[0] == 192 and parts[1] == 168) or parts[0] == 127)
|
||||
|
||||
|
||||
def _benign_domain(d: str) -> bool:
|
||||
return any(d.lower() == b or d.lower().endswith("." + b) for b in BENIGN_DOMAINS)
|
||||
|
||||
|
||||
def extract(text: str) -> dict:
|
||||
filtered = []
|
||||
urls = sorted(set(RE_URL.findall(text)))
|
||||
ips = sorted({ip for ip in RE_IPV4.findall(text) if not _is_private(ip)})
|
||||
emails = sorted(set(RE_EMAIL.findall(text)))
|
||||
domains = sorted({d for d in RE_DOMAIN.findall(text)
|
||||
if not RE_IPV4.fullmatch(d) and not _benign_domain(d)})
|
||||
hashes = sorted(set(RE_SHA256.findall(text)) | set(RE_SHA1.findall(text))
|
||||
| set(RE_MD5.findall(text)))
|
||||
return {
|
||||
"indicators": {"urls": urls, "domains": domains, "ipv4": ips,
|
||||
"emails": emails, "hashes": hashes},
|
||||
"counts": {"urls": len(urls), "domains": len(domains), "ipv4": len(ips),
|
||||
"emails": len(emails), "hashes": len(hashes)},
|
||||
"filtered_note": "private IPs and well-known service domains removed",
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Extract IOCs from analysis output")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("extract")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
text = Path(args.file).read_text(encoding="utf-8-sig", errors="replace")
|
||||
print(json.dumps(extract(text), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user