Detection rules › Splunk

Cisco SA - Automated Web Reconnaissance via HTTP Access Errors

Status
production
Severity
low
Group by
_time, domain, src_ip
Author
Mahamudul Chowdhury, Bhavin Patel, Splunk
Source
github.com/splunk/security_content

This analytic detects probable automated web reconnaissance using Cisco Secure Access proxy telemetry. A high volume of HTTP client errors (401/403/404/etc) across many unique URLs in a short window is consistent with directory/file enumeration behavior generated by tools such as Gobuster, DirBuster, ffuf, or Burp Intruder. Detecting this pattern helps identify pre-exploitation scanning activity, insider reconnaissance, compromised endpoints performing discovery, and attempts to find hidden administrative paths, APIs, backups, and exposed application files.

MITRE ATT&CK coverage

TacticTechniques
ReconnaissanceT1595 Active Scanning

Rule body splunk

name: Cisco SA - Automated Web Reconnaissance via HTTP Access Errors
id: 4cd44520-d404-4ca8-b736-c9d6b86ecf31
version: 2
creation_date: '2026-05-08'
modification_date: '2026-06-09'
author: Mahamudul Chowdhury, Bhavin Patel, Splunk
status: production
type: Anomaly
description: |
    This analytic detects probable automated web reconnaissance using Cisco Secure Access proxy telemetry.
    A high volume of HTTP client errors (401/403/404/etc) across many unique URLs in a short window is consistent with directory/file enumeration behavior generated by tools such as Gobuster, DirBuster, ffuf, or Burp Intruder.
    Detecting this pattern helps identify pre-exploitation scanning activity, insider reconnaissance, compromised endpoints performing discovery, and attempts to find hidden administrative paths, APIs, backups, and exposed application files.
data_source:
    - Cisco Secure Access Proxy
search: |-
    `cisco_secure_access_proxy`
    | eval src_ip=coalesce(src_ip, src)
    | eval host=coalesce(hostname, host)
    | eval user=coalesce(user, identities)
    | eval status=tonumber(status)
    | eval domain=replace(url, "^https?://([^/]+).*$", "\\1")
    | eval user_agent=coalesce(http_user_agent, user_agent)
    | where status IN (400, 401, 403, 404, 405, 407, 414, 429, 431)
    | bucket _time span=10m
    | stats count as errors dc(url) as unique_urls values(status) as statuses values(user_agent) as user_agent values(host) as host values(user) as user by src_ip domain _time
    | where errors > 100 AND unique_urls > 50
    | eval firstTime=_time, lastTime=_time
    | `security_content_ctime(firstTime)`
    | `security_content_ctime(lastTime)`
    | `cisco_sa___automated_web_reconnaissance_via_http_access_errors_filter`
how_to_implement: |
    Ingest Cisco Secure Access proxy logs into Splunk using the Splunk Add-on for Cisco Security Cloud.
    Update the `cisco_secure_access_proxy` macro so it resolves to the index, source, and sourcetype values used in your deployment.
known_false_positives: |
    Aggressive web vulnerability scanners, QA automation, uptime monitoring, and authorized security assessments can generate high HTTP error volumes with large URL diversity.
    Tune by allow-listing known scanner hosts, user agents, test domains, and approved assessment windows using the filter macro.
references:
    - https://attack.mitre.org/techniques/T1595/
drilldown_searches:
    - name: View the detection results for - "$src_ip$"
      search: '%original_detection_search% | search src_ip = "$src_ip$"'
      earliest_offset: $info_min_time$
      latest_offset: $info_max_time$
    - name: View risk events for the last 7 days for - "$src_ip$"
      search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_ip$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`'
      earliest_offset: 7d
      latest_offset: "0"
intermediate_findings:
    entities:
        - field: user
          type: user
          score: 20
          message: Source $src_ip$ triggered probable automated web reconnaissance on $domain$ with $errors$ HTTP access errors across $unique_urls$ unique URLs.
threat_objects:
    - field: domain
      type: domain
    - field: src_ip
      type: ip_address
analytic_story:
    - Cisco Secure Access Analytics
asset_type: Endpoint
mitre_attack_id:
    - T1595
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: network
security_domain: threat
tests:
    - name: Cisco Secure Access Proxy True Positive Test
      attack_data:
        - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/cisco_secure_access/proxy/automated_web_recon_http_errors.log
          source: cisco_cloud_security_addon
          sourcetype: cisco:cloud_security:proxy
      test_type: unit

Stages and Predicates

Stage 1: search

`cisco_secure_access_proxy`

Stage 2: eval

| eval src_ip=coalesce(src_ip, src)

Stage 3: eval

| eval host=coalesce(hostname, host)

Stage 4: eval

| eval user=coalesce(user, identities)

Stage 5: eval

| eval status=tonumber(status)

Stage 6: eval

| eval domain=replace(url, "^https?://([^/]+).*$", "\\1")

Stage 7: eval

| eval user_agent=coalesce(http_user_agent, user_agent)

Stage 8: where

| where status IN (400, 401, 403, 404, 405, 407, 414, 429, 431)

Stage 9: bucket

| bucket _time span=10m

Stage 10: stats

| stats count as errors dc(url) as unique_urls values(status) as statuses values(user_agent) as user_agent values(host) as host values(user) as user by src_ip domain _time

Stage 11: where

| where errors > 100 AND unique_urls > 50

Stage 12: eval

| eval firstTime=_time, lastTime=_time

Stage 13: search

| `security_content_ctime(firstTime)`

Stage 14: search

| `security_content_ctime(lastTime)`

Stage 15: search

| `cisco_sa___automated_web_reconnaissance_via_http_access_errors_filter`

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
errorsgt
  • 100
sourcetypeeq
  • cisco:cloud_security:proxy
statusin
  • 400
  • 401
  • 403
  • 404
  • 405
  • 407
  • 414
  • 429
  • 431
unique_urlsgt
  • 50