Web Server Local File Inclusion Activity

This rule detects potential Local File Inclusion (LFI) activity on web servers by identifying HTTP GET requests that attempt to access sensitive local files through directory traversal techniques or known file paths. Attackers may exploit LFI vulnerabilities to read sensitive files, gain system information, or further compromise the server.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2025/12/02"
  3integration = ["nginx", "apache", "apache_tomcat", "iis"]
  4maturity = "production"
  5min_stack_version = "9.2.0"
  6min_stack_comments = "The esql url_decode() operator was introduced in version 9.2.0"
  7updated_date = "2025/12/08"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12This rule detects potential Local File Inclusion (LFI) activity on web servers by identifying HTTP GET requests that
 13attempt to access sensitive local files through directory traversal techniques or known file paths. Attackers may
 14exploit LFI vulnerabilities to read sensitive files, gain system information, or further compromise the server.
 15"""
 16from = "now-11m"
 17interval = "10m"
 18language = "esql"
 19license = "Elastic License v2"
 20name = "Web Server Local File Inclusion Activity"
 21note = """ ## Triage and analysis
 22
 23> **Disclaimer**:
 24> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
 25
 26### Investigating Web Server Local File Inclusion Activity
 27
 28This rule surfaces successful GET requests containing directory traversal or direct access to sensitive paths, signaling Local File Inclusion exploitation that can expose credentials, configuration, and process context and enable further compromise. A common attacker pattern is abusing a vulnerable parameter to fetch ../../../../etc/passwd, then pivoting to /proc/self/environ to harvest secrets and identify execution context for subsequent steps.
 29
 30### Possible investigation steps
 31
 32- Retrieve contiguous access logs around the alert to rebuild each request/response pair (URI, parameters, user agent, referer, cookies, X-Forwarded-For) and identify which parameter reflected traversal or wrapper usage and whether the response likely contained file contents.
 33- Compare response sizes and content-types for the suspicious requests to normal pages and look for signatures such as "root:x:" lines, INI/XML keys, or base64 blobs that indicate disclosure of /etc/passwd, web.config/applicationhost.config, or other sensitive files.
 34- Review web server and application error logs at the same timestamps for include/open stream warnings, open_basedir or allow_url_fopen messages, and stack traces to confirm the code path handling the input and any mitigations in place.
 35- Pivot on the same source and timeframe to find adjacent probes (php://filter, data://, expect://, zip://, phar://, /proc/self/environ, traversal into webroots/configs) and any follow-on POSTs to upload endpoints or new script paths, signaling progression toward RCE or webshell placement.
 36- Determine whether the traffic was authenticated and whether it traversed a WAF or reverse proxy by correlating cookies or session IDs and client IPs with proxy/WAF logs, noting any blocks, rule matches, or bypasses to bound scope and urgency.
 37
 38### False positive analysis
 39
 40- A site search or documentation endpoint echoing user-supplied text can include strings like ../../../../etc/passwd, windows/win.ini, or php://filter in the query string and return a normal 200 OK results page rather than performing a file include.
 41- An authenticated admin feature (such as a log viewer or file browser) may legitimately accept path= or file= parameters referencing local paths like /var/log/nginx or /inetpub/logs/logfiles and return 200 when serving allowed files, producing URLs that match the rule without exploitation.
 42
 43### Response and remediation
 44
 45- Immediately block the source IP at the reverse proxy/WAF and deploy deny rules for GET requests using ../../ or ..\\..\\ traversal or wrappers (php://, expect://, data://) that fetch /etc/passwd, /proc/self/environ, wp-config.php, web.config, or applicationhost.config.
 46- Configure the web server to return 403 for paths resolving to /proc, /etc, /var/log, /inetpub, applicationhost.config, and web.config and to reject wrapper schemes like php:// and expect://, then reload Nginx/Apache/IIS to apply.
 47- Fix the vulnerable include logic by canonicalizing input with realpath, rejecting any .. segments or absolute paths, enforcing a whitelist of allowed files, and in PHP disabling allow_url_include/allow_url_fopen and setting open_basedir to a safe directory.
 48- Rotate exposed secrets by changing database and API credentials from wp-config.php, connection strings and machine keys from web.config/applicationhost.config, and any tokens in /proc/self/environ, then invalidate active sessions and cache.
 49- Escalate to incident leadership and quarantine the host if response bodies contain credential patterns (e.g., "root:x:" from /etc/passwd or XML keys from web.config), if /etc/shadow or windows/system32/config/SAM was requested, or if follow-on POSTs or new .php/.aspx files appear in the webroot.
 50- Recover by verifying integrity of /var/www and /inetpub/wwwroot, scanning for webshells and unexpected includes, redeploying a known-good build or container image if tampering is found, and adding WAF normalization to double-decode URLs and 403 traversal attempts.
 51"""
 52risk_score = 21
 53rule_id = "90e4ceab-79a5-4f8e-879b-513cac7fcad9"
 54severity = "low"
 55tags = [
 56    "Domain: Web",
 57    "Use Case: Threat Detection",
 58    "Tactic: Discovery",
 59    "Data Source: Nginx",
 60    "Data Source: Apache",
 61    "Data Source: Apache Tomcat",
 62    "Data Source: IIS",
 63    "Resources: Investigation Guide",
 64]
 65timestamp_override = "event.ingested"
 66type = "esql"
 67query = '''
 68from
 69  logs-nginx.access-*,
 70  logs-apache.access-*,
 71  logs-apache_tomcat.access-*,
 72  logs-iis.access-*
 73| where
 74    http.request.method == "GET" and
 75    http.response.status_code == 200 and
 76    url.original like "*=*"
 77
 78| eval Esql.url_original_url_decoded_to_lower = to_lower(URL_DECODE(url.original))
 79
 80| where
 81  /* 1) Relative traversal */
 82    Esql.url_original_url_decoded_to_lower like "*../../../../*" or           // Unix-style traversal
 83    Esql.url_original_url_decoded_to_lower like "*..\\\\..\\\\..\\\\..*" or           // Windows-style traversal
 84    // Potential security check bypassing (enforcing multiple dots and shortening the pattern)
 85    Esql.url_original_url_decoded_to_lower like "*..././*" or
 86    Esql.url_original_url_decoded_to_lower like "*...\\*" or
 87    Esql.url_original_url_decoded_to_lower like "*....\\*" or
 88
 89  /* 2) Linux system identity / basic info */
 90    Esql.url_original_url_decoded_to_lower like "*etc/passwd*" or
 91    Esql.url_original_url_decoded_to_lower like "*etc/shadow*" or
 92    Esql.url_original_url_decoded_to_lower like "*etc/hosts*" or
 93    Esql.url_original_url_decoded_to_lower like "*etc/os-release*" or
 94    Esql.url_original_url_decoded_to_lower like "*etc/issue*" or
 95
 96  /* 3) Linux /proc enumeration */
 97    Esql.url_original_url_decoded_to_lower like "*proc/self/environ*" or
 98    Esql.url_original_url_decoded_to_lower like "*proc/self/cmdline*" or
 99    Esql.url_original_url_decoded_to_lower like "*proc/self/fd*" or
100    Esql.url_original_url_decoded_to_lower like "*proc/self/exe*" or
101
102  /* 4) Linux webroots, configs & logs */
103    Esql.url_original_url_decoded_to_lower like "*var/www*" or               // generic webroot
104    Esql.url_original_url_decoded_to_lower like "*wp-config.php*" or         // classic WP config
105    Esql.url_original_url_decoded_to_lower like "*etc/apache2*" or
106    Esql.url_original_url_decoded_to_lower like "*etc/httpd*" or
107    Esql.url_original_url_decoded_to_lower like "*etc/nginx*" or
108    Esql.url_original_url_decoded_to_lower like "*var/log/apache2*" or
109    Esql.url_original_url_decoded_to_lower like "*var/log/httpd*" or
110    Esql.url_original_url_decoded_to_lower like "*var/log/nginx*" or
111
112  /* 5) Windows core files / identity */
113    Esql.url_original_url_decoded_to_lower like "*windows/panther/*unattend*" or
114    Esql.url_original_url_decoded_to_lower like "*windows/debug/netsetup.log*" or
115    Esql.url_original_url_decoded_to_lower like "*windows/win.ini*" or
116    Esql.url_original_url_decoded_to_lower like "*windows/system32/drivers/etc/hosts*" or
117    Esql.url_original_url_decoded_to_lower like "*boot.ini*" or
118    Esql.url_original_url_decoded_to_lower like "*windows/system32/config/*" or
119    Esql.url_original_url_decoded_to_lower like "*windows/repair/sam*" or
120    Esql.url_original_url_decoded_to_lower like "*windows/system32/license.rtf*" or
121
122  /* 6) Windows IIS / .NET configs, webroots & logs */
123     Esql.url_original_url_decoded_to_lower like "*/inetpub/wwwroot*" or
124     Esql.url_original_url_decoded_to_lower like "*/inetpub/logs/logfiles*" or
125     Esql.url_original_url_decoded_to_lower like "*applicationhost.config*" or
126     Esql.url_original_url_decoded_to_lower like "*/microsoft.net/framework64/*/config/web.config*" or
127     Esql.url_original_url_decoded_to_lower like "*windows/system32/inetsrv/*" or
128
129  /* 7) PHP & protocol wrappers */
130     Esql.url_original_url_decoded_to_lower like "*php://*" or
131     Esql.url_original_url_decoded_to_lower like "*zip://*" or
132     Esql.url_original_url_decoded_to_lower like "*phar://*" or
133     Esql.url_original_url_decoded_to_lower like "*expect://*" or
134     Esql.url_original_url_decoded_to_lower like "*file://*" or
135     Esql.url_original_url_decoded_to_lower like "*data://text/plain;base64*"
136
137| keep
138    @timestamp,
139    Esql.url_original_url_decoded_to_lower,
140    source.ip,
141    agent.id,
142    host.name,
143    http.request.method,
144    http.response.status_code,
145    event.dataset,
146    data_stream.namespace
147
148| stats
149    Esql.event_count = count(),
150    Esql.url_original_url_decoded_to_lower_count_distinct = count_distinct(Esql.url_original_url_decoded_to_lower),
151    Esql.host_name_values = values(host.name),
152    Esql.agent_id_values = values(agent.id),
153    Esql.http_request_method_values = values(http.request.method),
154    Esql.http_response_status_code_values = values(http.response.status_code),
155    Esql.url_original_url_decoded_to_lower_values = values(Esql.url_original_url_decoded_to_lower),
156    Esql.event_dataset_values = values(event.dataset),
157    Esql.data_stream_namespace_values = values(data_stream.namespace)
158    by source.ip
159'''
160
161[[rule.threat]]
162framework = "MITRE ATT&CK"
163
164[[rule.threat.technique]]
165id = "T1083"
166name = "File and Directory Discovery"
167reference = "https://attack.mitre.org/techniques/T1083/"
168
169[rule.threat.tactic]
170id = "TA0007"
171name = "Discovery"
172reference = "https://attack.mitre.org/tactics/TA0007/"

Triage and analysis

Disclaimer: This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.

Investigating Web Server Local File Inclusion Activity

This rule surfaces successful GET requests containing directory traversal or direct access to sensitive paths, signaling Local File Inclusion exploitation that can expose credentials, configuration, and process context and enable further compromise. A common attacker pattern is abusing a vulnerable parameter to fetch ../../../../etc/passwd, then pivoting to /proc/self/environ to harvest secrets and identify execution context for subsequent steps.

Possible investigation steps

  • Retrieve contiguous access logs around the alert to rebuild each request/response pair (URI, parameters, user agent, referer, cookies, X-Forwarded-For) and identify which parameter reflected traversal or wrapper usage and whether the response likely contained file contents.
  • Compare response sizes and content-types for the suspicious requests to normal pages and look for signatures such as "root:x:" lines, INI/XML keys, or base64 blobs that indicate disclosure of /etc/passwd, web.config/applicationhost.config, or other sensitive files.
  • Review web server and application error logs at the same timestamps for include/open stream warnings, open_basedir or allow_url_fopen messages, and stack traces to confirm the code path handling the input and any mitigations in place.
  • Pivot on the same source and timeframe to find adjacent probes (php://filter, data://, expect://, zip://, phar://, /proc/self/environ, traversal into webroots/configs) and any follow-on POSTs to upload endpoints or new script paths, signaling progression toward RCE or webshell placement.
  • Determine whether the traffic was authenticated and whether it traversed a WAF or reverse proxy by correlating cookies or session IDs and client IPs with proxy/WAF logs, noting any blocks, rule matches, or bypasses to bound scope and urgency.

False positive analysis

  • A site search or documentation endpoint echoing user-supplied text can include strings like ../../../../etc/passwd, windows/win.ini, or php://filter in the query string and return a normal 200 OK results page rather than performing a file include.
  • An authenticated admin feature (such as a log viewer or file browser) may legitimately accept path= or file= parameters referencing local paths like /var/log/nginx or /inetpub/logs/logfiles and return 200 when serving allowed files, producing URLs that match the rule without exploitation.

Response and remediation

  • Immediately block the source IP at the reverse proxy/WAF and deploy deny rules for GET requests using ../../ or ....\ traversal or wrappers (php://, expect://, data://) that fetch /etc/passwd, /proc/self/environ, wp-config.php, web.config, or applicationhost.config.
  • Configure the web server to return 403 for paths resolving to /proc, /etc, /var/log, /inetpub, applicationhost.config, and web.config and to reject wrapper schemes like php:// and expect://, then reload Nginx/Apache/IIS to apply.
  • Fix the vulnerable include logic by canonicalizing input with realpath, rejecting any .. segments or absolute paths, enforcing a whitelist of allowed files, and in PHP disabling allow_url_include/allow_url_fopen and setting open_basedir to a safe directory.
  • Rotate exposed secrets by changing database and API credentials from wp-config.php, connection strings and machine keys from web.config/applicationhost.config, and any tokens in /proc/self/environ, then invalidate active sessions and cache.
  • Escalate to incident leadership and quarantine the host if response bodies contain credential patterns (e.g., "root:x:" from /etc/passwd or XML keys from web.config), if /etc/shadow or windows/system32/config/SAM was requested, or if follow-on POSTs or new .php/.aspx files appear in the webroot.
  • Recover by verifying integrity of /var/www and /inetpub/wwwroot, scanning for webshells and unexpected includes, redeploying a known-good build or container image if tampering is found, and adding WAF normalization to double-decode URLs and 403 traversal attempts.

Related rules

to-top