5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2026-24072-analysis.md MD
# CVE-2026-24072: Apache HTTP Server mod_rewrite Privilege Escalation Analysis

## Executive Summary

**CVE-2026-24072** is a **moderate-severity local privilege escalation** vulnerability in Apache HTTP Server 2.4.66 and earlier. It allows **local `.htaccess` authors** to read arbitrary files on the filesystem with the privileges of the `httpd` user by leveraging `ap_expr` expression evaluation inside `mod_rewrite` (and other modules).

The fix was released in **Apache HTTP Server 2.4.67**.

## Root Cause

Apache's expression evaluation engine (`ap_expr`) provides a rich set of functions and operators for use in configuration directives. Several of these functions allow filesystem introspection:

- `file(path)` — reads the contents of a file
- `filesize(path)` — returns the size of a file
- `-d path` — tests if path is a directory
- `-e path` — tests if path exists
- `-f path` — tests if path is a regular file
- `-s path` — tests if path is a non-empty file
- `-L path` / `-h path` — tests if path is a symbolic link
- `-x path` — tests if path is executable

In **2.4.66 and earlier**, modules such as `mod_rewrite` (via `RewriteCond expr=...`), `mod_setenvif` (via `SetEnvIfExpr`), and `mod_proxy_fcgi` (via `ProxyFCGIBackendType` condition expressions) would parse and evaluate `ap_expr` expressions **without** passing the `AP_EXPR_FLAG_RESTRICTED` flag when operating in `.htaccess` context. This meant that any local user who could write a `.htaccess` file could use these expression functions to probe or read the filesystem with the full privileges of the `httpd` process.

For example, a malicious `.htaccess` could contain:

```apache
RewriteCond expr "-f /etc/passwd"
RewriteRule .* - [L]
```

or

```apache
RewriteCond expr "%{expr:file('/etc/shadow')}"
RewriteRule .* - [L]
```

These expressions would evaluate with the full `httpd` user privileges, bypassing the normal `.htaccess` directory confinement.

## The Fix (2.4.67)

The fix is conceptually simple and consistent across all affected modules: **when parsing `ap_expr` expressions in `.htaccess` context, pass the `AP_EXPR_FLAG_RESTRICTED` flag.**

Apache already had the `AP_EXPR_FLAG_RESTRICTED` mechanism (flag value `4`) and the restricted function list in `server/util_expr_eval.c`. What was missing was that several modules simply didn't use it when parsing expressions from `.htaccess` files.

### Detection of `.htaccess` Context

All three patched modules use the same idiom to detect `.htaccess` parsing context:

```c
int in_htaccess = cmd->pool == cmd->temp_pool;
```

When Apache parses `.htaccess` files, the per-directory configuration pool (`cmd->pool`) is the same as the temporary pool (`cmd->temp_pool`). This is a well-known internal Apache convention for detecting `.htaccess` (per-directory) context vs. main server/virtual host context.

### Modules Fixed

Three modules were patched in 2.4.67:

#### 1. `mod_rewrite.c` (`modules/mappers/mod_rewrite.c`)

```diff
--- httpd-2.4.66/modules/mappers/mod_rewrite.c
+++ httpd-2.4.67/modules/mappers/mod_rewrite.c
@@ -3679,12 +3679,17 @@
         newcond->regexp  = regexp;
     }
     else if (newcond->ptype == CONDPAT_AP_EXPR) {
+        int in_htaccess = cmd->pool == cmd->temp_pool;
         unsigned int flags = newcond->flags & CONDFLAG_NOVARY ?
                              AP_EXPR_FLAG_DONT_VARY : 0;
+        /* Use restricted ap_expr() parser in htaccess context. */
+        if (in_htaccess) flags |= AP_EXPR_FLAG_RESTRICTED;
         newcond->expr = ap_expr_parse_cmd(cmd, a2, flags, &err, NULL);
         if (err)
             return apr_psprintf(cmd->pool, "RewriteCond: cannot compile "
-                                "expression \"%s\": %s", a2, err);
+                                "expression%s \"%s\" %s",
+                                in_htaccess ? " in htaccess context" : "",
+                                a2, err);
     }
```

**Line reference:** `modules/mappers/mod_rewrite.c:3682-3691`

This affects `RewriteCond` directives that use the `expr=` pattern type (e.g., `RewriteCond expr "-f /etc/passwd"`).

#### 2. `mod_setenvif.c` (`modules/metadata/mod_setenvif.c`)

```diff
--- httpd-2.4.66/modules/metadata/mod_setenvif.c
+++ httpd-2.4.67/modules/metadata/mod_setenvif.c
@@ -422,6 +422,12 @@
     sei_cfg_rec *sconf;
     sei_entry *new;
     const char *err;
+    unsigned int flags = 0;
+
+    /* Use restricted ap_expr() parser in htaccess context. */
+    if (cmd->pool == cmd->temp_pool) {
+        flags |= AP_EXPR_FLAG_RESTRICTED;
+    }
 
     /*
      * Determine from our context into which record to put the entry.
@@ -445,7 +451,7 @@
     new->regex = NULL;
     new->pattern = NULL;
     new->preg = NULL;
-    new->expr = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
+    new->expr = ap_expr_parse_cmd(cmd, expr, flags, &err, NULL);
```

**Line reference:** `modules/metadata/mod_setenvif.c:425-430`

This affects `SetEnvIfExpr` directives in `.htaccess`.

#### 3. `mod_proxy_fcgi.c` (`modules/proxy/mod_proxy_fcgi.c`)

```diff
--- httpd-2.4.66/modules/proxy/mod_proxy_fcgi.c
+++ httpd-2.4.67/modules/proxy/mod_proxy_fcgi.c
@@ -1338,9 +1338,15 @@
     const char *err;
     sei_entry *new;
     const char *envvar = arg2;
+    unsigned int flags = 0;
+
+    /* Use restricted ap_expr() parser in htaccess context. */
+    if (cmd->pool == cmd->temp_pool) {
+        flags |= AP_EXPR_FLAG_RESTRICTED;
+    }
 
     new = apr_array_push(dconf->env_fixups);
-    new->cond = ap_expr_parse_cmd(cmd, arg1, 0, &err, NULL);
+    new->cond = ap_expr_parse_cmd(cmd, arg1, flags, &err, NULL);
```

**Line reference:** `modules/proxy/mod_proxy_fcgi.c:1341-1347`

This affects `ProxyFCGISetEnvIf` and related expression-based configuration directives.

## How `AP_EXPR_FLAG_RESTRICTED` Works

The restriction mechanism already existed in `server/util_expr_eval.c` (unchanged between 2.4.66 and 2.4.67). When `AP_EXPR_FLAG_RESTRICTED` is passed to `ap_expr_parse_cmd()`, the parser marks the expression as restricted. During expression evaluation, if a function or operator marked as `restricted` is encountered, evaluation fails with an error.

From `server/util_expr_eval.c:1774-1787`:

```c
if (match) {
    if ((parms->flags & AP_EXPR_FLAG_RESTRICTED)
        && prov->restricted) {
        *parms->err =
            apr_psprintf(parms->ptemp,
                         "%s%s not available in restricted context",
                         (parms->type == AP_EXPR_FUNC_STRING) ? "" : "-",
                         prov->name);
        return !OK;
    }
```

### Restricted Functions and Operators

The following `ap_expr` primitives are restricted (cannot be used in `.htaccess` context):

**String functions (`string_func_providers`):**
- `file(path)` — reads file contents
- `filesize(path)` — returns file size

**Unary operators (`unary_op_providers`):**
- `-d path` — is directory
- `-e path` — exists
- `-f path` — is regular file
- `-s path` — is non-empty file
- `-L path` / `-h path` — is symbolic link
- `-x path` — is executable

Non-restricted functions (allowed in `.htaccess`):
- `osenv()`, `env()`, `resp()`, `req()`, `http()`, `note()`, `reqenv()`, `req_novary()`
- `tolower()`, `toupper()`, `escape()`, `unescape()`
- `base64()`, `unbase64()`, `sha1()`, `md5()`
- `-n`, `-z`, `-R`, `-T`
- `-F`, `-U`, `-A` (subrequest-based, not direct filesystem)

## Impact and Attack Scenario

**Threat model:** Local attacker with ability to write `.htaccess` files in a web-accessible directory.

**Attack path:**
1. Attacker gains write access to a directory served by Apache (e.g., via compromised CMS, file upload vulnerability, or shared hosting).
2. Attacker creates `.htaccess` with `RewriteCond expr "%{expr:file('/etc/passwd')}"` or similar.
3. Apache parses the `.htaccess` and evaluates the expression with `httpd` user privileges.
4. Expression returns the contents of `/etc/passwd` (or any file readable by `httpd`).
5. Attacker can also use `-f`, `-d`, `-e`, etc. to probe the filesystem structure, discovering files and paths that exist on the server.

**Result:** Information disclosure and filesystem probing beyond the intended `.htaccess` directory scope.

## Affected Versions

- **Affected:** Apache HTTP Server 2.4.66 and earlier
- **Fixed:** Apache HTTP Server 2.4.67

## Mitigation and Remediation

1. **Upgrade to Apache HTTP Server 2.4.67 or later.** This is the primary and recommended fix.

2. **Restrict `.htaccess` usage.** If upgrading is not immediately possible, disable `.htaccess` processing entirely by setting `AllowOverride None` in your main configuration. This prevents any `.htaccess` from being parsed.

3. **Audit `.htaccess` writers.** Ensure only trusted users can write `.htaccess` files in web directories. In shared hosting environments, this may require OS-level permissions and web application security hardening.

4. **Monitor for exploitation attempts.** Look for `.htaccess` files containing `expr`, `file(`, `filesize(`, or filesystem test operators (`-f`, `-d`, `-e`, `-s`, `-L`, `-h`, `-x`).

## References

- [CVE-2026-24072 - Apache HTTP Server: mod_rewrite elevation of privileges via ap_expr](https://cvefeed.io/vuln/detail/CVE-2026-24072)
- [Apache HTTP Server 2.4 Security Vulnerabilities](https://httpd.apache.org/security/vulnerabilities_24.html)
- [cPanel Security Advisory for CVE-2026-24072](https://support.cpanel.net/hc/en-us/articles/40232024538775-Security-CVE-2026-24072-Apache-HTTP-Server-mod-rewrite-elevation-of-privileges-via-ap-expr)

## Diff Summary

```
modules/mappers/mod_rewrite.c       | +11 lines (adds AP_EXPR_FLAG_RESTRICTED in htaccess)
modules/metadata/mod_setenvif.c     | +7 lines (adds AP_EXPR_FLAG_RESTRICTED in htaccess)
modules/proxy/mod_proxy_fcgi.c     | +9 lines (adds AP_EXPR_FLAG_RESTRICTED in htaccess)
server/util_expr_eval.c            | unchanged (mechanism already existed)
include/ap_expr.h                  | unchanged (flag already defined)
```

The fix is **minimal and surgical**: it applies an existing security mechanism (`AP_EXPR_FLAG_RESTRICTED`) to three modules that were inadvertently bypassing it when parsing expressions from `.htaccess` files.