mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-05 23:38:57 +03:00
Add 5 new cybersecurity skills with full implementations
- implementing-vulnerability-management-with-greenbone: python-gvm GMP API, scan task creation, XML report parsing - detecting-email-account-compromise: Microsoft Graph inbox rules, impossible travel detection, OAuth grant analysis - performing-threat-intelligence-sharing-with-misp: PyMISP event creation, attribute management, sharing validation - analyzing-cobaltstrike-malleable-c2-profiles: dissect.cobaltstrike C2Profile parsing, Suricata rule generation - hunting-for-registry-run-key-persistence: Sysmon Event 13 analysis, T1547.001 detection, Sigma rule generation
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Mahipal
|
||||
|
||||
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,43 @@
|
||||
---
|
||||
name: analyzing-network-packets-with-scapy
|
||||
description: Craft, send, sniff, and dissect network packets using Scapy for protocol analysis, network reconnaissance, and traffic anomaly detection in authorized security testing
|
||||
domain: cybersecurity
|
||||
subdomain: network-security
|
||||
tags:
|
||||
- scapy
|
||||
- packet-analysis
|
||||
- network-forensics
|
||||
- protocol-dissection
|
||||
- pcap
|
||||
- traffic-analysis
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Analyzing Network Packets with Scapy
|
||||
|
||||
## Overview
|
||||
|
||||
Scapy is a Python packet manipulation library that enables crafting, sending, sniffing, and dissecting network packets at granular protocol layers. This skill covers using Scapy for security-relevant tasks including TCP/UDP/ICMP packet crafting, pcap file analysis, protocol field extraction, SYN scan implementation, DNS query analysis, and detecting anomalous traffic patterns such as unusually fragmented packets or malformed headers.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.8+ with `scapy` library installed (`pip install scapy`)
|
||||
- Root/administrator privileges for raw socket operations (sniffing, sending)
|
||||
- Npcap (Windows) or libpcap (Linux) for packet capture
|
||||
- Authorization to perform packet operations on target network
|
||||
|
||||
## Steps
|
||||
|
||||
1. Read and parse pcap/pcapng files with `rdpcap()` for offline analysis
|
||||
2. Extract protocol layers (IP, TCP, UDP, DNS, HTTP) and field values
|
||||
3. Compute traffic statistics: top talkers, protocol distribution, port frequency
|
||||
4. Detect SYN flood patterns by analyzing TCP flag ratios
|
||||
5. Identify DNS exfiltration indicators via query length and entropy analysis
|
||||
6. Craft custom probe packets for authorized network testing
|
||||
7. Export findings as structured JSON report
|
||||
|
||||
## Expected Output
|
||||
|
||||
JSON report containing packet statistics, protocol distribution, top source/destination IPs, detected anomalies (SYN floods, DNS tunneling indicators, fragmentation attacks), and per-flow summaries.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Scapy Network Packet Analysis API Reference
|
||||
|
||||
## Core Scapy Functions
|
||||
|
||||
### Reading Packets
|
||||
```python
|
||||
from scapy.all import rdpcap, sniff, wrpcap
|
||||
|
||||
# Read pcap file
|
||||
packets = rdpcap("capture.pcap")
|
||||
|
||||
# Live sniff with BPF filter (requires root)
|
||||
packets = sniff(filter="tcp port 80", count=100, iface="eth0")
|
||||
|
||||
# Write packets to pcap
|
||||
wrpcap("output.pcap", packets)
|
||||
```
|
||||
|
||||
### Packet Layer Access
|
||||
```python
|
||||
from scapy.all import IP, TCP, UDP, DNS, DNSQR, ICMP
|
||||
|
||||
pkt = packets[0]
|
||||
pkt.haslayer(IP) # Check if layer exists
|
||||
pkt[IP].src # Source IP
|
||||
pkt[IP].dst # Destination IP
|
||||
pkt[TCP].sport # Source port
|
||||
pkt[TCP].dport # Destination port
|
||||
pkt[TCP].flags # TCP flags: S, SA, A, FA, R, PA
|
||||
pkt[DNS].qd.qname # DNS query name
|
||||
pkt[ICMP].type # ICMP type (8=echo request, 0=echo reply)
|
||||
```
|
||||
|
||||
### Packet Crafting
|
||||
```python
|
||||
from scapy.all import IP, TCP, sr1, send
|
||||
|
||||
# SYN probe (authorized testing only)
|
||||
syn = IP(dst="192.168.1.1") / TCP(dport=80, flags="S")
|
||||
response = sr1(syn, timeout=2, verbose=0)
|
||||
|
||||
# ICMP ping
|
||||
ping = IP(dst="192.168.1.1") / ICMP()
|
||||
send(ping, verbose=0)
|
||||
|
||||
# Custom DNS query
|
||||
dns = IP(dst="8.8.8.8") / UDP(dport=53) / DNS(rd=1, qd=DNSQR(qname="example.com"))
|
||||
```
|
||||
|
||||
## Protocol Fields Reference
|
||||
|
||||
### TCP Flags
|
||||
| Flag | Value | Meaning |
|
||||
|------|-------|---------|
|
||||
| S | 0x02 | SYN |
|
||||
| SA | 0x12 | SYN-ACK |
|
||||
| A | 0x10 | ACK |
|
||||
| F | 0x01 | FIN |
|
||||
| R | 0x04 | RST |
|
||||
| P | 0x08 | PSH |
|
||||
|
||||
### ICMP Types
|
||||
| Type | Meaning |
|
||||
|------|---------|
|
||||
| 0 | Echo Reply |
|
||||
| 3 | Destination Unreachable |
|
||||
| 8 | Echo Request |
|
||||
| 11 | Time Exceeded |
|
||||
|
||||
## BPF Filter Syntax
|
||||
```
|
||||
tcp port 443 # TCP traffic on port 443
|
||||
host 10.0.0.1 # All traffic to/from IP
|
||||
src net 192.168.0.0/24 # Source from subnet
|
||||
udp and port 53 # DNS traffic
|
||||
tcp[tcpflags] & tcp-syn != 0 # SYN packets only
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
# Analyze pcap file for anomalies
|
||||
python agent.py --pcap capture.pcap --output report.json
|
||||
|
||||
# Custom thresholds
|
||||
python agent.py --pcap traffic.pcapng --syn-threshold 50 --dns-length 30
|
||||
|
||||
# Port scan detection sensitivity
|
||||
python agent.py --pcap scan.pcap --scan-threshold 10
|
||||
```
|
||||
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Network packet analysis agent using Scapy for pcap parsing and anomaly detection."""
|
||||
|
||||
import json
|
||||
import math
|
||||
import argparse
|
||||
from collections import defaultdict, Counter
|
||||
from datetime import datetime
|
||||
|
||||
from scapy.all import rdpcap, IP, TCP, UDP, DNS, DNSQR, ICMP, Raw
|
||||
|
||||
|
||||
def load_pcap(filepath):
|
||||
"""Load packets from a pcap/pcapng file."""
|
||||
packets = rdpcap(filepath)
|
||||
print(f"[+] Loaded {len(packets)} packets from {filepath}")
|
||||
return packets
|
||||
|
||||
|
||||
def extract_packet_info(packets):
|
||||
"""Extract structured info from each packet with IP layer."""
|
||||
records = []
|
||||
for pkt in packets:
|
||||
if not pkt.haslayer(IP):
|
||||
continue
|
||||
info = {
|
||||
"src_ip": pkt[IP].src,
|
||||
"dst_ip": pkt[IP].dst,
|
||||
"proto": pkt[IP].proto,
|
||||
"ttl": pkt[IP].ttl,
|
||||
"length": len(pkt),
|
||||
"flags": str(pkt[IP].flags),
|
||||
"timestamp": float(pkt.time),
|
||||
}
|
||||
if pkt.haslayer(TCP):
|
||||
info["src_port"] = pkt[TCP].sport
|
||||
info["dst_port"] = pkt[TCP].dport
|
||||
info["tcp_flags"] = str(pkt[TCP].flags)
|
||||
info["protocol"] = "TCP"
|
||||
elif pkt.haslayer(UDP):
|
||||
info["src_port"] = pkt[UDP].sport
|
||||
info["dst_port"] = pkt[UDP].dport
|
||||
info["protocol"] = "UDP"
|
||||
elif pkt.haslayer(ICMP):
|
||||
info["icmp_type"] = pkt[ICMP].type
|
||||
info["icmp_code"] = pkt[ICMP].code
|
||||
info["protocol"] = "ICMP"
|
||||
else:
|
||||
info["protocol"] = str(pkt[IP].proto)
|
||||
if pkt.haslayer(DNS) and pkt.haslayer(DNSQR):
|
||||
info["dns_query"] = pkt[DNSQR].qname.decode("utf-8", errors="ignore").rstrip(".")
|
||||
info["dns_type"] = pkt[DNSQR].qtype
|
||||
records.append(info)
|
||||
return records
|
||||
|
||||
|
||||
def compute_traffic_stats(records):
|
||||
"""Compute overall traffic statistics."""
|
||||
src_ips = Counter(r["src_ip"] for r in records)
|
||||
dst_ips = Counter(r["dst_ip"] for r in records)
|
||||
protocols = Counter(r["protocol"] for r in records)
|
||||
dst_ports = Counter(r.get("dst_port", 0) for r in records if r.get("dst_port"))
|
||||
total_bytes = sum(r["length"] for r in records)
|
||||
return {
|
||||
"total_packets": len(records),
|
||||
"total_bytes": total_bytes,
|
||||
"unique_src_ips": len(src_ips),
|
||||
"unique_dst_ips": len(dst_ips),
|
||||
"top_src_ips": src_ips.most_common(10),
|
||||
"top_dst_ips": dst_ips.most_common(10),
|
||||
"protocol_distribution": dict(protocols),
|
||||
"top_dst_ports": dst_ports.most_common(10),
|
||||
}
|
||||
|
||||
|
||||
def detect_syn_flood(records, threshold=100):
|
||||
"""Detect SYN flood by counting SYN-only packets per destination IP."""
|
||||
syn_counts = defaultdict(int)
|
||||
synack_counts = defaultdict(int)
|
||||
for r in records:
|
||||
if r.get("tcp_flags") == "S":
|
||||
syn_counts[r["dst_ip"]] += 1
|
||||
elif r.get("tcp_flags") == "SA":
|
||||
synack_counts[r["dst_ip"]] += 1
|
||||
alerts = []
|
||||
for ip, count in syn_counts.items():
|
||||
ack_count = synack_counts.get(ip, 0)
|
||||
ratio = ack_count / count if count > 0 else 1.0
|
||||
if count >= threshold and ratio < 0.3:
|
||||
alerts.append({
|
||||
"detection": "SYN Flood",
|
||||
"target_ip": ip,
|
||||
"syn_count": count,
|
||||
"synack_count": ack_count,
|
||||
"synack_ratio": round(ratio, 4),
|
||||
"severity": "critical",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def calculate_entropy(data):
|
||||
"""Calculate Shannon entropy of a string."""
|
||||
if not data:
|
||||
return 0.0
|
||||
freq = Counter(data)
|
||||
length = len(data)
|
||||
return -sum((c / length) * math.log2(c / length) for c in freq.values())
|
||||
|
||||
|
||||
def detect_dns_tunneling(records, length_threshold=50, entropy_threshold=3.5):
|
||||
"""Detect DNS tunneling via long/high-entropy query names."""
|
||||
alerts = []
|
||||
for r in records:
|
||||
query = r.get("dns_query", "")
|
||||
if not query:
|
||||
continue
|
||||
subdomain = query.split(".")[0] if "." in query else query
|
||||
if len(subdomain) >= length_threshold or calculate_entropy(subdomain) >= entropy_threshold:
|
||||
alerts.append({
|
||||
"detection": "DNS Tunneling Indicator",
|
||||
"query": query,
|
||||
"subdomain_length": len(subdomain),
|
||||
"entropy": round(calculate_entropy(subdomain), 4),
|
||||
"src_ip": r["src_ip"],
|
||||
"severity": "high",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_port_scan(records, threshold=20):
|
||||
"""Detect port scanning by counting unique destination ports per source IP."""
|
||||
src_ports = defaultdict(set)
|
||||
for r in records:
|
||||
if r.get("tcp_flags") == "S" and r.get("dst_port"):
|
||||
src_ports[r["src_ip"]].add(r["dst_port"])
|
||||
alerts = []
|
||||
for ip, ports in src_ports.items():
|
||||
if len(ports) >= threshold:
|
||||
alerts.append({
|
||||
"detection": "Port Scan",
|
||||
"source_ip": ip,
|
||||
"unique_ports_probed": len(ports),
|
||||
"sample_ports": sorted(list(ports))[:20],
|
||||
"severity": "high",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Network Packet Analysis Agent (Scapy)")
|
||||
parser.add_argument("--pcap", required=True, help="Path to pcap/pcapng file")
|
||||
parser.add_argument("--syn-threshold", type=int, default=100, help="SYN flood detection threshold")
|
||||
parser.add_argument("--dns-length", type=int, default=50, help="DNS tunneling subdomain length threshold")
|
||||
parser.add_argument("--scan-threshold", type=int, default=20, help="Port scan unique ports threshold")
|
||||
parser.add_argument("--output", default="packet_analysis_report.json", help="Output report path")
|
||||
args = parser.parse_args()
|
||||
|
||||
packets = load_pcap(args.pcap)
|
||||
records = extract_packet_info(packets)
|
||||
print(f"[+] Extracted {len(records)} IP-layer records")
|
||||
|
||||
stats = compute_traffic_stats(records)
|
||||
syn_alerts = detect_syn_flood(records, args.syn_threshold)
|
||||
dns_alerts = detect_dns_tunneling(records, args.dns_length)
|
||||
scan_alerts = detect_port_scan(records, args.scan_threshold)
|
||||
|
||||
report = {
|
||||
"analysis_time": datetime.utcnow().isoformat() + "Z",
|
||||
"pcap_file": args.pcap,
|
||||
"traffic_stats": stats,
|
||||
"anomalies": {
|
||||
"syn_flood": syn_alerts,
|
||||
"dns_tunneling": dns_alerts,
|
||||
"port_scan": scan_alerts,
|
||||
},
|
||||
"total_anomalies": len(syn_alerts) + len(dns_alerts) + len(scan_alerts),
|
||||
}
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"[+] SYN flood alerts: {len(syn_alerts)}")
|
||||
print(f"[+] DNS tunneling indicators: {len(dns_alerts)}")
|
||||
print(f"[+] Port scan detections: {len(scan_alerts)}")
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user