MDstable
NoteSnippetChecklistPlaybook

Investigation réseau — SOC

Analyse de flux réseau, détection C2, beaconing, tunneling DNS/HTTPS

snippetadvanced 2025-05-13 6 min read
socnetworkC2beaconingDNSinvestigationZeekSuricata

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
Variables
{{EXTERNAL_IP}}
# 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
Variables
{{SUSPICIOUS_IP}}
{{EXTERNAL_IP}}
# 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
Variables
{{CDN_RANGES}}
# 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
Headers suspects X-Malware-ID, X-Session-ID dans requtes POST
Certificats TLS auto-signés avec CN "Major Cobalt Strike User"
JA3 fingerprint connu CS default
JARM fingerprint
# Splunk — Cobalt Strike beacon default
indexproxy url"*/submit.php" methodPOST 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 gnrique sur reverse HTTPS
Stage download GET /AAAA payload 4 bytes puis rponse >
Port par dfaut 4444 reverse_tcp 4433 reverse_https

Détection générique C2

splunk
Variables
{{KNOWN_IP_SERVICES}}
# 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 idorig_h idresp_h idresp_p proto duration orig_bytes resp_bytes
< connlog | awk '$5 > 3600 && $6 < 10000' | sort -k5 -rn | head -20
# Connexions vers IPs inconnues sur port 443 (TLS C2)
zeek-cut idorig_h idresp_h idresp_p conn_state < connlog
| 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 < dnslog
| awk 'length($1) > 50' | sort | uniq -c | sort -rn | head -20
# Requêtes TXT non-SPF/DKIM
zeek-cut query qtype_name answers < dnslog
| awk '$2 == "TXT" && $1 !~ /(spf|dkim|dmarc)/' | head -30

Suricata — eve.json

bash
# Alertes C2 dans eve.json
jq selectevent_type"alert" |
timestamp timestamp
src src_ip
dest dest_ip
alert alertsignature
category alertcategory
/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
Variables
{{JA3_HASH}}
# Extraire JA3 fingerprints depuis PCAP avec zeek
zeek -r capturepcap policy/protocols/ssl/ja3.zeek
cat ssllog | 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.
OPS·BRAIN v1.075 notes · Securitylocal