CVE-2025-29914: OWASP Coraza WAF URI Parsing Confusion Leading to Rule Bypass

Executive Summary

CVE-2025-29914 describes a vulnerability in OWASP Coraza Web Application Firewall (WAF) versions prior to 3.3.3. This vulnerability arises from a parser confusion issue when handling URIs that begin with double slashes (//). Due to this confusion, the REQUEST_FILENAME variable, which is crucial for rule matching, is populated with an incorrect value. This can lead to a bypass of intended WAF rules, potentially allowing malicious requests to reach the backend server. The vulnerability has a CVSS v3.1 score of 5.4 (Medium).

Technical Details

The vulnerability affects OWASP Coraza WAF, a Golang ModSecurity-compatible web application firewall library. Specifically, versions prior to 3.3.3 are susceptible. The affected component is the URI parsing logic within the Transaction struct, which is responsible for processing incoming HTTP requests and extracting relevant information.

The core issue lies in how Coraza WAF handles URIs that start with //. Instead of correctly interpreting these URIs, the parsing logic incorrectly extracts the REQUEST_FILENAME, leading to a mismatch between the intended resource and the value used for rule evaluation.

For example, a request with the URI //bar/uploads/foo.php?a=b would result in REQUEST_FILENAME being set to /uploads/foo.php. This incorrect value can be exploited to bypass rules that are designed to protect /bar/uploads/foo.php.

The vulnerability is classified as CWE-706: Use of Incorrectly-Resolved Name or Reference.

Root Cause Analysis

The root cause of CVE-2025-29914 lies in the incorrect usage of the url.Parse function from the net/url package in Go. The url.Parse function is designed to parse a complete URL, including the scheme (e.g., http:// or https://). When provided with a URI that lacks a scheme and starts with //, url.Parse can produce unexpected results, particularly in how it interprets the path component.

The problematic code snippet, before the patch, was located in internal/corazawaf/transaction.go:

func (tx *Transaction) ProcessURI(uri string, method string, httpVersion string) {
	// ... other code ...
	path := ""
	parsedURL, err := url.Parse(uri)
	query := ""
	if err != nil {
		tx.variables.urlencodedError.Set(err.Error())
		return
	}
	// ... other code using parsedURL ...
}

The url.Parse(uri) function, when given a URI like //bar/uploads/foo.php?a=b, doesn't correctly identify the path. This leads to the REQUEST_FILENAME variable being populated with an incorrect value derived from the misparsed URL.

Patch Analysis

The vulnerability was addressed by replacing url.Parse with url.ParseRequestURI in the ProcessURI function. The url.ParseRequestURI function is specifically designed for parsing request URIs, which are typically relative paths without a scheme. This function handles URIs starting with // more predictably and correctly.

The following diff shows the change in internal/corazawaf/transaction.go:

--- a/internal/corazawaf/transaction.go
+++ b/internal/corazawaf/transaction.go
@@ -765,7 +765,7 @@ func (tx *Transaction) ProcessURI(uri string, method string, httpVersion string) {
 		uri = uri[:in]
 	}
 	path := ""
-	parsedURL, err := url.Parse(uri)
+	parsedURL, err := url.ParseRequestURI(uri)
 	query := ""
 	if err != nil {
 		tx.variables.urlencodedError.Set(err.Error())

The key change is the replacement of url.Parse with url.ParseRequestURI. url.ParseRequestURI is specifically designed to parse the request URI part of an HTTP request. It expects a relative URI and handles cases like those starting with // more appropriately than url.Parse, which is intended for full URLs.

Additionally, the patch includes a comprehensive set of unit tests in internal/corazawaf/transaction_test.go to verify the correct behavior of the ProcessURI function with various URI inputs, including those with multiple leading slashes and encoded characters.

--- /dev/null
+++ b/internal/corazawaf/transaction_test.go
@@ -1787,3 +1788,78 @@ func TestCloseFails(t *testing.T) {
 		t.Fatalf("unexpected error message: %s", err.Error())
 	}
 }
+
+func TestRequestFilename(t *testing.T) {
+	tests := []struct {
+		name     string
+		uri      string
+		expected string
+	}{
+		{
+			name:     "simple",
+			uri:      "/foo",
+			expected: "/foo",
+		},
+		{
+			name:     "with query",
+			uri:      "/foo?bar=baz",
+			expected: "/foo",
+		},
+		{
+			name:     "with query and fragment",
+			uri:      "/foo?bar=baz#qux",
+			expected: "/foo",
+		},
+		{
+			name:     "subdirectory",
+			uri:      "/foo/bar",
+			expected: "/foo/bar",
+		},
+		{
+			name:     "subdirectory with query",
+			uri:      "/foo/bar?baz=qux",
+			expected: "/foo/bar",
+		},
+		{
+			name:     "multiple leading slashes",
+			uri:      "//foo/bar",
+			expected: "//foo/bar",
+		},
+		{
+			name:     "multiple leading slashes - 2",
+			uri:      "///foo/bar",
+			expected: "///foo/bar",
+		},
+		{ // This is a bug. This test should be adapted when the issue is fixed.
+			name:     "invalid encoding",
+			uri:      "/foo%zz?a=b",
+			expected: "/foo%zz?a=b",
+		},
+		{
+			name:     "valid encoding",
+			uri:      "/foo%20bar",
+			expected: "/foo bar",
+		},
+		{
+			name:     "trailing slash",
+			uri:      "/foo/bar/",
+			expected: "/foo/bar/",
+		},
+		{
+			name:     "duplicated slashes",
+			uri:      "//foo//bar",
+			expected: "//foo//bar",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			waf := NewWAF()
+			tx := waf.NewTransaction()
+			tx.ProcessURI(test.uri, http.MethodGet, "HTTP/1.1")
+			if tx.variables.requestFilename.Get() != test.expected {
+				t.Fatalf("Expected REQUEST_FILENAME %q, got %q", test.expected, tx.variables.requestFilename.Get())
+			}
+		})
+	}
+}

These tests cover various scenarios, including URIs with and without query parameters, subdirectories, multiple leading slashes, and encoded characters. The test case marked "// This is a bug. This test should be adapted when the issue is fixed." highlights a remaining issue with invalid encoding that is not addressed by this patch.

Exploitation Techniques

An attacker can exploit this vulnerability by crafting requests with URIs that begin with // to bypass WAF rules.

Attack Scenario:

  1. A WAF rule is configured to block access to /bar/uploads/foo.php to prevent unauthorized file uploads.
  2. An attacker sends a request with the URI //bar/uploads/foo.php?a=b.
  3. Due to the parsing error, Coraza WAF sets REQUEST_FILENAME to /uploads/foo.php.
  4. The WAF rule targeting /bar/uploads/foo.php is not triggered because the REQUEST_FILENAME variable does not match.
  5. The request is forwarded to the backend server, potentially allowing the attacker to upload a malicious file.

Proof of Concept (Made-up):

Let's assume a simple WAF rule that blocks requests to /admin/sensitive.php.

SecRule REQUEST_FILENAME "@streq /admin/sensitive.php" "id:1234,phase:2,t:lowercase,deny,msg:'Access to sensitive file blocked'"

An attacker can bypass this rule by sending a request with the following URI:

GET //admin/sensitive.php HTTP/1.1
Host: example.com

Because of the vulnerability, REQUEST_FILENAME will be set to /sensitive.php instead of /admin/sensitive.php, thus bypassing the rule.

Real-World Impacts:

  • Remote Code Execution (RCE): If the bypassed rules were protecting against file upload vulnerabilities, an attacker could upload a malicious script and execute arbitrary code on the server.
  • Data Breach: If the bypassed rules were protecting access to sensitive data, an attacker could gain unauthorized access to confidential information.
  • Website Defacement: An attacker could bypass rules protecting against unauthorized modifications and deface the website.

Mitigation Strategies

To mitigate CVE-2025-29914, the following steps are recommended:

  1. Upgrade to Coraza WAF version 3.3.3 or later: This version contains the fix for the vulnerability.
  2. Review and update WAF rules: After upgrading, review existing WAF rules to ensure they are effective in light of the corrected URI parsing. Pay special attention to rules that rely on the REQUEST_FILENAME variable.
  3. Implement input validation: Implement robust input validation on the backend server to prevent malicious requests from being processed, even if they bypass the WAF.
  4. Regularly update Coraza WAF: Stay informed about security updates and apply them promptly to address any newly discovered vulnerabilities.

Configuration Changes:

No specific configuration changes are required beyond upgrading to the patched version. However, it is crucial to review and potentially adjust existing WAF rules to account for the corrected URI parsing behavior.

Security Best Practices:

  • Adopt a defense-in-depth approach, combining WAF protection with other security measures such as input validation, output encoding, and regular security audits.
  • Implement a web application firewall with up-to-date rule sets.
  • Monitor web server logs for suspicious activity.

Timeline of Discovery and Disclosure

  • 2025-03-12: Vulnerability reported to Coraza WAF maintainers.
  • 2025-03-20: Coraza WAF version 3.3.3 released with the fix.
  • 2025-03-20: CVE-2025-29914 assigned and published.
  • 2025-03-20: Security advisory GHSA-q9f5-625g-xm39 published on GitHub.

References

Read more