Kernel of Truth

Below are Splunk detection query examples for WAF logs. They are written to be vendor-neutral, and I’ve included vendor-specific variants (AWS WAF, Cloudflare) where it materially helps.

Assumptions:

  • Your WAF logs land in Splunk with fields resembling src_ip, uri_path, uri_query, http_method, status, action, rule, user_agent, host, bytes, request_body (where available).
  • Replace index=waf and sourcetype=waf:* with your actual index/sourcetype.

1) High volume blocks from a single source (scan or L7 flood)

Detects a single IP triggering lots of blocks across multiple URLs.

index=waf sourcetype=waf:*
(action=block OR status IN (403,406,429))
| stats count as blocks dc(uri_path) as unique_paths values(rule) as rules earliest(_time) as firstSeen latest(_time) as lastSeen by src_ip host
| where blocks >= 50 OR unique_paths >= 20
| eval duration=lastSeen-firstSeen
| sort - blocks

Tuning ideas:

  • Increase thresholds during known pentest windows.
  • Add | where cidrmatch("x.x.x.x/yy", src_ip)=0 to exclude corporate IP ranges.

2) SQL Injection indicators in query string (generic)

Useful when your WAF vendor does not provide a clean attack_type field.

index=waf sourcetype=waf:*
(action=block OR status IN (403,406))
| eval q=coalesce(uri_query, url_query, query_string, "")
| where match(lower(q), "(union(\s|%20)+select|or(\s|%20)+1=1|'\s*or\s*'1'='1|sleep\(|benchmark\(|information_schema|%27%20or%20)")
| stats count as hits values(uri_path) as paths values(q) as sample_query values(rule) as rules by src_ip host user_agent
| sort - hits

3) XSS indicators (generic)

Looks for script injection patterns in URL/query.

index=waf sourcetype=waf:*
(action=block OR status IN (403,406))
| eval raw=lower(coalesce(uri_query,"")." ".coalesce(uri_path,""))
| where match(raw, "(<script|%3cscript|onerror=|onload=|javascript:|%3cimg|%3csvg)")
| stats count values(uri_path) as paths values(uri_query) as queries by src_ip host user_agent
| sort - count

4) Path traversal and sensitive file probing

Catches ../ traversal and common sensitive targets.

index=waf sourcetype=waf:*
(action=block OR status IN (403,404,406))
| eval target=lower(coalesce(uri_path,""))
| where match(target, "(\.\./|%2e%2e%2f|%252e%252e%252f)") 
   OR match(target, "(/etc/passwd|/proc/self/environ|/windows/win\.ini|/\.git/|/\.env|/wp-config\.php|/phpmyadmin|/cgi-bin)")
| stats count as hits values(target) as targets values(rule) as rules by src_ip host user_agent
| sort - hits

5) Credential stuffing or brute-force against login endpoints

Focuses on login paths and authentication failures, plus behavioural signals.

index=waf sourcetype=waf:*
| eval path=lower(coalesce(uri_path,""))
| search path IN ("/login","/signin","/wp-login.php","/oauth/token","/api/auth","/users/sign_in")
| stats count as attempts dc(user_agent) as ua_count dc(src_ip) as src_count values(action) as actions values(status) as statuses by host path
| sort - attempts

Variant: “single IP brute forcing a login path”

index=waf sourcetype=waf:*
| eval path=lower(coalesce(uri_path,""))
| search path IN ("/login","/signin","/wp-login.php","/oauth/token","/api/auth","/users/sign_in")
| stats count as attempts dc(user_agent) as ua_count values(status) as statuses values(action) as actions by src_ip host path
| where attempts >= 30
| sort - attempts

6) Bot-like user agents and tooling

Flags typical scanners and automation frameworks.

index=waf sourcetype=waf:*
(action=block OR status IN (403,406,429))
| eval ua=lower(coalesce(user_agent,"unknown"))
| where match(ua, "(sqlmap|nikto|nmap|masscan|acunetix|burp|zap|gobuster|dirbuster|python-requests|curl|wget)")
| stats count values(ua) as user_agents values(uri_path) as paths by src_ip host
| sort - count

7) Suspicious method use (TRACE, TRACK) and odd verbs

TRACE is often disabled; unexpected methods can indicate probing.

index=waf sourcetype=waf:*
| eval m=upper(coalesce(http_method, method, ""))
| where m IN ("TRACE","TRACK","CONNECT") OR (m="PUT" OR m="DELETE") 
| stats count values(uri_path) as paths values(status) as statuses values(action) as actions by src_ip host m
| sort - count

Tune it by excluding known APIs that legitimately use PUT/DELETE.


8) Potential SSRF probing patterns (limited but useful)

SSRF can be hard to detect purely at WAF, but these patterns are common.

index=waf sourcetype=waf:*
(action=block OR status IN (403,406))
| eval raw=lower(coalesce(uri_query,"")." ".coalesce(request_body,""))
| where match(raw, "(http://|https://|file://|gopher://|ftp://|127\.0\.0\.1|localhost|169\.254\.169\.254|metadata\.google|metadata\.azure)")
| stats count values(uri_path) as paths values(uri_query) as queries by src_ip host user_agent
| sort - count

9) New source countries (if geo IP is available)

Only works if you have geolocation enrichment (Splunk built-in or via app).

index=waf sourcetype=waf:*
(action=block OR status IN (403,406,429))
| iplocation src_ip
| stats count as blocks dc(uri_path) as unique_paths by Country src_ip host
| where blocks >= 20 AND unique_paths >= 10
| sort - blocks

Use this for “new country with high blocks”, especially for customer-facing apps.


Vendor-specific examples

AWS WAF (common fields: httpRequest.clientIp, terminatingRuleId, action, httpRequest.uri)

index=waf sourcetype=aws:waf
| rename httpRequest.clientIp as src_ip httpRequest.uri as uri_path httpRequest.args as uri_query terminatingRuleId as rule
| search action="BLOCK"
| stats count dc(uri_path) as unique_paths values(rule) as rules by src_ip host
| where count >= 50 OR unique_paths >= 20
| sort - count

Cloudflare WAF (common fields: ClientIP, ClientRequestPath, WAFAction, WAFRuleID)

index=waf sourcetype=cloudflare:waf
| rename ClientIP as src_ip ClientRequestPath as uri_path WAFAction as action WAFRuleID as rule
| search action IN ("block","challenge","jschallenge")
| stats count dc(uri_path) as unique_paths values(rule) as rules by src_ip host
| where count >= 50 OR unique_paths >= 20
| sort - count

Operational guidance (what to alert on)

For alerting, the best high-signal patterns are:

  • High blocks per IP with broad path diversity (recon/scanning)
  • Login endpoint attack bursts (credential stuffing)
  • Tooling user agents (sqlmap, Burp, ZAP, Nikto)
  • Traversal and sensitive file probing (/.env, /.git/, /etc/passwd)

Avoid alert fatigue by:

  • Using thresholds
  • Suppressing known scanners (internal IPs, authorised testing)
  • Deduplicating alerts by src_ip + host + rule over a rolling window


🛡️Latest Security Alerts 🛡️

NCSC Latest
(The National Cyber Security Centre UK)