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,101 @@
|
||||
---
|
||||
name: capturing-and-analyzing-malware-network-traffic
|
||||
description: 'Captures and analyzes malware network traffic from a detonation: extracting C2
|
||||
endpoints, HTTP/DNS patterns, TLS metadata (JA3/SNI), and beaconing from a PCAP to build
|
||||
network IOCs and detection. Activates for requests to analyze malware PCAP, extract C2 from
|
||||
traffic, or study network behavior.'
|
||||
domain: cybersecurity
|
||||
subdomain: malware-analysis
|
||||
tags:
|
||||
- malware
|
||||
- network-analysis
|
||||
- pcap
|
||||
- c2
|
||||
- dns
|
||||
- beaconing
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1071.001
|
||||
- T1071.004
|
||||
- T1571
|
||||
- T1095
|
||||
d3fend:
|
||||
- D3-NTA
|
||||
- D3-NTPA
|
||||
references:
|
||||
- 'Wireshark User Guide — https://www.wireshark.org/docs/wsug_html_chunked/'
|
||||
- 'Zeek documentation — https://docs.zeek.org/'
|
||||
---
|
||||
|
||||
# Capturing and Analyzing Malware Network Traffic
|
||||
|
||||
## When to Use
|
||||
|
||||
- You detonated a sample and captured a PCAP on the services guest, and need to extract its
|
||||
network behavior.
|
||||
- You want C2 endpoints, HTTP/DNS patterns, TLS SNI/JA3, and beaconing intervals.
|
||||
- You are turning traffic observations into network IOCs and detection signatures.
|
||||
|
||||
**Do not use** a single capture as the full C2 picture — staged samples reveal more on longer
|
||||
runs or with the right context. Combine with binary config extraction.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A PCAP captured from the isolated lab (capture on the analysis guest, not the victim).
|
||||
- `tshark`/Wireshark, or Zeek for connection logs, or the bundled PCAP summarizer.
|
||||
- The lab's simulated-internet IP so you can separate sample traffic from noise.
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Capture from the services/analysis guest so the victim never hosts a sniffer.
|
||||
- Defang all extracted domains/IPs/URLs before sharing.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Get a connection overview
|
||||
|
||||
Summarize conversations and protocols to see who talks to whom and how often:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py summarize capture.pcap
|
||||
```
|
||||
|
||||
### Step 2: Analyze DNS
|
||||
|
||||
List queried domains and resolved IPs. Look for DGA-like randomness, repeated NXDOMAIN, and
|
||||
long TXT records (possible tunneling).
|
||||
|
||||
### Step 3: Inspect HTTP/HTTPS
|
||||
|
||||
For HTTP, examine URIs, methods, User-Agent (often unique/odd), and POST bodies. For HTTPS,
|
||||
extract SNI and JA3/JA3S fingerprints since payloads are encrypted.
|
||||
|
||||
### Step 4: Detect beaconing
|
||||
|
||||
Compute inter-arrival times to the same destination. Regular intervals (with jitter) to one
|
||||
host indicate C2 check-ins.
|
||||
|
||||
### Step 5: Build IOCs and detection
|
||||
|
||||
Produce a network IOC set (domains, IPs, URIs, JA3, User-Agent) and hand candidate signatures
|
||||
to the Suricata/Zeek detection workflow.
|
||||
|
||||
## Validation
|
||||
|
||||
- Beaconing intervals are consistent and tied to a specific destination, not random browsing.
|
||||
- Extracted SNI/JA3 reproduce across runs of the same sample.
|
||||
- DNS and HTTP observations corroborate the C2 endpoints found in the binary's config.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Mistaking simulated-internet responses for real C2 behavior; know your lab's IPs.
|
||||
- Ignoring HTTPS because it is encrypted — SNI, JA3, and timing are still strong signals.
|
||||
- Treating one short capture as complete; some C2 only beacons periodically.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the PCAP summarizer and
|
||||
beaconing detector.
|
||||
- Wireshark and Zeek documentation (linked in frontmatter).
|
||||
@@ -0,0 +1,43 @@
|
||||
# API Reference: Capturing and Analyzing Malware Network Traffic
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` summarizes a PCAP into top conversations, DNS queries, TLS SNI, and detects
|
||||
beaconing by inter-arrival timing. It prefers `pyshark` (tshark) and falls back to a minimal
|
||||
pure-Python classic-pcap parser for IPv4 conversations and beaconing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| pyshark | >= 0.6 | Rich packet parsing via tshark (optional) |
|
||||
| (stdlib) | — | Minimal pcap parser, statistics |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| tshark / Wireshark | Interactive and scripted PCAP analysis |
|
||||
| Zeek | Connection, DNS, HTTP, and SSL logs from PCAP |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `summarize(path)`
|
||||
Dispatches to pyshark or the minimal parser; returns conversations, DNS/SNI (pyshark), and
|
||||
beaconing.
|
||||
|
||||
### `_beaconing(timestamps_by_dst)`
|
||||
Flags destinations with low-jitter regular intervals (likely C2 check-ins).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
pip install pyshark # optional; requires tshark on PATH
|
||||
python scripts/analyst.py summarize capture.pcap
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- Wireshark User Guide — https://www.wireshark.org/docs/wsug_html_chunked/
|
||||
- Zeek documentation — https://docs.zeek.org/
|
||||
- libpcap classic file format (header/record layout)
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Summarize a PCAP and detect beaconing.
|
||||
|
||||
Prefers ``pyshark`` (a tshark wrapper) for rich parsing; falls back to a minimal pure-Python
|
||||
parser that reads the classic pcap format and tallies IPv4 conversations. Analyzes captured
|
||||
traffic only — it never generates traffic.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import statistics
|
||||
import struct
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def _beaconing(timestamps_by_dst: dict[str, list[float]]) -> list[dict]:
|
||||
results = []
|
||||
for dst, times in timestamps_by_dst.items():
|
||||
if len(times) < 4:
|
||||
continue
|
||||
times = sorted(times)
|
||||
deltas = [t2 - t1 for t1, t2 in zip(times, times[1:])]
|
||||
mean = statistics.mean(deltas)
|
||||
if mean <= 0:
|
||||
continue
|
||||
stdev = statistics.pstdev(deltas)
|
||||
jitter = stdev / mean
|
||||
if jitter < 0.25: # regular interval = likely beacon
|
||||
results.append({"dst": dst, "count": len(times),
|
||||
"interval_s": round(mean, 2), "jitter": round(jitter, 3)})
|
||||
return sorted(results, key=lambda r: r["jitter"])
|
||||
|
||||
|
||||
def summarize_with_pyshark(path: str) -> dict:
|
||||
import pyshark # type: ignore
|
||||
cap = pyshark.FileCapture(path, keep_packets=False)
|
||||
convos: dict[tuple, int] = defaultdict(int)
|
||||
times_by_dst: dict[str, list[float]] = defaultdict(list)
|
||||
dns: set[str] = set()
|
||||
sni: set[str] = set()
|
||||
for pkt in cap:
|
||||
try:
|
||||
if hasattr(pkt, "ip"):
|
||||
src, dst = pkt.ip.src, pkt.ip.dst
|
||||
convos[(src, dst)] += 1
|
||||
times_by_dst[dst].append(float(pkt.sniff_timestamp))
|
||||
if hasattr(pkt, "dns") and hasattr(pkt.dns, "qry_name"):
|
||||
dns.add(pkt.dns.qry_name)
|
||||
if hasattr(pkt, "tls") and hasattr(pkt.tls, "handshake_extensions_server_name"):
|
||||
sni.add(pkt.tls.handshake_extensions_server_name)
|
||||
except AttributeError:
|
||||
continue
|
||||
cap.close()
|
||||
return {
|
||||
"engine": "pyshark",
|
||||
"top_conversations": sorted(
|
||||
[{"src": s, "dst": d, "packets": n} for (s, d), n in convos.items()],
|
||||
key=lambda c: c["packets"], reverse=True)[:20],
|
||||
"dns_queries": sorted(dns),
|
||||
"tls_sni": sorted(sni),
|
||||
"beaconing": _beaconing(times_by_dst),
|
||||
}
|
||||
|
||||
|
||||
def summarize_minimal(path: str) -> dict:
|
||||
"""Minimal classic-pcap parser: IPv4 conversations and per-dst timestamps."""
|
||||
with open(path, "rb") as fh:
|
||||
data = fh.read()
|
||||
if len(data) < 24:
|
||||
raise ValueError("file too small to be a pcap")
|
||||
magic = data[:4]
|
||||
if magic in (b"\xd4\xc3\xb2\xa1", b"\xa1\xb2\xc3\xd4"):
|
||||
endian = "<" if magic == b"\xd4\xc3\xb2\xa1" else ">"
|
||||
else:
|
||||
raise ValueError("not a classic pcap (try pyshark for pcapng)")
|
||||
off = 24
|
||||
convos: dict[tuple, int] = defaultdict(int)
|
||||
times_by_dst: dict[str, list[float]] = defaultdict(list)
|
||||
while off + 16 <= len(data):
|
||||
ts_sec, ts_usec, incl, _orig = struct.unpack_from(endian + "IIII", data, off)
|
||||
off += 16
|
||||
pkt = data[off:off + incl]
|
||||
off += incl
|
||||
if len(pkt) < 14 + 20:
|
||||
continue
|
||||
eth_type = struct.unpack_from(">H", pkt, 12)[0]
|
||||
if eth_type != 0x0800: # IPv4 only
|
||||
continue
|
||||
ip = pkt[14:]
|
||||
src = ".".join(str(b) for b in ip[12:16])
|
||||
dst = ".".join(str(b) for b in ip[16:20])
|
||||
convos[(src, dst)] += 1
|
||||
times_by_dst[dst].append(ts_sec + ts_usec / 1e6)
|
||||
return {
|
||||
"engine": "minimal",
|
||||
"top_conversations": sorted(
|
||||
[{"src": s, "dst": d, "packets": n} for (s, d), n in convos.items()],
|
||||
key=lambda c: c["packets"], reverse=True)[:20],
|
||||
"beaconing": _beaconing(times_by_dst),
|
||||
"note": "install pyshark for DNS/TLS/HTTP detail and pcapng support",
|
||||
}
|
||||
|
||||
|
||||
def summarize(path: str) -> dict:
|
||||
try:
|
||||
return summarize_with_pyshark(path)
|
||||
except ImportError:
|
||||
return summarize_minimal(path)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Summarize malware PCAP")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("summarize")
|
||||
p.add_argument("pcap")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(summarize(args.pcap), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user