Assaalam o Alaikum :)
My name is Muhammad Waseem. Today, we are going to discuss one of the most critical vulnerabilities in modern enterprise web architecture: Adobe Experience Manager (AEM) Dispatcher Bypasses.
Let's start.
01: The Spark of Research
I was normally scrolling through Twitter when I noticed a post from Shubham Shah (infosec_au). In this post, he demonstrated how the Adobe AEM Dispatcher could be bypassed.

The post mentioned a detailed research center link that provides deep technical insights into AEM bugs:

02: Analyzing the Foundation
"Adobe Experience Manager is one of the most popular CMSes around. Given its widespread use throughout the enterprise, you likely interact with AEM-based sites almost every day."

After reading this, I began to see the key things mentioned in the blog and how they apply to modern bug hunting.
03: The Core of AEM Endpoints
The core of AEM exposes plenty of endpoints that leak information about how the site is structured. Some of the most famous, such as /bin/querybuilder.json, allow queries of all the "nodes" (page content) of the application.
Obviously, as a website running AEM, you don’t want all this functionality intended for authors to be exposed to the general public. And indeed, if you visit /bin/querybuilder.json on a properly secured AEM site, you should be blocked.

04: Standard Dispatcher Configuration
Here’s a sample of the out-of-the-box (OOTB) dispatcher configuration for cloud AEM instances. It starts with everything blocked as a safeguard and opens only what customers need.


05: Dispatcher Bypass #1
Adobe offers two ways to implement the dispatcher: via an Apache configuration and module (disp_apache2.so) or an IIS module. Since the Apache-based setup is vastly more popular, we will focus on attacking this.
Before disassembling the binary module itself, we checked the sample Apache config used by default (and used by all cloud instances). Most paths have checks run through the dispatcher module, but there are a couple of paths Apache handles specially:
These paths forward the request contents directly to the AEM host via ProxyPassMatch, without running through the dispatcher ruleset at all. Our very first instinct was to use an old traversal trick; the AEM host itself most commonly runs on Jetty, which allows ..;/ as a stand-in for a path traversal.
The answer as it turns out is no – this style of attack is so popular that Jetty has a specific mitigation; it will not allow ..;/ sequences in the path.
06: Jetty Security Internals
There are a couple of instances with a very old version of Jetty where this may work, but it doesn’t really satisfy our goal of a widespread dispatcher bypass.
I discovered that Jetty specifically blocks ..;/ sequences — this behavior is built into Jetty’s request handling logic.

07: The Power of 'nocanon'
Looking at the next LocationMatch is more interesting. Unlike the other rule, this one uses nocanon.
mod_proxy: nocanon
"Normally, mod_proxy will canonicalise ProxyPassed URLs. But this may be incompatible with some backends, particularly those that make use of PATH_INFO. The optional nocanon keyword suppresses this and passes the URL path 'raw' to the backend."
To give a concrete example, in usual operation, Apache will normalize the URL before checking the match. Thus, the URLs /foo and /bar/..%2ffoo are treated exactly the same. However, if nocanon is applied, the proxy passes the raw, un-normalized URL.
And it worked! Apache matches the raw URL due to nocanon, since it matches the regex /graphql/execute.json/.*. Then on the backend, Jetty normalizes the URL to /bin/querybuilder.json before passing to AEM.
08: Apache Sling – URL Decomposition
To understand how AEM handles the request, we had to look into **Apache Sling**, the web framework used by AEM. Sling centers a lot of its design around “resources”.
Any time a request comes into Sling, the path is broken up and used to find the corresponding resource. Once Sling has found the resource and checked the user’s permissions, it uses additional information from the path to determine how to handle the actual request.

09: URL Components
Sling parses the incoming URL into several components. For example, /bin/querybuilder.tiny.json;x='hello'/extra decomposes into:
- Resource Path:
/bin/querybuilder - Selectors:
tiny - Extension:
json - Path Parameter:
;x='hello' - Suffix:
/extra
10: Dispatcher Bypass #2
Analyzing the Apache module in Ghidra revealed the decompose_url function.