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:
- Sensitive-file probing —
/.env,/wp-admin/install.php,/admin/config.php. - SQL injection — a
UNION SELECTpayload in the request URI. - Attack tooling — a
sqlmapuser-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:
| Selection | Phase | Signal |
|---|---|---|
sensitive_file_probe | Discovery | Requests for secret files and admin panels |
sqli_payload | Exploitation | UNION SELECT in the URI |
attack_tool | Automation | sqlmap 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#stablerequest 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.0POSTs to/api/v1/auth/login, all 401 — a credential brute-force burst - A spoofed
Googlebotuser-agent fetching/wp-admin/install.php(status 200) - A
Go-http-client/1.1request 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.