Chapter 2.2 Quiz - IDS/IPS: Signatures, Anomaly Detection & Evasion
Quiz Mode - All answers are hidden under collapsible sections. Attempt each question before revealing the answer.
Question 1
You deploy a Suricata rule with the content keyword matching "passwd" in HTTP URI. During testing, an attacker uses the URL /etc%2Fpasswd. Your rule does not fire. What is the most likely cause, and what is the fix?
Reveal Answer & Explanation
Answer: The content keyword is matching against the raw (un-normalized) URI buffer. %2F is the URL-encoded form of /, so the literal bytes in the packet are %2Fpasswd, not /passwd.
Fix: Use the http_uri buffer modifier, which applies Suricata's HTTP normalization (URL decoding) before matching:
alert http any any -> $HTTP_SERVERS any (
msg:"LFI attempt - /etc/passwd";
content:"etc"; http_uri; nocase;
content:"passwd"; http_uri; nocase; distance:0;
sid:9900010;
rev:1;
)
http_uri invokes the HTTP inspector, which decodes %2F to /, %252F to %2F (double-encoding), and collapses /../ sequences. The match now fires on /etc%2Fpasswd, /etc/./passwd, /%65tc/passwd (hex-encoded 'e'), etc.
Deeper context: HTTP normalization is a multi-step process; double-encoding (%252F to %2F to /) requires two decode passes. Configure double_decode: yes in the Suricata HTTP configuration to handle this. This corresponds to evasion of T1027 (Obfuscated Files or Information).
Question 2
An attacker runs nmap -sS -T0 --randomize-hosts 10.0.0.0/24. Your Snort sfportscan preprocessor is configured to fire after 15 probes in 60 seconds from a single source. Does this scan trigger the alert? Why or why not? What detection mechanism would catch it?
Reveal Answer & Explanation
Answer: With -T0 (paranoid timing), Nmap sends one probe every ~5 minutes and randomizes target order. Over a /24 (254 hosts), the total scan duration is ~21 hours. In any 60-second window, at most 1-2 probes originate from the scanner - far below the 15-probe threshold. The sfportscan alert does not fire.
What catches it:
- Long-window behavioral aggregation - NetFlow/IPFIX analysis with a multi-hour window. Tools like Zeek + custom analytics or a SIEM query over connection logs can detect a single source touching many unique destinations over hours.
# Zeek conn.log: count unique dst IPs per src over the last 24h
cat conn.log \
| zeek-cut id.orig_h id.resp_h \
| awk '{count[$1][$2]=1} END {for(src in count) print length(count[src]), src}' \
| sort -rn | head -20
-
SYN-only flow anomaly - Stealth SYN scans (
-sS) never complete the three-way handshake. A source IP with hundreds of SYN-only flows (no corresponding SYN-ACK from target or ACK from source) is a statistical outlier regardless of timing. -
Distributed scan detection - If the attacker uses
-D(decoys) or multiple source IPs, per-source thresholds fail entirely. Detection requires correlation across sources (shared destination patterns, same scan timing).
This maps to T1046 (Network Service Discovery).
Question 3
Explain the JA3 fingerprinting technique. Why does it provide detection value against TLS-encrypted C2 traffic, and what are its limits?
Reveal Answer & Explanation
Answer: JA3 hashes a set of fields from the TLS ClientHello message - fields that are determined by the TLS client library (not the application payload): TLS version, cipher suite list (in order), extension list (in order), elliptic curves, and elliptic curve point formats. The MD5 hash of these concatenated values is the JA3 fingerprint.
JA3 = MD5(SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats)
Why it has detection value:
- A specific C2 framework (e.g., Cobalt Strike with default profile) produces a consistent JA3 regardless of payload. The beacon's TLS library is identifiable even though the payload is encrypted.
- Known-malicious JA3 hashes are published by threat intel sources and can be matched against observed traffic.
- A JA3 mismatch - e.g., a connection to port 443 that produces a JA3 fingerprint associated with the Python
requestslibrary rather than a browser - is anomalous even without a specific IOC match.
# Zeek ssl.log has JA3 natively
cat ssl.log | zeek-cut ts id.orig_h ja3 server_name \
| grep "51c64c77e60f3980eea90869b68c58a8" # known Cobalt Strike default
Limits:
- Malleable profiles: Cobalt Strike and Sliver let operators customize the TLS profile, changing the JA3 fingerprint. Defenders cannot rely on a static hash blocklist for sophisticated actors.
- Shared JA3s: A JA3 associated with a popular browser (Chrome's JA3 is used by millions of legitimate sessions). Attacker libraries can mimic Chrome's TLS fingerprint.
- JA3S (server fingerprint): Hashes the ServerHello response; combining JA3 + JA3S pairs reduces ambiguity but increases tuning complexity.
- TLS 1.3 + ECH (Encrypted Client Hello): ECH encrypts the ClientHello extension data, potentially eliminating JA3 signal in future deployments.
JA3 is one signal in a layered detection strategy, not a standalone detection method. This relates to T1573 (Encrypted Channel).
Question 4
Write a Suricata rule that detects potential DNS tunneling by matching DNS queries with a QNAME longer than 50 characters over UDP port 53. Include appropriate metadata.
Reveal Answer & Explanation
Answer:
alert dns any any -> any 53 (
msg:"POSSIBLE DNS Tunnel - Excessive QNAME Length";
dns_query; -- match in the DNS query name buffer
dsize:>50; -- DNS query payload > 50 bytes (proxy for long QNAME)
threshold:type limit, -- rate-limit: alert once per source per 60s
track by_src,
count 1,
seconds 60;
classtype:policy-violation;
sid:9900020;
rev:1;
metadata:attack_target DNS_Server,
affected_product DNS,
mitre_tactic TA0011, -- Command and Control
mitre_technique T1071.004; -- Application Layer Protocol: DNS
)
Notes on accuracy:
dns_queryis a Suricata-specific sticky buffer that normalizes the DNS QNAME field, making the content/dsize match operate on the decoded query name.dsizealone is a blunt instrument - it fires on any large DNS payload, not just long query names. For production, combine with a PCRE or Lua script to measure only the QNAME length precisely.- Long QNAME queries are used in iodine, dns2tcp, dnscat2, and similar DNS tunneling tools. Legitimate queries rarely exceed 40 characters; DGA domains cluster around 20-30 characters but with high entropy rather than length.
- Pair this rule with entropy analysis (see Lua example in Section 6) for lower false-positive rates.
Detection context (T1071.004): DNS tunneling typically shows:
- Queries to a single authoritative NS (the attacker's domain) at high frequency
- Large TXT or NULL record responses
- Unusually high DNS query rate per host
- Responses containing base64 or binary-encoded data
Combine NIDS detection with DNS query logging (BIND querylog, Windows DNS debug logging) for full coverage.