mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-23 21:21:00 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: implementing-network-traffic-analysis-with-arkime
|
||||
description: >-
|
||||
Deploy and query Arkime (formerly Moloch) for full packet capture network
|
||||
traffic analysis. Uses the Arkime API v3 to search sessions, download PCAPs,
|
||||
analyze connection patterns, detect beaconing behavior, and identify suspicious
|
||||
network flows. Monitors DNS queries, HTTP traffic, and TLS certificate anomalies
|
||||
across captured traffic.
|
||||
---
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Install dependencies: `pip install requests`
|
||||
2. Configure Arkime viewer URL and credentials.
|
||||
3. Run the agent to query Arkime sessions and analyze traffic:
|
||||
- Search sessions by IP, port, protocol, or expression
|
||||
- Download PCAP data for forensic analysis
|
||||
- Detect C2 beaconing via connection interval analysis
|
||||
- Identify DNS tunneling through query length statistics
|
||||
- Flag connections to known-bad TLS certificate issuers
|
||||
|
||||
```bash
|
||||
python scripts/agent.py --arkime-url https://arkime.local:8005 --user admin --password secret --output arkime_report.json
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Beaconing Detection
|
||||
```
|
||||
Source: 10.1.2.50 -> 185.220.101.34:443
|
||||
Sessions: 288 over 24 hours
|
||||
Avg interval: 300s, Jitter: 4.2%
|
||||
Verdict: HIGH confidence C2 beaconing (jitter < 5%)
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
# API Reference: Arkime Network Traffic Analysis
|
||||
|
||||
## Authentication
|
||||
```
|
||||
HTTPDigestAuth(username, password)
|
||||
```
|
||||
All API requests require Digest authentication.
|
||||
|
||||
## Session Search
|
||||
```
|
||||
GET /api/sessions
|
||||
```
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `date` | int | Time range in hours (1=last hour) |
|
||||
| `expression` | string | Arkime search expression |
|
||||
| `length` | int | Max results to return |
|
||||
| `order` | string | Sort field:direction (e.g. `lastPacket:desc`) |
|
||||
| `fields` | string | Comma-separated field list |
|
||||
|
||||
## PCAP Download
|
||||
```
|
||||
GET /api/sessions/pcap
|
||||
GET /api/sessions/pcapng
|
||||
```
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `date` | Time range in hours |
|
||||
| `expression` | Filter expression |
|
||||
Returns raw PCAP/PCAPNG binary data.
|
||||
|
||||
## Connection Graph
|
||||
```
|
||||
GET /api/connections
|
||||
```
|
||||
Returns `nodes` (IPs) and `links` (connections) for network graph visualization.
|
||||
|
||||
## SPI View (Field Statistics)
|
||||
```
|
||||
GET /api/spiview
|
||||
```
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `spi` | Comma-separated fields (e.g. `srcIp,dstIp,dstPort`) |
|
||||
Returns top values and counts for each field.
|
||||
|
||||
## Session Fields
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `srcIp` | Source IP address |
|
||||
| `dstIp` | Destination IP address |
|
||||
| `srcPort` | Source port |
|
||||
| `dstPort` | Destination port |
|
||||
| `srcBytes` | Bytes sent by source |
|
||||
| `dstBytes` | Bytes sent by destination |
|
||||
| `lastPacket` | Timestamp of last packet (ms) |
|
||||
| `srcJa3` | JA3 fingerprint of client TLS |
|
||||
| `tls.issuerCN` | TLS certificate issuer CN |
|
||||
| `tls.subjectCN` | TLS certificate subject CN |
|
||||
| `tls.notAfter` | Certificate expiry (ms epoch) |
|
||||
|
||||
## Search Expressions
|
||||
```
|
||||
ip.src == 10.0.0.0/8
|
||||
port.dst == 443
|
||||
protocols == tls
|
||||
country.src == CN
|
||||
bytes > 1000000
|
||||
```
|
||||
|
||||
## Beaconing Detection Logic
|
||||
- Collect connection timestamps per (src, dst, port) tuple
|
||||
- Calculate intervals between consecutive connections
|
||||
- Compute jitter ratio: `std_dev / avg_interval`
|
||||
- Jitter < 0.05 = high confidence C2, < 0.15 = medium
|
||||
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Arkime Network Traffic Analysis Agent - Queries Arkime API for session analysis and anomaly detection."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
import requests
|
||||
from requests.auth import HTTPDigestAuth
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def arkime_request(base_url, endpoint, auth, params=None):
|
||||
"""Make an authenticated request to Arkime API v3."""
|
||||
url = f"{base_url}{endpoint}"
|
||||
try:
|
||||
resp = requests.get(url, auth=HTTPDigestAuth(*auth), params=params, verify=False, timeout=30)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
except requests.RequestException as e:
|
||||
logger.error("Arkime API error %s: %s", endpoint, e)
|
||||
return None
|
||||
|
||||
|
||||
def search_sessions(base_url, auth, expression, date_range=1, length=500):
|
||||
"""Search Arkime sessions with an expression filter."""
|
||||
params = {
|
||||
"date": date_range,
|
||||
"expression": expression,
|
||||
"length": length,
|
||||
"order": "lastPacket:desc",
|
||||
}
|
||||
data = arkime_request(base_url, "/api/sessions", auth, params)
|
||||
if data and "data" in data:
|
||||
logger.info("Found %d sessions for expression: %s", len(data["data"]), expression)
|
||||
return data["data"]
|
||||
return []
|
||||
|
||||
|
||||
def get_connections(base_url, auth, expression, date_range=1):
|
||||
"""Get connection graph data from Arkime."""
|
||||
params = {"date": date_range, "expression": expression}
|
||||
data = arkime_request(base_url, "/api/connections", auth, params)
|
||||
if data:
|
||||
nodes = data.get("nodes", [])
|
||||
links = data.get("links", [])
|
||||
logger.info("Connection graph: %d nodes, %d links", len(nodes), len(links))
|
||||
return {"nodes": nodes, "links": links}
|
||||
return {"nodes": [], "links": []}
|
||||
|
||||
|
||||
def get_spi_view(base_url, auth, expression, date_range=1):
|
||||
"""Get SPI view field statistics from Arkime."""
|
||||
params = {"date": date_range, "expression": expression, "spi": "srcIp,dstIp,dstPort"}
|
||||
data = arkime_request(base_url, "/api/spiview", auth, params)
|
||||
return data if data else {}
|
||||
|
||||
|
||||
def detect_beaconing(sessions, interval_threshold=0.15):
|
||||
"""Detect C2 beaconing by analyzing connection intervals."""
|
||||
connections = defaultdict(list)
|
||||
for s in sessions:
|
||||
key = (s.get("srcIp", ""), s.get("dstIp", ""), s.get("dstPort", 0))
|
||||
connections[key].append(s.get("lastPacket", 0))
|
||||
|
||||
beacons = []
|
||||
for (src, dst, port), timestamps in connections.items():
|
||||
if len(timestamps) < 10:
|
||||
continue
|
||||
timestamps.sort()
|
||||
intervals = [timestamps[i + 1] - timestamps[i] for i in range(len(timestamps) - 1)]
|
||||
if not intervals:
|
||||
continue
|
||||
avg_interval = sum(intervals) / len(intervals)
|
||||
if avg_interval == 0:
|
||||
continue
|
||||
std_dev = (sum((i - avg_interval) ** 2 for i in intervals) / len(intervals)) ** 0.5
|
||||
jitter_ratio = std_dev / avg_interval
|
||||
|
||||
if jitter_ratio < interval_threshold:
|
||||
beacons.append({
|
||||
"src_ip": src,
|
||||
"dst_ip": dst,
|
||||
"dst_port": port,
|
||||
"session_count": len(timestamps),
|
||||
"avg_interval_sec": round(avg_interval / 1000, 1),
|
||||
"jitter_ratio": round(jitter_ratio, 4),
|
||||
"confidence": "high" if jitter_ratio < 0.05 else "medium",
|
||||
"severity": "critical",
|
||||
})
|
||||
logger.warning("Beaconing: %s -> %s:%d (jitter: %.4f)", src, dst, port, jitter_ratio)
|
||||
return beacons
|
||||
|
||||
|
||||
def detect_dns_tunneling(sessions, query_len_threshold=50):
|
||||
"""Detect DNS tunneling via abnormally long DNS queries."""
|
||||
dns_sessions = [s for s in sessions if s.get("dstPort") == 53]
|
||||
suspicious = []
|
||||
src_stats = defaultdict(lambda: {"count": 0, "total_bytes": 0})
|
||||
|
||||
for s in dns_sessions:
|
||||
src = s.get("srcIp", "")
|
||||
src_stats[src]["count"] += 1
|
||||
src_stats[src]["total_bytes"] += s.get("srcBytes", 0) + s.get("dstBytes", 0)
|
||||
|
||||
for src, stats in src_stats.items():
|
||||
avg_bytes = stats["total_bytes"] / max(stats["count"], 1)
|
||||
if stats["count"] > 100 and avg_bytes > query_len_threshold:
|
||||
suspicious.append({
|
||||
"src_ip": src,
|
||||
"dns_query_count": stats["count"],
|
||||
"avg_bytes_per_query": round(avg_bytes, 1),
|
||||
"total_bytes": stats["total_bytes"],
|
||||
"severity": "high",
|
||||
"indicator": "DNS tunneling - high volume with large payloads",
|
||||
})
|
||||
return suspicious
|
||||
|
||||
|
||||
def detect_large_transfers(sessions, threshold_mb=100):
|
||||
"""Detect unusually large data transfers."""
|
||||
threshold_bytes = threshold_mb * 1024 * 1024
|
||||
large = []
|
||||
for s in sessions:
|
||||
total = s.get("srcBytes", 0) + s.get("dstBytes", 0)
|
||||
if total > threshold_bytes:
|
||||
large.append({
|
||||
"src_ip": s.get("srcIp", ""),
|
||||
"dst_ip": s.get("dstIp", ""),
|
||||
"dst_port": s.get("dstPort", 0),
|
||||
"total_bytes": total,
|
||||
"total_mb": round(total / (1024 * 1024), 2),
|
||||
"severity": "high",
|
||||
})
|
||||
return large
|
||||
|
||||
|
||||
def detect_tls_anomalies(sessions):
|
||||
"""Detect TLS certificate anomalies (self-signed, expired, unusual issuers)."""
|
||||
anomalies = []
|
||||
for s in sessions:
|
||||
tls = s.get("tls", {})
|
||||
if not tls:
|
||||
continue
|
||||
ja3 = s.get("srcJa3", "")
|
||||
issuer_cn = tls.get("issuerCN", "")
|
||||
not_after = tls.get("notAfter", 0)
|
||||
if issuer_cn and issuer_cn == tls.get("subjectCN", ""):
|
||||
anomalies.append({
|
||||
"src_ip": s.get("srcIp", ""),
|
||||
"dst_ip": s.get("dstIp", ""),
|
||||
"issue": "self-signed certificate",
|
||||
"issuer": issuer_cn,
|
||||
"severity": "medium",
|
||||
})
|
||||
if not_after and not_after < int(datetime.utcnow().timestamp() * 1000):
|
||||
anomalies.append({
|
||||
"src_ip": s.get("srcIp", ""),
|
||||
"dst_ip": s.get("dstIp", ""),
|
||||
"issue": "expired certificate",
|
||||
"issuer": issuer_cn,
|
||||
"severity": "medium",
|
||||
})
|
||||
return anomalies
|
||||
|
||||
|
||||
def generate_report(beacons, dns_tunneling, large_transfers, tls_anomalies, session_count):
|
||||
"""Generate network traffic analysis report."""
|
||||
all_findings = beacons + dns_tunneling + large_transfers + tls_anomalies
|
||||
critical = [f for f in all_findings if f.get("severity") == "critical"]
|
||||
report = {
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"sessions_analyzed": session_count,
|
||||
"findings_total": len(all_findings),
|
||||
"critical_count": len(critical),
|
||||
"beaconing_detected": beacons,
|
||||
"dns_tunneling": dns_tunneling,
|
||||
"large_transfers": large_transfers,
|
||||
"tls_anomalies": tls_anomalies,
|
||||
}
|
||||
print(f"ARKIME REPORT: {len(all_findings)} findings ({len(critical)} critical) from {session_count} sessions")
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Arkime Network Traffic Analysis Agent")
|
||||
parser.add_argument("--arkime-url", required=True, help="Arkime viewer URL")
|
||||
parser.add_argument("--user", required=True)
|
||||
parser.add_argument("--password", required=True)
|
||||
parser.add_argument("--expression", default="*", help="Arkime search expression")
|
||||
parser.add_argument("--date-range", type=int, default=1, help="Date range in hours")
|
||||
parser.add_argument("--output", default="arkime_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
auth = (args.user, args.password)
|
||||
sessions = search_sessions(args.arkime_url, auth, args.expression, args.date_range)
|
||||
beacons = detect_beaconing(sessions)
|
||||
dns_tunnel = detect_dns_tunneling(sessions)
|
||||
large = detect_large_transfers(sessions)
|
||||
tls = detect_tls_anomalies(sessions)
|
||||
|
||||
report = generate_report(beacons, dns_tunnel, large, tls, len(sessions))
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Report saved to %s", args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user