HIGH Zeek Reconnaissance Sweep LTR-0018 ✓ validated

Detect Web Attack Recon and SQL Injection in Zeek HTTP Logs

Published

MITRE ATT&CK: T1595.002T1190T1083

Sigma Rule

title: Zeek HTTP — Web Attack Recon, SQLi, and Tooling
id: a5996c6e-547f-43ef-bdbc-7e4de0cb2fc5
status: experimental
description: >
  Detects web-application attack activity in Zeek http.log network telemetry via
  three independent signals: probing for sensitive files and admin panels
  (/.env, /wp-admin/, /admin/config.php), a SQL injection payload in the request
  URI (UNION SELECT), and the user-agent of an automated injection tool. Zeek
  sees this from the wire, so it captures the attack even against an application
  whose own logs are unavailable, misconfigured, or already tampered with. Any
  one signal fires the rule.
references:
  - https://attack.mitre.org/techniques/T1595/002/
  - https://attack.mitre.org/techniques/T1190/
author: LogTriage
date: 2026/09/18
logsource:
  product: zeek
  service: http
detection:
  sensitive_file_probe:
    uri|contains:
      - '/.env'
      - '/wp-admin/install.php'
      - '/admin/config.php'
  sqli_payload:
    uri|contains:
      - 'UNION SELECT'
  attack_tool:
    user_agent|contains:
      - 'sqlmap'
  condition: sensitive_file_probe or sqli_payload or attack_tool
falsepositives:
  - Authorized scanners (allowlist the originating host id.orig_h)
  - An application path that legitimately contains config.php or an env route
  - Announced bug-bounty or pentest traffic
level: high
tags:
  - attack.reconnaissance
  - attack.t1595.002
  - attack.initial_access
  - attack.t1190

What this rule detects

Zeek (formerly Bro) reconstructs application-layer events from raw network traffic, and its http.log records the method, host, URI, user-agent, and response status of every HTTP request on the monitored segment. This rule fires on three independent web-attack signals in that telemetry:

  1. Sensitive-file probing — /.env, /wp-admin/install.php, /admin/config.php.
  2. SQL injection — a UNION SELECT payload in the request URI.
  3. Attack tooling — a sqlmap user-agent.

Any one fires the rule. Each has no benign interpretation against a normal internal application, so there is no count threshold.

Detection logic

The value of doing this at the network layer is independence. The rule keys on Zeek’s uri and user_agent fields — populated by a sensor the attacker does not control — so it holds up even when the application’s own logs are missing, misconfigured, or being tampered with. It is also application-agnostic: a single rule covers every web service on the segment.

The three selections map to three phases of the same intrusion:

SelectionPhaseSignal
sensitive_file_probeDiscoveryRequests for secret files and admin panels
sqli_payloadExploitationUNION SELECT in the URI
attack_toolAutomationsqlmap user-agent

They are combined with or, not and, on purpose: an attacker who randomizes the sqlmap user-agent still trips the URI-based selections the moment they request /.env or send a UNION SELECT. The tool-UA branch catches the careless; the URI branches catch everyone, because you cannot pursue the objective without sending the payload.

Validated against a real sample

Validated against zeek_http_attack.log (shipped with LogTriage), a Zeek #path http TSV capturing a single attacker, 185.220.101.42 (and a second address 45.142.212.100), against the internal host 10.0.0.50:

  • A sqlmap/1.7.8#stable request for /api/v1/users?id=1 UNION SELECT 1,username,password FROM users-- (status 200) — injection against the users table
  • The same tool hitting /admin/config.php (status 403)
  • Three python-requests/2.28.0 POSTs to /api/v1/auth/login, all 401 — a credential brute-force burst
  • A spoofed Googlebot user-agent fetching /wp-admin/install.php (status 200)
  • A Go-http-client/1.1 request for /.env (status 200, 1,024-byte body) — the environment file actually served

That is discovery, injection, brute force, and credential harvesting in one connection thread.

The rule fires on that sample and stays silent on the matching benign log, which contains normal browser and okhttp traffic to /api/v1/products, /api/v1/users/42, and /api/v1/orders against api.example.com — no UNION SELECT, no sqlmap, and none of the probed paths.

False positives

The main source is your own tooling. Authorized scanners — Nikto, Nuclei, sqlmap on the security team’s schedule — emit byte-identical URIs and user-agents, so allowlist the scanner’s originating host (id.orig_h) rather than deleting patterns the real attacker also uses.

The narrower case is an application that legitimately serves a path containing config.php or an env-style route; scope those specific hosts out. But an unknown external source requesting /.env or sending UNION SELECT to an internal app is the rule doing exactly its job — and if that /.env request returned a 200, it is already an incident.

Frequently Asked Questions

Why detect web attacks in Zeek instead of in the web server's own access log?
Because Zeek sees the traffic on the wire, independent of the application. If the target app is unmanaged shadow IT, if its logging is misconfigured, or if the attacker has already compromised the host and is tampering with local logs, the web server's own record is unreliable — but Zeek's http.log, generated by a network sensor the attacker cannot touch, still shows every request. Network telemetry is the detection of last resort precisely because it is out of the attacker's reach. It is also format-agnostic: one Zeek rule covers every web app on the segment, not just the ones you remembered to instrument.
What makes /.env such a high-value probe to alert on?
A .env file is where applications keep their secrets in plaintext — database passwords, API keys, cloud credentials, signing secrets. It should never be web-reachable, so an external request for /.env has no benign explanation: the attacker is not browsing, they are attempting to harvest credentials directly from a misconfiguration. In the validation sample the /.env request returns a 200 with a 1 KB body, which means the file was actually served. That single event can hand an attacker the keys to the whole environment, which is why it is worth a high-severity alert even in isolation.
The three selections look unrelated — why bundle them into one rule?
They are the three faces of the same early-stage web attack, and grouping them gives one alert per campaign instead of three. Sensitive-file probing is discovery, SQL injection is exploitation, and the tool user-agent is the automation driving both. An attacker rarely does just one: the sample shows the same source running a UNION SELECT, hitting /admin/config.php, brute-forcing /api/v1/auth/login, then grabbing /.env. Bundling the signals as an OR means whichever phase your sensor happens to catch first, the rule fires — and an analyst triaging it sees the whole picture in one Zeek connection thread.
Will an attacker who changes sqlmap's user-agent evade this?
They evade the attack_tool selection, yes — but not the rule. The three selections are deliberately independent (an OR, not an AND), so an attacker who randomizes their user-agent still trips sensitive_file_probe the moment they request /.env or /wp-admin/, and still trips sqli_payload the moment a UNION SELECT crosses the wire. The tool-UA branch is a cheap bonus catch for lazy operators, not a load-bearing requirement. The URI-based signals are the durable ones because the attacker cannot pursue the objective without sending the payload or requesting the target.

Related Resources

See this detection run on a real report

Try the live demo with a pre-loaded malicious log set — no signup required — or upload your own log file and get a full AI-reviewed threat report in minutes.