---
title: "Investigation réseau — SOC"
domain: security
subdomain: soc
phase: 02-investigation
type: snippet
tags: [soc, network, C2, beaconing, DNS, investigation, Zeek, Suricata]
difficulty: advanced
status: stable
updated: "2025-05-13"
---
## Détection de Beaconing

### Indicateurs caractéristiques

| Indicateur | Valeur suspecte | Explication |
|---|---|---|
| **Régularité** | Intervalle < 5% de variation | Timers programmatiques vs humains |
| **Jitter** | Jitter artificiel faible et constant | Jitter C2 = ±10-20% du beacon interval |
| **Taille payload** | Très petite et constante (< 200 bytes) | Heartbeat pur, pas de vrai trafic |
| **Durée session** | Sessions très courtes et régulières | Check-in / instruction poll |
| **Destination** | Toujours même IP/domaine externe | Pas de CDN, pas de résolution variable |
| **Horaires** | Continue 24/7 sans pause week-end | Automatisé, pas humain |

### Splunk — Détection de beaconing

```splunk
# Beaconing via transaction — connexions régulières vers même dest
index=network earliest=-24h dest_ip="{{EXTERNAL_IP}}"
| transaction src_ip dest_ip maxpause=10m
| eval interval=duration/eventcount
| where eventcount > 20 AND interval < 300
| stats stdev(interval) as jitter, avg(interval) as avg_interval, count
  by src_ip, dest_ip, dest_port
| where jitter < 10
| sort jitter
```

```splunk
# Beaconing timechart — régularité des connexions
index=network src_ip="{{SUSPICIOUS_IP}}" dest_ip="{{EXTERNAL_IP}}" earliest=-6h
| timechart span=5m count as connections
| stats stdev(connections) as regularity_score

# Faible stdev → connexions très régulières → probable beacon
```

```splunk
# Top destinations suspectes par régularité
index=network earliest=-24h action=allowed
  NOT (dest_ip IN ("{{CDN_RANGES}}"))
  dest_ip!="10.*" dest_ip!="172.16.*" dest_ip!="192.168.*"
| bucket _time span=1h
| stats count by src_ip, dest_ip, dest_port, _time
| stats stdev(count) as regularity, avg(count) as avg_hourly, dc(_time) as hours_active
  by src_ip, dest_ip, dest_port
| where hours_active > 12 AND regularity < 2
| sort regularity
```

---

## DNS Tunneling

### Indicateurs

| Indicateur | Valeur suspecte | Détection |
|---|---|---|
| **Entropie sous-domaine** | > 3.5 bits/char | Calcul Shannon entropy |
| **Longueur label** | > 40 caractères | Labels DNS max légitimes ≈ 20 |
| **Types de requête** | TXT, NULL, CNAME inhabituels | Protocoles de tunneling |
| **Volume** | > 100 req/min vers même domaine parent | Exfiltration de données |
| **Unicité** | Sous-domaines tous différents | Encodage de données en subdomain |
| **Domaine parent** | Inconnu, enregistré récemment | Nouveau domaine = IoC |

### Splunk — Analyse DNS

```splunk
# DNS — Volume élevé vers même domaine parent (tunneling)
index=dns earliest=-2h query_type IN ("A", "TXT", "NULL", "CNAME")
| rex field=query "(?:[^.]+\.)+(?P<parent_domain>[^.]+\.[^.]+)$"
| stats
    count as total_queries,
    dc(query) as unique_subdomains,
    avg(eval(len(query))) as avg_query_len
  by src_ip, parent_domain
| where unique_subdomains > 50 OR avg_query_len > 50
| sort -unique_subdomains
```

```splunk
# DNS — Entropie des sous-domaines (ioc_entropy lookup ou calcul)
index=dns earliest=-1h
| rex field=query "^(?P<subdomain>[^.]+)\."
| eval entropy=0
| eval chars=split(subdomain, "")
| eval entropy=mvsum(eval(-log(1/mvcount(chars))/log(2)))
# Approximation — utiliser un lookup pré-calculé en prod
| where len(subdomain) > 30
| table _time, src_ip, query, subdomain, len(subdomain)
```

```splunk
# DNS — Requêtes TXT suspectes (souvent utilisées pour C2)
index=dns query_type=TXT earliest=-24h
  NOT (query IN ("*.spf*", "*.dkim*", "*.dmarc*"))
| stats count by src_ip, query, query_type
| sort -count
```

```kql
# Elastic — DNS tunneling indicators
dns.question.type: ("TXT" or "NULL") 
  and not dns.question.name: ("_dmarc.*" or "_domainkey.*")

# Long subdomains
dns.question.name.length > 50
```

---

## Frameworks C2 — Indicateurs

### Cobalt Strike

```
# Patterns réseau Cobalt Strike
- Malleable C2 profiles — modifier les headers HTTP pour se fondre dans le trafic
- Default beacon : POST /submit.php toutes les 60s
- Headers suspects : X-Malware-ID, X-Session-ID dans requêtes POST
- Certificats TLS auto-signés avec CN= "Major Cobalt Strike User"
- JA3 fingerprint connu : 72a7c13d19aac27b729330f7c9dc940f (CS default)
- JARM fingerprint : 07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1

# Splunk — Cobalt Strike beacon default
index=proxy url="*/submit.php" method=POST content_length < 200 earliest=-6h
| stats count by src_ip, dest_domain, content_length
| where count > 10
```

### Metasploit Meterpreter

```
# Patterns Meterpreter
- Handshake TLS suivi de trafic HTTP/HTTPS vers IP publique non CDN
- User-Agent vide ou générique sur reverse HTTPS
- Stage download : GET /AAAA (payload 4 bytes) puis réponse > 100KB
- Port par défaut : 4444 (reverse_tcp), 4433 (reverse_https)
```

### Détection générique C2

```splunk
# HTTP/HTTPS vers IPs directes (pas de domaine) — indicateur fort C2
index=proxy earliest=-24h
  NOT (dest_domain IN ("{{KNOWN_IP_SERVICES}}"))
  dest_ip!="10.*" dest_ip!="192.168.*" dest_ip!="172.16.*"
| rex field=url "^https?://(?P<raw_ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"
| where isnotnull(raw_ip)
| stats count by src_ip, raw_ip, dest_port, method
| sort -count
```

---

## Zeek & Suricata — Analyse de logs

### Zeek — conn.log

```bash
# Connexions longues suspectes (possible C2 idle connection)
zeek-cut id.orig_h id.resp_h id.resp_p proto duration orig_bytes resp_bytes \
  < conn.log | awk '$5 > 3600 && $6 < 10000' | sort -k5 -rn | head -20

# Connexions vers IPs inconnues sur port 443 (TLS C2)
zeek-cut id.orig_h id.resp_h id.resp_p conn_state < conn.log \
  | awk '$3 == 443 && $1 !~ /^10\./ && $1 !~ /^192\.168\./' \
  | sort | uniq -c | sort -rn
```

### Zeek — dns.log

```bash
# Domaines à haute entropie dans dns.log
zeek-cut query qtype_name < dns.log \
  | awk 'length($1) > 50' | sort | uniq -c | sort -rn | head -20

# Requêtes TXT non-SPF/DKIM
zeek-cut query qtype_name answers < dns.log \
  | awk '$2 == "TXT" && $1 !~ /(spf|dkim|dmarc)/' | head -30
```

### Suricata — eve.json

```bash
# Alertes C2 dans eve.json
jq 'select(.event_type=="alert") | {
  timestamp: .timestamp,
  src: .src_ip,
  dest: .dest_ip,
  alert: .alert.signature,
  category: .alert.category
}' /var/log/suricata/eve.json | grep -i "C2\|beacon\|rat\|trojan"

# Stats par catégorie d'alerte
jq -r 'select(.event_type=="alert") | .alert.category' \
  /var/log/suricata/eve.json | sort | uniq -c | sort -rn
```

---

## Combos ports/protocoles suspects

| Protocole observé | Port attendu | Port suspect | Technique |
|---|---|---|---|
| HTTP | 80 | 443, 8080, 8443 | HTTP over HTTPS port |
| HTTPS | 443 | 8443, 8080, 4443 | C2 sur port alternatif |
| DNS | 53 | 5353, 5555, 53535 | DNS tunneling sur port non-standard |
| SSH | 22 | 443, 80 | SSH over HTTP port (bypass FW) |
| SMB | 445 | 135, 139 | Variation SMB legacy |
| RDP | 3389 | 443, 80, 3390 | RDP over HTTPS (bypass) |
| IRC | 6667 | 443, 80 | IRC-based C2 (rare mais existant) |

```splunk
# Détection de protocoles sur mauvais ports
index=network earliest=-24h
| eval protocol_mismatch=case(
    dest_port=443 AND protocol="http", "HTTP_on_HTTPS_port",
    dest_port=80 AND protocol="https", "HTTPS_on_HTTP_port",
    dest_port=53 AND protocol!="dns", "non-DNS_on_DNS_port",
    dest_port!=22 AND protocol="ssh", "SSH_non_standard_port",
    true(), null()
  )
| where isnotnull(protocol_mismatch)
| stats count by src_ip, dest_ip, dest_port, protocol, protocol_mismatch
```

---

## TLS — Fingerprinting JA3/JA3S

```bash
# Extraire JA3 fingerprints depuis PCAP avec zeek
zeek -r capture.pcap policy/protocols/ssl/ja3.zeek
cat ssl.log | zeek-cut ja3 ja3s server_name | sort | uniq -c | sort -rn

# Vérifier JA3 contre base connue (JA3er, TLS-Fingerprint.io)
curl "https://ja3er.com/json/{{JA3_HASH}}"
```

```splunk
# Splunk — JA3 malveillants connus (nécessite lookup ja3_blacklist.csv)
index=ssl earliest=-24h
| lookup ja3_blacklist.csv ja3_hash OUTPUT malware_family, confidence
| where isnotnull(malware_family)
| table _time, src_ip, dest_ip, server_name, ja3_hash, malware_family, confidence
```

<Tip>Les fingerprints JA3/JA3S permettent d'identifier les clients et serveurs TLS indépendamment du SNI ou du certificat — un outil C2 qui usurpe un User-Agent légitime laisse quand même son empreinte JA3 caractéristique dans le handshake TLS. Alimenter une base de JA3 malveillants (abuse.ch, Salesforce JA3) améliore significativement la détection C2.</Tip>
